Skip to content

Commit 9bf7ff2

Browse files
committed
Introduce EntityManager initialization callbacks
JpaVendorAdapter.postProcessEntityManager and EntityManagerFactoryInfo.createNativeEntityManager SPI, plus convenient setEntityManagerInitializer configuration options. Closes spring-projectsgh-25125
1 parent a853a58 commit 9bf7ff2

8 files changed

Lines changed: 154 additions & 32 deletions

File tree

spring-orm/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818

1919
import java.sql.Connection;
2020
import java.sql.ResultSet;
21+
import java.util.function.Consumer;
2122

2223
import javax.persistence.PersistenceException;
2324
import javax.sql.DataSource;
@@ -125,6 +126,9 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
125126

126127
private boolean hibernateManagedSession = false;
127128

129+
@Nullable
130+
private Consumer<Session> sessionInitializer;
131+
128132
@Nullable
129133
private Object entityInterceptor;
130134

@@ -300,6 +304,17 @@ public void setHibernateManagedSession(boolean hibernateManagedSession) {
300304
this.hibernateManagedSession = hibernateManagedSession;
301305
}
302306

307+
/**
308+
* Specify a callback for customizing every Hibernate {@code Session} resource
309+
* created for a new transaction managed by this {@code HibernateTransactionManager}.
310+
* <p>This enables convenient customizations for application purposes, e.g.
311+
* setting Hibernate filters.
312+
* @since 5.3
313+
*/
314+
public void setSessionInitializer(Consumer<Session> sessionInitializer) {
315+
this.sessionInitializer = sessionInitializer;
316+
}
317+
303318
/**
304319
* Set the bean name of a Hibernate entity interceptor that allows to inspect
305320
* and change property values before writing to and reading from the database.
@@ -462,6 +477,9 @@ protected void doBegin(Object transaction, TransactionDefinition definition) {
462477
Session newSession = (entityInterceptor != null ?
463478
obtainSessionFactory().withOptions().interceptor(entityInterceptor).openSession() :
464479
obtainSessionFactory().openSession());
480+
if (this.sessionInitializer != null) {
481+
this.sessionInitializer.accept(session);
482+
}
465483
if (logger.isDebugEnabled()) {
466484
logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction");
467485
}

spring-orm/src/main/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryBean.java

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -32,6 +32,7 @@
3232
import java.util.Set;
3333
import java.util.concurrent.ExecutionException;
3434
import java.util.concurrent.Future;
35+
import java.util.function.Consumer;
3536

3637
import javax.persistence.EntityManager;
3738
import javax.persistence.EntityManagerFactory;
@@ -115,6 +116,9 @@ public abstract class AbstractEntityManagerFactoryBean implements
115116
@Nullable
116117
private JpaVendorAdapter jpaVendorAdapter;
117118

119+
@Nullable
120+
private Consumer<EntityManager> entityManagerInitializer;
121+
118122
@Nullable
119123
private AsyncTaskExecutor bootstrapExecutor;
120124

@@ -193,8 +197,8 @@ public String getPersistenceUnitName() {
193197
* {@code Persistence.createEntityManagerFactory} (if any).
194198
* <p>Can be populated with a String "value" (parsed via PropertiesEditor) or a
195199
* "props" element in XML bean definitions.
196-
* @see javax.persistence.Persistence#createEntityManagerFactory(String, java.util.Map)
197-
* @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(javax.persistence.spi.PersistenceUnitInfo, java.util.Map)
200+
* @see javax.persistence.Persistence#createEntityManagerFactory(String, Map)
201+
* @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(PersistenceUnitInfo, Map)
198202
*/
199203
public void setJpaProperties(Properties jpaProperties) {
200204
CollectionUtils.mergePropertiesIntoMap(jpaProperties, this.jpaPropertyMap);
@@ -204,8 +208,8 @@ public void setJpaProperties(Properties jpaProperties) {
204208
* Specify JPA properties as a Map, to be passed into
205209
* {@code Persistence.createEntityManagerFactory} (if any).
206210
* <p>Can be populated with a "map" or "props" element in XML bean definitions.
207-
* @see javax.persistence.Persistence#createEntityManagerFactory(String, java.util.Map)
208-
* @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(javax.persistence.spi.PersistenceUnitInfo, java.util.Map)
211+
* @see javax.persistence.Persistence#createEntityManagerFactory(String, Map)
212+
* @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(PersistenceUnitInfo, Map)
209213
*/
210214
public void setJpaPropertyMap(@Nullable Map<String, ?> jpaProperties) {
211215
if (jpaProperties != null) {
@@ -290,6 +294,20 @@ public JpaVendorAdapter getJpaVendorAdapter() {
290294
return this.jpaVendorAdapter;
291295
}
292296

297+
/**
298+
* Specify a callback for customizing every {@code EntityManager} created
299+
* by the exposed {@code EntityManagerFactory}.
300+
* <p>This is an alternative to a {@code JpaVendorAdapter}-level
301+
* {@code postProcessEntityManager} implementation, enabling convenient
302+
* customizations for application purposes, e.g. setting Hibernate filters.
303+
* @since 5.3
304+
* @see JpaVendorAdapter#postProcessEntityManager
305+
* @see JpaTransactionManager#setEntityManagerInitializer
306+
*/
307+
public void setEntityManagerInitializer(Consumer<EntityManager> entityManagerInitializer) {
308+
this.entityManagerInitializer = entityManagerInitializer;
309+
}
310+
293311
/**
294312
* Specify an asynchronous executor for background bootstrapping,
295313
* e.g. a {@link org.springframework.core.task.SimpleAsyncTaskExecutor}.
@@ -472,6 +490,7 @@ else if (method.getName().equals("createEntityManager") && args != null && args.
472490
EntityManager rawEntityManager = (args.length > 1 ?
473491
getNativeEntityManagerFactory().createEntityManager((Map<?, ?>) args[1]) :
474492
getNativeEntityManagerFactory().createEntityManager());
493+
postProcessEntityManager(rawEntityManager);
475494
return ExtendedEntityManagerCreator.createApplicationManagedEntityManager(rawEntityManager, this, true);
476495
}
477496

@@ -498,6 +517,7 @@ else if (method.getName().equals("createEntityManager") && args != null && args.
498517
if (retVal instanceof EntityManager) {
499518
// Any other createEntityManager variant - expecting non-synchronized semantics
500519
EntityManager rawEntityManager = (EntityManager) retVal;
520+
postProcessEntityManager(rawEntityManager);
501521
retVal = ExtendedEntityManagerCreator.createApplicationManagedEntityManager(rawEntityManager, this, false);
502522
}
503523
return retVal;
@@ -555,6 +575,36 @@ public EntityManagerFactory getNativeEntityManagerFactory() {
555575
}
556576
}
557577

578+
@Override
579+
public EntityManager createNativeEntityManager(@Nullable Map<?, ?> properties) {
580+
EntityManager rawEntityManager = (!CollectionUtils.isEmpty(properties) ?
581+
getNativeEntityManagerFactory().createEntityManager(properties) :
582+
getNativeEntityManagerFactory().createEntityManager());
583+
postProcessEntityManager(rawEntityManager);
584+
return rawEntityManager;
585+
}
586+
587+
/**
588+
* Optional callback for post-processing the native EntityManager
589+
* before active use.
590+
* <p>The default implementation delegates to
591+
* {@link JpaVendorAdapter#postProcessEntityManager}, if available.
592+
* @param rawEntityManager the EntityManager to post-process
593+
* @since 5.3
594+
* @see #createNativeEntityManager
595+
* @see JpaVendorAdapter#postProcessEntityManager
596+
*/
597+
protected void postProcessEntityManager(EntityManager rawEntityManager) {
598+
JpaVendorAdapter jpaVendorAdapter = getJpaVendorAdapter();
599+
if (jpaVendorAdapter != null) {
600+
jpaVendorAdapter.postProcessEntityManager(rawEntityManager);
601+
}
602+
Consumer<EntityManager> customizer = this.entityManagerInitializer;
603+
if (customizer != null) {
604+
customizer.accept(rawEntityManager);
605+
}
606+
}
607+
558608
@Override
559609
@Nullable
560610
public PersistenceUnitInfo getPersistenceUnitInfo() {

spring-orm/src/main/java/org/springframework/orm/jpa/EntityManagerFactoryInfo.java

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.orm.jpa;
1818

19+
import java.util.Map;
20+
1921
import javax.persistence.EntityManager;
2022
import javax.persistence.EntityManagerFactory;
2123
import javax.persistence.spi.PersistenceProvider;
@@ -36,12 +38,6 @@
3638
*/
3739
public interface EntityManagerFactoryInfo {
3840

39-
/**
40-
* Return the raw underlying EntityManagerFactory.
41-
* @return the unadorned EntityManagerFactory (never {@code null})
42-
*/
43-
EntityManagerFactory getNativeEntityManagerFactory();
44-
4541
/**
4642
* Return the underlying PersistenceProvider that the underlying
4743
* EntityManagerFactory was created with.
@@ -105,4 +101,23 @@ public interface EntityManagerFactoryInfo {
105101
*/
106102
ClassLoader getBeanClassLoader();
107103

104+
/**
105+
* Return the raw underlying EntityManagerFactory.
106+
* @return the unadorned EntityManagerFactory (never {@code null})
107+
*/
108+
EntityManagerFactory getNativeEntityManagerFactory();
109+
110+
/**
111+
* Create a native JPA EntityManager to be used as the framework-managed
112+
* resource behind an application-level EntityManager handle.
113+
* <p>This exposes a native {@code EntityManager} from the underlying
114+
* {@link #getNativeEntityManagerFactory() native EntityManagerFactory},
115+
* taking {@link JpaVendorAdapter#postProcessEntityManager(EntityManager)}
116+
* into account.
117+
* @since 5.3
118+
* @see #getNativeEntityManagerFactory()
119+
* @see EntityManagerFactory#createEntityManager()
120+
*/
121+
EntityManager createNativeEntityManager(@Nullable Map<?, ?> properties);
122+
108123
}

spring-orm/src/main/java/org/springframework/orm/jpa/ExtendedEntityManagerCreator.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,7 @@ public static EntityManager createContainerManagedEntityManager(
172172
Assert.notNull(emf, "EntityManagerFactory must not be null");
173173
if (emf instanceof EntityManagerFactoryInfo) {
174174
EntityManagerFactoryInfo emfInfo = (EntityManagerFactoryInfo) emf;
175-
EntityManagerFactory nativeEmf = emfInfo.getNativeEntityManagerFactory();
176-
EntityManager rawEntityManager = (!CollectionUtils.isEmpty(properties) ?
177-
nativeEmf.createEntityManager(properties) : nativeEmf.createEntityManager());
175+
EntityManager rawEntityManager = emfInfo.createNativeEntityManager(properties);
178176
return createProxy(rawEntityManager, emfInfo, true, synchronizedWithTransaction);
179177
}
180178
else {

spring-orm/src/main/java/org/springframework/orm/jpa/JpaTransactionManager.java

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.HashMap;
2020
import java.util.Map;
2121
import java.util.Properties;
22+
import java.util.function.Consumer;
2223

2324
import javax.persistence.EntityManager;
2425
import javax.persistence.EntityManagerFactory;
@@ -128,6 +129,9 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
128129

129130
private JpaDialect jpaDialect = new DefaultJpaDialect();
130131

132+
@Nullable
133+
private Consumer<EntityManager> entityManagerInitializer;
134+
131135

132136
/**
133137
* Create a new JpaTransactionManager instance.
@@ -298,6 +302,21 @@ public JpaDialect getJpaDialect() {
298302
return this.jpaDialect;
299303
}
300304

305+
/**
306+
* Specify a callback for customizing every {@code EntityManager} resource
307+
* created for a new transaction managed by this {@code JpaTransactionManager}.
308+
* <p>This is an alternative to a factory-level {@code EntityManager} customizer
309+
* and to a {@code JpaVendorAdapter}-level {@code postProcessEntityManager}
310+
* callback, enabling specific customizations of transactional resources.
311+
* @since 5.3
312+
* @see #createEntityManagerForTransaction()
313+
* @see AbstractEntityManagerFactoryBean#setEntityManagerInitializer
314+
* @see JpaVendorAdapter#postProcessEntityManager
315+
*/
316+
public void setEntityManagerInitializer(Consumer<EntityManager> entityManagerInitializer) {
317+
this.entityManagerInitializer = entityManagerInitializer;
318+
}
319+
301320
/**
302321
* Retrieves an EntityManagerFactory by persistence unit name, if none set explicitly.
303322
* Falls back to a default EntityManagerFactory bean if no persistence unit specified.
@@ -398,7 +417,7 @@ protected void doBegin(Object transaction, TransactionDefinition definition) {
398417
EntityManager em = txObject.getEntityManagerHolder().getEntityManager();
399418

400419
// Delegate to JpaDialect for actual transaction begin.
401-
final int timeoutToUse = determineTimeout(definition);
420+
int timeoutToUse = determineTimeout(definition);
402421
Object transactionData = getJpaDialect().beginTransaction(em,
403422
new JpaTransactionDefinition(definition, timeoutToUse, txObject.isNewEntityManagerHolder()));
404423
txObject.setTransactionData(transactionData);
@@ -452,18 +471,27 @@ protected void doBegin(Object transaction, TransactionDefinition definition) {
452471
/**
453472
* Create a JPA EntityManager to be used for a transaction.
454473
* <p>The default implementation checks whether the EntityManagerFactory
455-
* is a Spring proxy and unwraps it first.
474+
* is a Spring proxy and delegates to
475+
* {@link EntityManagerFactoryInfo#createNativeEntityManager}
476+
* if possible which in turns applies
477+
* {@link JpaVendorAdapter#postProcessEntityManager(EntityManager)}.
456478
* @see javax.persistence.EntityManagerFactory#createEntityManager()
457-
* @see EntityManagerFactoryInfo#getNativeEntityManagerFactory()
458479
*/
459480
protected EntityManager createEntityManagerForTransaction() {
460481
EntityManagerFactory emf = obtainEntityManagerFactory();
482+
Map<String, Object> properties = getJpaPropertyMap();
483+
EntityManager em;
461484
if (emf instanceof EntityManagerFactoryInfo) {
462-
emf = ((EntityManagerFactoryInfo) emf).getNativeEntityManagerFactory();
485+
em = ((EntityManagerFactoryInfo) emf).createNativeEntityManager(properties);
463486
}
464-
Map<String, Object> properties = getJpaPropertyMap();
465-
return (!CollectionUtils.isEmpty(properties) ?
466-
emf.createEntityManager(properties) : emf.createEntityManager());
487+
else {
488+
em = (!CollectionUtils.isEmpty(properties) ?
489+
emf.createEntityManager(properties) : emf.createEntityManager());
490+
}
491+
if (this.entityManagerInitializer != null) {
492+
this.entityManagerInitializer.accept(em);
493+
}
494+
return em;
467495
}
468496

469497
/**

spring-orm/src/main/java/org/springframework/orm/jpa/JpaVendorAdapter.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -137,4 +137,14 @@ default Class<? extends EntityManager> getEntityManagerInterface() {
137137
default void postProcessEntityManagerFactory(EntityManagerFactory emf) {
138138
}
139139

140+
/**
141+
* Optional callback for post-processing the native EntityManager
142+
* before active use.
143+
* <p>This can be used for setting vendor-specific parameters, e.g.
144+
* Hibernate filters, on every new EntityManager.
145+
* @since 5.3
146+
*/
147+
default void postProcessEntityManager(EntityManager em) {
148+
}
149+
140150
}

spring-orm/src/main/java/org/springframework/orm/jpa/vendor/AbstractJpaVendorAdapter.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -160,4 +160,8 @@ public Class<? extends EntityManager> getEntityManagerInterface() {
160160
public void postProcessEntityManagerFactory(EntityManagerFactory emf) {
161161
}
162162

163+
@Override
164+
public void postProcessEntityManager(EntityManager em) {
165+
}
166+
163167
}

0 commit comments

Comments
 (0)