Introduce EntityManager initialization callbacks
JpaVendorAdapter.postProcessEntityManager and EntityManagerFactoryInfo.createNativeEntityManager SPI, plus convenient setEntityManagerInitializer configuration options. Closes gh-25125
This commit is contained in:
parent
a853a58c62
commit
9bf7ff23c0
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -18,6 +18,7 @@ package org.springframework.orm.hibernate5;
|
|||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
|
@ -125,6 +126,9 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
|
|||
|
||||
private boolean hibernateManagedSession = false;
|
||||
|
||||
@Nullable
|
||||
private Consumer<Session> sessionInitializer;
|
||||
|
||||
@Nullable
|
||||
private Object entityInterceptor;
|
||||
|
||||
|
@ -300,6 +304,17 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
|
|||
this.hibernateManagedSession = hibernateManagedSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a callback for customizing every Hibernate {@code Session} resource
|
||||
* created for a new transaction managed by this {@code HibernateTransactionManager}.
|
||||
* <p>This enables convenient customizations for application purposes, e.g.
|
||||
* setting Hibernate filters.
|
||||
* @since 5.3
|
||||
*/
|
||||
public void setSessionInitializer(Consumer<Session> sessionInitializer) {
|
||||
this.sessionInitializer = sessionInitializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bean name of a Hibernate entity interceptor that allows to inspect
|
||||
* and change property values before writing to and reading from the database.
|
||||
|
@ -462,6 +477,9 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
|
|||
Session newSession = (entityInterceptor != null ?
|
||||
obtainSessionFactory().withOptions().interceptor(entityInterceptor).openSession() :
|
||||
obtainSessionFactory().openSession());
|
||||
if (this.sessionInitializer != null) {
|
||||
this.sessionInitializer.accept(session);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -32,6 +32,7 @@ import java.util.Properties;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
|
@ -115,6 +116,9 @@ public abstract class AbstractEntityManagerFactoryBean implements
|
|||
@Nullable
|
||||
private JpaVendorAdapter jpaVendorAdapter;
|
||||
|
||||
@Nullable
|
||||
private Consumer<EntityManager> entityManagerInitializer;
|
||||
|
||||
@Nullable
|
||||
private AsyncTaskExecutor bootstrapExecutor;
|
||||
|
||||
|
@ -193,8 +197,8 @@ public abstract class AbstractEntityManagerFactoryBean implements
|
|||
* {@code Persistence.createEntityManagerFactory} (if any).
|
||||
* <p>Can be populated with a String "value" (parsed via PropertiesEditor) or a
|
||||
* "props" element in XML bean definitions.
|
||||
* @see javax.persistence.Persistence#createEntityManagerFactory(String, java.util.Map)
|
||||
* @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(javax.persistence.spi.PersistenceUnitInfo, java.util.Map)
|
||||
* @see javax.persistence.Persistence#createEntityManagerFactory(String, Map)
|
||||
* @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(PersistenceUnitInfo, Map)
|
||||
*/
|
||||
public void setJpaProperties(Properties jpaProperties) {
|
||||
CollectionUtils.mergePropertiesIntoMap(jpaProperties, this.jpaPropertyMap);
|
||||
|
@ -204,8 +208,8 @@ public abstract class AbstractEntityManagerFactoryBean implements
|
|||
* Specify JPA properties as a Map, to be passed into
|
||||
* {@code Persistence.createEntityManagerFactory} (if any).
|
||||
* <p>Can be populated with a "map" or "props" element in XML bean definitions.
|
||||
* @see javax.persistence.Persistence#createEntityManagerFactory(String, java.util.Map)
|
||||
* @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(javax.persistence.spi.PersistenceUnitInfo, java.util.Map)
|
||||
* @see javax.persistence.Persistence#createEntityManagerFactory(String, Map)
|
||||
* @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(PersistenceUnitInfo, Map)
|
||||
*/
|
||||
public void setJpaPropertyMap(@Nullable Map<String, ?> jpaProperties) {
|
||||
if (jpaProperties != null) {
|
||||
|
@ -290,6 +294,20 @@ public abstract class AbstractEntityManagerFactoryBean implements
|
|||
return this.jpaVendorAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a callback for customizing every {@code EntityManager} created
|
||||
* by the exposed {@code EntityManagerFactory}.
|
||||
* <p>This is an alternative to a {@code JpaVendorAdapter}-level
|
||||
* {@code postProcessEntityManager} implementation, enabling convenient
|
||||
* customizations for application purposes, e.g. setting Hibernate filters.
|
||||
* @since 5.3
|
||||
* @see JpaVendorAdapter#postProcessEntityManager
|
||||
* @see JpaTransactionManager#setEntityManagerInitializer
|
||||
*/
|
||||
public void setEntityManagerInitializer(Consumer<EntityManager> entityManagerInitializer) {
|
||||
this.entityManagerInitializer = entityManagerInitializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify an asynchronous executor for background bootstrapping,
|
||||
* e.g. a {@link org.springframework.core.task.SimpleAsyncTaskExecutor}.
|
||||
|
@ -472,6 +490,7 @@ public abstract class AbstractEntityManagerFactoryBean implements
|
|||
EntityManager rawEntityManager = (args.length > 1 ?
|
||||
getNativeEntityManagerFactory().createEntityManager((Map<?, ?>) args[1]) :
|
||||
getNativeEntityManagerFactory().createEntityManager());
|
||||
postProcessEntityManager(rawEntityManager);
|
||||
return ExtendedEntityManagerCreator.createApplicationManagedEntityManager(rawEntityManager, this, true);
|
||||
}
|
||||
|
||||
|
@ -498,6 +517,7 @@ public abstract class AbstractEntityManagerFactoryBean implements
|
|||
if (retVal instanceof EntityManager) {
|
||||
// Any other createEntityManager variant - expecting non-synchronized semantics
|
||||
EntityManager rawEntityManager = (EntityManager) retVal;
|
||||
postProcessEntityManager(rawEntityManager);
|
||||
retVal = ExtendedEntityManagerCreator.createApplicationManagedEntityManager(rawEntityManager, this, false);
|
||||
}
|
||||
return retVal;
|
||||
|
@ -555,6 +575,36 @@ public abstract class AbstractEntityManagerFactoryBean implements
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityManager createNativeEntityManager(@Nullable Map<?, ?> properties) {
|
||||
EntityManager rawEntityManager = (!CollectionUtils.isEmpty(properties) ?
|
||||
getNativeEntityManagerFactory().createEntityManager(properties) :
|
||||
getNativeEntityManagerFactory().createEntityManager());
|
||||
postProcessEntityManager(rawEntityManager);
|
||||
return rawEntityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional callback for post-processing the native EntityManager
|
||||
* before active use.
|
||||
* <p>The default implementation delegates to
|
||||
* {@link JpaVendorAdapter#postProcessEntityManager}, if available.
|
||||
* @param rawEntityManager the EntityManager to post-process
|
||||
* @since 5.3
|
||||
* @see #createNativeEntityManager
|
||||
* @see JpaVendorAdapter#postProcessEntityManager
|
||||
*/
|
||||
protected void postProcessEntityManager(EntityManager rawEntityManager) {
|
||||
JpaVendorAdapter jpaVendorAdapter = getJpaVendorAdapter();
|
||||
if (jpaVendorAdapter != null) {
|
||||
jpaVendorAdapter.postProcessEntityManager(rawEntityManager);
|
||||
}
|
||||
Consumer<EntityManager> customizer = this.entityManagerInitializer;
|
||||
if (customizer != null) {
|
||||
customizer.accept(rawEntityManager);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PersistenceUnitInfo getPersistenceUnitInfo() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.orm.jpa;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.spi.PersistenceProvider;
|
||||
|
@ -36,12 +38,6 @@ import org.springframework.lang.Nullable;
|
|||
*/
|
||||
public interface EntityManagerFactoryInfo {
|
||||
|
||||
/**
|
||||
* Return the raw underlying EntityManagerFactory.
|
||||
* @return the unadorned EntityManagerFactory (never {@code null})
|
||||
*/
|
||||
EntityManagerFactory getNativeEntityManagerFactory();
|
||||
|
||||
/**
|
||||
* Return the underlying PersistenceProvider that the underlying
|
||||
* EntityManagerFactory was created with.
|
||||
|
@ -105,4 +101,23 @@ public interface EntityManagerFactoryInfo {
|
|||
*/
|
||||
ClassLoader getBeanClassLoader();
|
||||
|
||||
/**
|
||||
* Return the raw underlying EntityManagerFactory.
|
||||
* @return the unadorned EntityManagerFactory (never {@code null})
|
||||
*/
|
||||
EntityManagerFactory getNativeEntityManagerFactory();
|
||||
|
||||
/**
|
||||
* Create a native JPA EntityManager to be used as the framework-managed
|
||||
* resource behind an application-level EntityManager handle.
|
||||
* <p>This exposes a native {@code EntityManager} from the underlying
|
||||
* {@link #getNativeEntityManagerFactory() native EntityManagerFactory},
|
||||
* taking {@link JpaVendorAdapter#postProcessEntityManager(EntityManager)}
|
||||
* into account.
|
||||
* @since 5.3
|
||||
* @see #getNativeEntityManagerFactory()
|
||||
* @see EntityManagerFactory#createEntityManager()
|
||||
*/
|
||||
EntityManager createNativeEntityManager(@Nullable Map<?, ?> properties);
|
||||
|
||||
}
|
||||
|
|
|
@ -172,9 +172,7 @@ public abstract class ExtendedEntityManagerCreator {
|
|||
Assert.notNull(emf, "EntityManagerFactory must not be null");
|
||||
if (emf instanceof EntityManagerFactoryInfo) {
|
||||
EntityManagerFactoryInfo emfInfo = (EntityManagerFactoryInfo) emf;
|
||||
EntityManagerFactory nativeEmf = emfInfo.getNativeEntityManagerFactory();
|
||||
EntityManager rawEntityManager = (!CollectionUtils.isEmpty(properties) ?
|
||||
nativeEmf.createEntityManager(properties) : nativeEmf.createEntityManager());
|
||||
EntityManager rawEntityManager = emfInfo.createNativeEntityManager(properties);
|
||||
return createProxy(rawEntityManager, emfInfo, true, synchronizedWithTransaction);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.springframework.orm.jpa;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
|
@ -128,6 +129,9 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
|
|||
|
||||
private JpaDialect jpaDialect = new DefaultJpaDialect();
|
||||
|
||||
@Nullable
|
||||
private Consumer<EntityManager> entityManagerInitializer;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new JpaTransactionManager instance.
|
||||
|
@ -298,6 +302,21 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
|
|||
return this.jpaDialect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a callback for customizing every {@code EntityManager} resource
|
||||
* created for a new transaction managed by this {@code JpaTransactionManager}.
|
||||
* <p>This is an alternative to a factory-level {@code EntityManager} customizer
|
||||
* and to a {@code JpaVendorAdapter}-level {@code postProcessEntityManager}
|
||||
* callback, enabling specific customizations of transactional resources.
|
||||
* @since 5.3
|
||||
* @see #createEntityManagerForTransaction()
|
||||
* @see AbstractEntityManagerFactoryBean#setEntityManagerInitializer
|
||||
* @see JpaVendorAdapter#postProcessEntityManager
|
||||
*/
|
||||
public void setEntityManagerInitializer(Consumer<EntityManager> entityManagerInitializer) {
|
||||
this.entityManagerInitializer = entityManagerInitializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an EntityManagerFactory by persistence unit name, if none set explicitly.
|
||||
* Falls back to a default EntityManagerFactory bean if no persistence unit specified.
|
||||
|
@ -398,7 +417,7 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
|
|||
EntityManager em = txObject.getEntityManagerHolder().getEntityManager();
|
||||
|
||||
// Delegate to JpaDialect for actual transaction begin.
|
||||
final int timeoutToUse = determineTimeout(definition);
|
||||
int timeoutToUse = determineTimeout(definition);
|
||||
Object transactionData = getJpaDialect().beginTransaction(em,
|
||||
new JpaTransactionDefinition(definition, timeoutToUse, txObject.isNewEntityManagerHolder()));
|
||||
txObject.setTransactionData(transactionData);
|
||||
|
@ -452,18 +471,27 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
|
|||
/**
|
||||
* Create a JPA EntityManager to be used for a transaction.
|
||||
* <p>The default implementation checks whether the EntityManagerFactory
|
||||
* is a Spring proxy and unwraps it first.
|
||||
* is a Spring proxy and delegates to
|
||||
* {@link EntityManagerFactoryInfo#createNativeEntityManager}
|
||||
* if possible which in turns applies
|
||||
* {@link JpaVendorAdapter#postProcessEntityManager(EntityManager)}.
|
||||
* @see javax.persistence.EntityManagerFactory#createEntityManager()
|
||||
* @see EntityManagerFactoryInfo#getNativeEntityManagerFactory()
|
||||
*/
|
||||
protected EntityManager createEntityManagerForTransaction() {
|
||||
EntityManagerFactory emf = obtainEntityManagerFactory();
|
||||
if (emf instanceof EntityManagerFactoryInfo) {
|
||||
emf = ((EntityManagerFactoryInfo) emf).getNativeEntityManagerFactory();
|
||||
}
|
||||
Map<String, Object> properties = getJpaPropertyMap();
|
||||
return (!CollectionUtils.isEmpty(properties) ?
|
||||
emf.createEntityManager(properties) : emf.createEntityManager());
|
||||
EntityManager em;
|
||||
if (emf instanceof EntityManagerFactoryInfo) {
|
||||
em = ((EntityManagerFactoryInfo) emf).createNativeEntityManager(properties);
|
||||
}
|
||||
else {
|
||||
em = (!CollectionUtils.isEmpty(properties) ?
|
||||
emf.createEntityManager(properties) : emf.createEntityManager());
|
||||
}
|
||||
if (this.entityManagerInitializer != null) {
|
||||
this.entityManagerInitializer.accept(em);
|
||||
}
|
||||
return em;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -137,4 +137,14 @@ public interface JpaVendorAdapter {
|
|||
default void postProcessEntityManagerFactory(EntityManagerFactory emf) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional callback for post-processing the native EntityManager
|
||||
* before active use.
|
||||
* <p>This can be used for setting vendor-specific parameters, e.g.
|
||||
* Hibernate filters, on every new EntityManager.
|
||||
* @since 5.3
|
||||
*/
|
||||
default void postProcessEntityManager(EntityManager em) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -160,4 +160,8 @@ public abstract class AbstractJpaVendorAdapter implements JpaVendorAdapter {
|
|||
public void postProcessEntityManagerFactory(EntityManagerFactory emf) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessEntityManager(EntityManager em) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -105,7 +105,7 @@ public class PersistenceInjectionTests extends AbstractEntityManagerFactoryBeanT
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testPublicExtendedPersistenceContextSetter() throws Exception {
|
||||
public void testPublicExtendedPersistenceContextSetter() {
|
||||
EntityManager mockEm = mock(EntityManager.class);
|
||||
given(mockEmf.createEntityManager()).willReturn(mockEm);
|
||||
|
||||
|
@ -123,7 +123,7 @@ public class PersistenceInjectionTests extends AbstractEntityManagerFactoryBeanT
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testPublicSpecificExtendedPersistenceContextSetter() throws Exception {
|
||||
public void testPublicSpecificExtendedPersistenceContextSetter() {
|
||||
EntityManagerFactory mockEmf2 = mock(EntityManagerFactory.class);
|
||||
EntityManager mockEm2 = mock(EntityManager.class);
|
||||
given(mockEmf2.createEntityManager()).willReturn(mockEm2);
|
||||
|
@ -194,16 +194,15 @@ public class PersistenceInjectionTests extends AbstractEntityManagerFactoryBeanT
|
|||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void testPublicExtendedPersistenceContextSetterWithEntityManagerInfoAndSerialization() throws Exception {
|
||||
EntityManager mockEm = mock(EntityManager.class, withSettings().serializable());
|
||||
given(mockEm.isOpen()).willReturn(true);
|
||||
EntityManagerFactoryWithInfo mockEmf = mock(EntityManagerFactoryWithInfo.class);
|
||||
given(mockEmf.getNativeEntityManagerFactory()).willReturn(mockEmf);
|
||||
given(mockEmf.getJpaDialect()).willReturn(new DefaultJpaDialect());
|
||||
given(mockEmf.getEntityManagerInterface()).willReturn((Class)EntityManager.class);
|
||||
given(mockEmf.getEntityManagerInterface()).willReturn((Class) EntityManager.class);
|
||||
given(mockEmf.getBeanClassLoader()).willReturn(getClass().getClassLoader());
|
||||
given(mockEmf.createEntityManager()).willReturn(mockEm);
|
||||
given(mockEmf.createNativeEntityManager(null)).willReturn(mockEm);
|
||||
|
||||
GenericApplicationContext gac = new GenericApplicationContext();
|
||||
gac.getDefaultListableBeanFactory().registerSingleton("entityManagerFactory", mockEmf);
|
||||
|
|
Loading…
Reference in New Issue