Skip to content

Commit d3ec48f

Browse files
committed
PersistenceAnnotationBeanPostProcessor correctly detects JPA 2.1 synchronization attribute
Issue: SPR-12396 (cherry picked from commit a181b40)
1 parent 2d874d7 commit d3ec48f

File tree

2 files changed

+183
-13
lines changed

2 files changed

+183
-13
lines changed

spring-orm/src/main/java/org/springframework/orm/jpa/support/PersistenceAnnotationBeanPostProcessor.java

+13-13
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
* with the "unitName" attribute, or no attribute at all (for the default unit).
8383
* If those annotations are present with the "name" attribute at the class level,
8484
* they will simply be ignored, since those only serve as deployment hint
85-
* (as per the Java EE 5 specification).
85+
* (as per the Java EE specification).
8686
*
8787
* <p>This post-processor can either obtain EntityManagerFactory beans defined
8888
* in the Spring application context (the default), or obtain EntityManagerFactory
@@ -167,9 +167,9 @@ public class PersistenceAnnotationBeanPostProcessor
167167
implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor,
168168
MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware, Serializable {
169169

170-
/* Check JPA 2.1 PersistenceContext.synchronizationType attribute */
171-
private static final Method synchronizationTypeAttribute =
172-
ClassUtils.getMethodIfAvailable(PersistenceContext.class, "synchronizationType");
170+
/* Check JPA 2.1 PersistenceContext.synchronization() attribute */
171+
private static final Method synchronizationAttribute =
172+
ClassUtils.getMethodIfAvailable(PersistenceContext.class, "synchronization");
173173

174174

175175
private Object jndiEnvironment;
@@ -231,16 +231,16 @@ public void setResourceRef(boolean resourceRef) {
231231
* for the {@link #setDefaultPersistenceUnitName default persistence unit}
232232
* will be taken (by default, the value mapped to the empty String),
233233
* or simply the single persistence unit if there is only one.
234-
* <p>This is mainly intended for use in a Java EE 5 environment, with all
235-
* lookup driven by the standard JPA annotations, and all EntityManagerFactory
234+
* <p>This is mainly intended for use in a Java EE environment, with all lookup
235+
* driven by the standard JPA annotations, and all EntityManagerFactory
236236
* references obtained from JNDI. No separate EntityManagerFactory bean
237237
* definitions are necessary in such a scenario.
238238
* <p>If no corresponding "persistenceContexts"/"extendedPersistenceContexts"
239239
* are specified, {@code @PersistenceContext} will be resolved to
240240
* EntityManagers built on top of the EntityManagerFactory defined here.
241241
* Note that those will be Spring-managed EntityManagers, which implement
242242
* transaction synchronization based on Spring's facilities.
243-
* If you prefer the Java EE 5 server's own EntityManager handling,
243+
* If you prefer the Java EE server's own EntityManager handling,
244244
* specify corresponding "persistenceContexts"/"extendedPersistenceContexts".
245245
*/
246246
public void setPersistenceUnits(Map<String, String> persistenceUnits) {
@@ -258,11 +258,11 @@ public void setPersistenceUnits(Map<String, String> persistenceUnits) {
258258
* for the {@link #setDefaultPersistenceUnitName default persistence unit}
259259
* will be taken (by default, the value mapped to the empty String),
260260
* or simply the single persistence unit if there is only one.
261-
* <p>This is mainly intended for use in a Java EE 5 environment, with all
261+
* <p>This is mainly intended for use in a Java EE environment, with all
262262
* lookup driven by the standard JPA annotations, and all EntityManager
263263
* references obtained from JNDI. No separate EntityManagerFactory bean
264264
* definitions are necessary in such a scenario, and all EntityManager
265-
* handling is done by the Java EE 5 server itself.
265+
* handling is done by the Java EE server itself.
266266
*/
267267
public void setPersistenceContexts(Map<String, String> persistenceContexts) {
268268
this.persistenceContexts = persistenceContexts;
@@ -279,11 +279,11 @@ public void setPersistenceContexts(Map<String, String> persistenceContexts) {
279279
* for the {@link #setDefaultPersistenceUnitName default persistence unit}
280280
* will be taken (by default, the value mapped to the empty String),
281281
* or simply the single persistence unit if there is only one.
282-
* <p>This is mainly intended for use in a Java EE 5 environment, with all
282+
* <p>This is mainly intended for use in a Java EE environment, with all
283283
* lookup driven by the standard JPA annotations, and all EntityManager
284284
* references obtained from JNDI. No separate EntityManagerFactory bean
285285
* definitions are necessary in such a scenario, and all EntityManager
286-
* handling is done by the Java EE 5 server itself.
286+
* handling is done by the Java EE server itself.
287287
*/
288288
public void setExtendedPersistenceContexts(Map<String, String> extendedPersistenceContexts) {
289289
this.extendedPersistenceContexts = extendedPersistenceContexts;
@@ -632,8 +632,8 @@ public PersistenceElement(Member member, PropertyDescriptor pd) {
632632
}
633633
this.unitName = pc.unitName();
634634
this.type = pc.type();
635-
this.synchronizedWithTransaction = (synchronizationTypeAttribute == null ||
636-
"SYNCHRONIZED".equals(ReflectionUtils.invokeMethod(synchronizationTypeAttribute, pc).toString()));
635+
this.synchronizedWithTransaction = (synchronizationAttribute == null ||
636+
"SYNCHRONIZED".equals(ReflectionUtils.invokeMethod(synchronizationAttribute, pc).toString()));
637637
this.properties = properties;
638638
}
639639
else {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* Copyright 2002-2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.orm.jpa.support;
18+
19+
import javax.persistence.EntityManager;
20+
import javax.persistence.EntityManagerFactory;
21+
import javax.persistence.EntityTransaction;
22+
import javax.persistence.PersistenceContext;
23+
import javax.persistence.PersistenceContextType;
24+
25+
import org.junit.After;
26+
import org.junit.Before;
27+
import org.junit.Test;
28+
29+
import org.springframework.orm.jpa.JpaTransactionManager;
30+
import org.springframework.transaction.TransactionDefinition;
31+
import org.springframework.transaction.TransactionStatus;
32+
import org.springframework.transaction.support.TransactionCallback;
33+
import org.springframework.transaction.support.TransactionSynchronizationManager;
34+
import org.springframework.transaction.support.TransactionTemplate;
35+
36+
import static org.junit.Assert.*;
37+
import static org.mockito.BDDMockito.*;
38+
39+
/**
40+
* @author Juergen Hoeller
41+
* @since 4.1.2
42+
*/
43+
public class PersistenceContextTransactionTests {
44+
45+
private EntityManagerFactory factory;
46+
47+
private EntityManager manager;
48+
49+
private EntityTransaction tx;
50+
51+
private TransactionTemplate tt;
52+
53+
private EntityManagerHoldingBean bean;
54+
55+
56+
@Before
57+
public void setUp() throws Exception {
58+
factory = mock(EntityManagerFactory.class);
59+
manager = mock(EntityManager.class);
60+
tx = mock(EntityTransaction.class);
61+
62+
JpaTransactionManager tm = new JpaTransactionManager(factory);
63+
tt = new TransactionTemplate(tm);
64+
65+
given(factory.createEntityManager()).willReturn(manager);
66+
given(manager.getTransaction()).willReturn(tx);
67+
given(manager.isOpen()).willReturn(true);
68+
69+
bean = new EntityManagerHoldingBean();
70+
PersistenceAnnotationBeanPostProcessor pabpp = new PersistenceAnnotationBeanPostProcessor() {
71+
@Override
72+
protected EntityManagerFactory findEntityManagerFactory(String unitName, String requestingBeanName) {
73+
return factory;
74+
}
75+
};
76+
pabpp.postProcessPropertyValues(null, null, bean, "bean");
77+
78+
assertTrue(TransactionSynchronizationManager.getResourceMap().isEmpty());
79+
assertFalse(TransactionSynchronizationManager.isSynchronizationActive());
80+
}
81+
82+
@After
83+
public void tearDown() throws Exception {
84+
assertTrue(TransactionSynchronizationManager.getResourceMap().isEmpty());
85+
assertFalse(TransactionSynchronizationManager.isSynchronizationActive());
86+
assertFalse(TransactionSynchronizationManager.isCurrentTransactionReadOnly());
87+
assertFalse(TransactionSynchronizationManager.isActualTransactionActive());
88+
}
89+
90+
91+
@Test
92+
public void testTransactionCommitWithSharedEntityManager() {
93+
given(manager.getTransaction()).willReturn(tx);
94+
95+
tt.execute(new TransactionCallback() {
96+
@Override
97+
public Object doInTransaction(TransactionStatus status) {
98+
bean.sharedEntityManager.flush();
99+
return null;
100+
}
101+
});
102+
103+
verify(tx).commit();
104+
verify(manager).flush();
105+
verify(manager).close();
106+
}
107+
108+
@Test
109+
public void testTransactionCommitWithSharedEntityManagerAndPropagationSupports() {
110+
given(manager.isOpen()).willReturn(true);
111+
112+
tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
113+
114+
tt.execute(new TransactionCallback() {
115+
@Override
116+
public Object doInTransaction(TransactionStatus status) {
117+
bean.sharedEntityManager.flush();
118+
return null;
119+
}
120+
});
121+
122+
verify(manager).flush();
123+
verify(manager).close();
124+
}
125+
126+
@Test
127+
public void testTransactionCommitWithExtendedEntityManager() {
128+
given(manager.getTransaction()).willReturn(tx);
129+
130+
tt.execute(new TransactionCallback() {
131+
@Override
132+
public Object doInTransaction(TransactionStatus status) {
133+
bean.extendedEntityManager.flush();
134+
return null;
135+
}
136+
});
137+
138+
verify(tx, times(2)).commit();
139+
verify(manager).flush();
140+
verify(manager).close();
141+
}
142+
143+
@Test
144+
public void testTransactionCommitWithExtendedEntityManagerAndPropagationSupports() {
145+
given(manager.isOpen()).willReturn(true);
146+
147+
tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
148+
149+
tt.execute(new TransactionCallback() {
150+
@Override
151+
public Object doInTransaction(TransactionStatus status) {
152+
bean.extendedEntityManager.flush();
153+
return null;
154+
}
155+
});
156+
157+
verify(manager).flush();
158+
}
159+
160+
161+
public static class EntityManagerHoldingBean {
162+
163+
@PersistenceContext
164+
public EntityManager sharedEntityManager;
165+
166+
@PersistenceContext(type = PersistenceContextType.EXTENDED)
167+
public EntityManager extendedEntityManager;
168+
}
169+
170+
}

0 commit comments

Comments
 (0)