Add support for JPA 3.2 PersistenceConfiguration

See gh-35662
This commit is contained in:
Juergen Hoeller
2025-10-18 15:05:38 +02:00
parent cfb268f6de
commit beb224e3f9
10 changed files with 422 additions and 154 deletions
@@ -39,27 +39,27 @@ public abstract class AbstractEntityManagerFactoryBeanTests {
protected static EntityManagerFactory mockEmf;
@BeforeEach
void setUp() {
void setup() {
mockEmf = mock();
}
@AfterEach
void tearDown() {
void cleanup() {
assertThat(TransactionSynchronizationManager.getResourceMap()).isEmpty();
assertThat(TransactionSynchronizationManager.isSynchronizationActive()).isFalse();
assertThat(TransactionSynchronizationManager.isCurrentTransactionReadOnly()).isFalse();
assertThat(TransactionSynchronizationManager.isActualTransactionActive()).isFalse();
}
protected void checkInvariants(AbstractEntityManagerFactoryBean demf) {
assertThat(EntityManagerFactory.class.isAssignableFrom(demf.getObjectType())).isTrue();
Object gotObject = demf.getObject();
boolean condition = gotObject instanceof EntityManagerFactoryInfo;
assertThat(condition).as("Object created by factory implements EntityManagerFactoryInfo").isTrue();
EntityManagerFactoryInfo emfi = (EntityManagerFactoryInfo) demf.getObject();
assertThat(demf.getObject()).as("Successive invocations of getObject() return same object").isSameAs(emfi);
assertThat(demf.getObject()).isSameAs(emfi);
protected void checkInvariants(AbstractEntityManagerFactoryBean emfb) {
assertThat(EntityManagerFactory.class.isAssignableFrom(emfb.getObjectType())).isTrue();
EntityManagerFactory emf = emfb.getObject();
assertThat(emf instanceof EntityManagerFactoryInfo).as("Object created by factory implements EntityManagerFactoryInfo").isTrue();
EntityManagerFactoryInfo emfi = (EntityManagerFactoryInfo) emf;
assertThat(emfb.getObject()).as("Successive invocations of getObject() return same object").isSameAs(emfi);
assertThat(emfb.getObject()).isSameAs(emfi);
assertThat(mockEmf).isSameAs(emfi.getNativeEntityManagerFactory());
}
@@ -36,6 +36,8 @@ import org.springframework.core.testfixture.io.SerializationTestUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;
import org.springframework.orm.jpa.domain.DriversLicense;
import org.springframework.orm.jpa.domain.Person;
import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
@@ -53,23 +55,16 @@ import static org.mockito.Mockito.verify;
* @author Juergen Hoeller
* @author Phillip Webb
*/
@SuppressWarnings({"rawtypes", "removal"})
@SuppressWarnings("rawtypes")
class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerFactoryBeanTests {
// Static fields set by inner class DummyPersistenceProvider
private static Map actualProps;
private static PersistenceUnitInfo actualPui;
@Test
void testValidPersistenceUnit() throws Exception {
void validPersistenceUnit() {
parseValidPersistenceUnit();
}
@Test
void testExceptionTranslationWithNoDialect() throws Exception {
void exceptionTranslationWithNoDialect() {
LocalContainerEntityManagerFactoryBean cefb = parseValidPersistenceUnit();
cefb.getObject();
assertThat(cefb.getJpaDialect()).as("No dialect set").isNull();
@@ -83,7 +78,7 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF
}
@Test
void testEntityManagerFactoryIsProxied() throws Exception {
void entityManagerFactoryIsProxied() throws Exception {
LocalContainerEntityManagerFactoryBean cefb = parseValidPersistenceUnit();
EntityManagerFactory emf = cefb.getObject();
assertThat(cefb.getObject()).as("EntityManagerFactory reference must be cached after init").isSameAs(emf);
@@ -100,7 +95,7 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF
}
@Test
void testApplicationManagedEntityManagerWithoutTransaction() throws Exception {
void applicationManagedEntityManagerWithoutTransaction() {
Object testEntity = new Object();
EntityManager mockEm = mock();
@@ -120,7 +115,7 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF
}
@Test
void testApplicationManagedEntityManagerWithTransaction() throws Exception {
void applicationManagedEntityManagerWithTransaction() {
Object testEntity = new Object();
EntityTransaction mockTx = mock();
@@ -161,7 +156,7 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF
}
@Test
void testApplicationManagedEntityManagerWithTransactionAndCommitException() throws Exception {
void applicationManagedEntityManagerWithTransactionAndCommitException() {
Object testEntity = new Object();
EntityTransaction mockTx = mock();
@@ -203,7 +198,7 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF
}
@Test
void testApplicationManagedEntityManagerWithJtaTransaction() throws Exception {
void applicationManagedEntityManagerWithJtaTransaction() {
Object testEntity = new Object();
// This one's for the tx (shared)
@@ -240,68 +235,80 @@ class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityManagerF
verify(mockEmf).close();
}
public LocalContainerEntityManagerFactoryBean parseValidPersistenceUnit(
PersistenceUnitPostProcessor... postProcessors) {
@Test
void invalidPersistenceUnitName() {
assertThatIllegalArgumentException().isThrownBy(() ->
createEntityManagerFactoryBean("org/springframework/orm/jpa/domain/persistence.xml", null, "call me Bob"));
}
@Test
void rejectsMissingPersistenceUnitInfo() {
LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
emfb.setPersistenceProviderClass(DummyContainerPersistenceProvider.class);
String entityManagerName = "call me Bob";
emfb.setPersistenceUnitName(entityManagerName);
assertThatIllegalArgumentException().isThrownBy(emfb::afterPropertiesSet);
}
@Test
void acceptsPersistenceConfiguration() {
DummyContainerPersistenceProvider persistenceProvider = new DummyContainerPersistenceProvider();
LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
emfb.setPersistenceProvider(persistenceProvider);
String entityManagerName = "call me Bob";
emfb.setPersistenceConfiguration(new PersistenceConfiguration(entityManagerName).
managedClass(DriversLicense.class).managedClass(Person.class));
emfb.afterPropertiesSet();
assertThat(persistenceProvider.actualPui.getPersistenceUnitName()).isEqualTo(entityManagerName);
assertThat(persistenceProvider.actualPui.getManagedClassNames()).containsExactly(
DriversLicense.class.getName(), Person.class.getName());
}
private LocalContainerEntityManagerFactoryBean parseValidPersistenceUnit(PersistenceUnitPostProcessor... postProcessors) {
return createEntityManagerFactoryBean(
"org/springframework/orm/jpa/domain/persistence.xml", null,
"Person", postProcessors);
}
@Test
void testInvalidPersistenceUnitName() {
assertThatIllegalArgumentException().isThrownBy(() ->
createEntityManagerFactoryBean("org/springframework/orm/jpa/domain/persistence.xml", null, "call me Bob"));
}
@SuppressWarnings("unchecked")
protected LocalContainerEntityManagerFactoryBean createEntityManagerFactoryBean(
private LocalContainerEntityManagerFactoryBean createEntityManagerFactoryBean(
String persistenceXml, Properties props, String entityManagerName,
PersistenceUnitPostProcessor... postProcessors) {
// This will be set by DummyPersistenceProvider
actualPui = null;
actualProps = null;
DummyContainerPersistenceProvider persistenceProvider = new DummyContainerPersistenceProvider();
LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
LocalContainerEntityManagerFactoryBean containerEmfb = new LocalContainerEntityManagerFactoryBean();
containerEmfb.setPersistenceUnitName(entityManagerName);
containerEmfb.setPersistenceProviderClass(DummyContainerPersistenceProvider.class);
emfb.setPersistenceUnitName(entityManagerName);
emfb.setPersistenceProvider(persistenceProvider);
if (props != null) {
containerEmfb.setJpaProperties(props);
emfb.setJpaProperties(props);
}
containerEmfb.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
containerEmfb.setPersistenceXmlLocation(persistenceXml);
containerEmfb.setPersistenceUnitPostProcessors(postProcessors);
containerEmfb.afterPropertiesSet();
emfb.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
emfb.setPersistenceXmlLocation(persistenceXml);
emfb.setPersistenceUnitPostProcessors(postProcessors);
emfb.afterPropertiesSet();
assertThat(actualPui.getPersistenceUnitName()).isEqualTo(entityManagerName);
assertThat(persistenceProvider.actualPui.getPersistenceUnitName()).isEqualTo(entityManagerName);
if (props != null) {
assertThat(actualProps).isEqualTo(props);
assertThat(persistenceProvider.actualProps).isEqualTo(props);
}
//checkInvariants(containerEmfb);
checkInvariants(emfb);
return containerEmfb;
//containerEmfb.destroy();
//emfMc.verify();
}
@Test
void testRejectsMissingPersistenceUnitInfo() {
LocalContainerEntityManagerFactoryBean containerEmfb = new LocalContainerEntityManagerFactoryBean();
String entityManagerName = "call me Bob";
containerEmfb.setPersistenceUnitName(entityManagerName);
containerEmfb.setPersistenceProviderClass(DummyContainerPersistenceProvider.class);
assertThatIllegalArgumentException().isThrownBy(
containerEmfb::afterPropertiesSet);
return emfb;
}
private static class DummyContainerPersistenceProvider implements PersistenceProvider {
PersistenceUnitInfo actualPui;
Map actualProps;
@Override
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo pui, Map map) {
actualPui = pui;
@@ -27,64 +27,138 @@ import jakarta.persistence.spi.ProviderUtil;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verify;
/**
* @author Rod Johnson
* @author Juergen Hoeller
* @author Phillip Webb
*/
@SuppressWarnings("rawtypes")
class LocalEntityManagerFactoryBeanTests extends AbstractEntityManagerFactoryBeanTests {
// Static fields set by inner class DummyPersistenceProvider
private static String actualName;
private static Map actualProps;
@AfterEach
void verifyClosed() {
verify(mockEmf).close();
}
@Test
void testValidUsageWithDefaultProperties() throws Exception {
testValidUsage(null);
void withDefault() {
DummyPersistenceProvider persistenceProvider = new DummyPersistenceProvider();
LocalEntityManagerFactoryBean emfb = new LocalEntityManagerFactoryBean();
emfb.setPersistenceProvider(persistenceProvider);
emfb.afterPropertiesSet();
assertThat(persistenceProvider.actualName).isSameAs(
DefaultPersistenceUnitManager.ORIGINAL_DEFAULT_PERSISTENCE_UNIT_NAME);
assertThat(persistenceProvider.actualProps).isEqualTo(emfb.getJpaPropertyMap());
checkInvariants(emfb);
emfb.destroy();
}
@Test
void testValidUsageWithExplicitProperties() throws Exception {
testValidUsage(new Properties());
void withName() {
DummyPersistenceProvider persistenceProvider = new DummyPersistenceProvider();
LocalEntityManagerFactoryBean emfb = new LocalEntityManagerFactoryBean();
String name = "call me Bob";
emfb.setPersistenceUnitName(name);
emfb.setPersistenceProvider(persistenceProvider);
emfb.afterPropertiesSet();
assertThat(persistenceProvider.actualName).isSameAs(name);
assertThat(persistenceProvider.actualProps).isEqualTo(emfb.getJpaPropertyMap());
checkInvariants(emfb);
emfb.destroy();
}
@SuppressWarnings("unchecked")
protected void testValidUsage(Properties props) {
// This will be set by DummyPersistenceProvider
actualName = null;
actualProps = null;
@Test
void withNameAndExplicitProperties() {
DummyPersistenceProvider persistenceProvider = new DummyPersistenceProvider();
LocalEntityManagerFactoryBean emfb = new LocalEntityManagerFactoryBean();
String name = "call me Bob";
Properties props = new Properties();
props.setProperty("myProp", "myVal");
LocalEntityManagerFactoryBean lemfb = new LocalEntityManagerFactoryBean();
String entityManagerName = "call me Bob";
emfb.setPersistenceUnitName(name);
emfb.setPersistenceProvider(persistenceProvider);
emfb.setJpaProperties(props);
emfb.afterPropertiesSet();
lemfb.setPersistenceUnitName(entityManagerName);
lemfb.setPersistenceProviderClass(DummyPersistenceProvider.class);
if (props != null) {
lemfb.setJpaProperties(props);
}
lemfb.afterPropertiesSet();
assertThat(persistenceProvider.actualName).isSameAs(name);
assertThat(persistenceProvider.actualProps).isEqualTo(props);
checkInvariants(emfb);
assertThat(actualName).isSameAs(entityManagerName);
if (props != null) {
assertThat(actualProps).isEqualTo(props);
}
checkInvariants(lemfb);
emfb.destroy();
}
lemfb.destroy();
@Test
void withDefaultPersistenceConfiguration() {
DummyPersistenceProvider persistenceProvider = new DummyPersistenceProvider();
LocalEntityManagerFactoryBean emfb = new LocalEntityManagerFactoryBean();
emfb.getPersistenceConfiguration().property("myProp", "myVal");
emfb.setPersistenceProvider(persistenceProvider);
emfb.afterPropertiesSet();
assertThat(persistenceProvider.actualName).isSameAs(
DefaultPersistenceUnitManager.ORIGINAL_DEFAULT_PERSISTENCE_UNIT_NAME);
assertThat(persistenceProvider.actualProps).containsEntry("myProp", "myVal");
checkInvariants(emfb);
emfb.destroy();
}
@Test
void withNameAndDefaultPersistenceConfiguration() {
DummyPersistenceProvider persistenceProvider = new DummyPersistenceProvider();
LocalEntityManagerFactoryBean emfb = new LocalEntityManagerFactoryBean();
String name = "call me Bob";
emfb.setPersistenceUnitName(name);
emfb.getPersistenceConfiguration().property("myProp", "myVal");
emfb.setPersistenceProvider(persistenceProvider);
emfb.afterPropertiesSet();
assertThat(persistenceProvider.actualName).isSameAs(name);
assertThat(persistenceProvider.actualProps).containsEntry("myProp", "myVal");
checkInvariants(emfb);
emfb.destroy();
}
@Test
void withExplicitPersistenceConfiguration() {
DummyPersistenceProvider persistenceProvider = new DummyPersistenceProvider();
LocalEntityManagerFactoryBean emfb = new LocalEntityManagerFactoryBean();
String name = "call me Bob";
PersistenceConfiguration config = new PersistenceConfiguration(name);
config.property("myProp", "myVal");
emfb.setPersistenceConfiguration(config);
emfb.setPersistenceProvider(persistenceProvider);
emfb.afterPropertiesSet();
assertThat(persistenceProvider.actualName).isSameAs(name);
assertThat(persistenceProvider.actualProps).containsEntry("myProp", "myVal");
checkInvariants(emfb);
emfb.destroy();
}
protected static class DummyPersistenceProvider implements PersistenceProvider {
private static class DummyPersistenceProvider implements PersistenceProvider {
String actualName;
Map actualProps;
@Override
public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo pui, Map map) {
@@ -99,8 +173,10 @@ class LocalEntityManagerFactoryBeanTests extends AbstractEntityManagerFactoryBea
}
@Override
public EntityManagerFactory createEntityManagerFactory(PersistenceConfiguration persistenceConfiguration) {
throw new UnsupportedOperationException();
public EntityManagerFactory createEntityManagerFactory(PersistenceConfiguration config) {
actualName = config.name();
actualProps = config.properties();
return mockEmf;
}
@Override
@@ -108,13 +184,11 @@ class LocalEntityManagerFactoryBeanTests extends AbstractEntityManagerFactoryBea
throw new UnsupportedOperationException();
}
// JPA 2.1 method
@Override
public void generateSchema(PersistenceUnitInfo persistenceUnitInfo, Map map) {
throw new UnsupportedOperationException();
}
// JPA 2.1 method
@Override
public boolean generateSchema(String persistenceUnitName, Map map) {
throw new UnsupportedOperationException();
@@ -46,7 +46,6 @@ import static org.assertj.core.api.Assertions.assertThatRuntimeException;
* @author Juergen Hoeller
* @author Nicholas Williams
*/
@SuppressWarnings("removal")
class PersistenceXmlParsingTests {
@Test