Skip to content

Commit 2f7fa3f

Browse files
samebGuice Team
authored andcommitted
Don't wrap methods declared by Object in transactions. Fixes #958, fixes #788.
PiperOrigin-RevId: 526028062
1 parent 2d78877 commit 2f7fa3f

File tree

4 files changed

+157
-12
lines changed

4 files changed

+157
-12
lines changed

extensions/persist/pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515

1616
<dependencies>
1717
<dependency>
18-
<groupId>org.hibernate.javax.persistence</groupId>
19-
<artifactId>hibernate-jpa-2.0-api</artifactId>
20-
<version>1.0.0.Final</version>
18+
<groupId>javax.persistence</groupId>
19+
<artifactId>javax.persistence-api</artifactId>
20+
<version>2.2</version>
2121
<scope>provided</scope>
2222
</dependency>
2323
<dependency>

extensions/persist/src/com/google/inject/persist/PersistModule.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
import com.google.inject.AbstractModule;
2323
import com.google.inject.internal.InternalFlags;
24+
import com.google.inject.matcher.AbstractMatcher;
25+
import com.google.inject.matcher.Matcher;
26+
import java.lang.reflect.Method;
2427
import org.aopalliance.intercept.MethodInterceptor;
2528

2629
/**
@@ -38,7 +41,8 @@ protected final void configure() {
3841
requireBinding(UnitOfWork.class);
3942
if (InternalFlags.isBytecodeGenEnabled()) {
4043
// class-level @Transacational
41-
bindInterceptor(annotatedWith(Transactional.class), any(), getTransactionInterceptor());
44+
bindInterceptor(
45+
annotatedWith(Transactional.class), NOT_OBJECT_METHOD, getTransactionInterceptor());
4246
// method-level @Transacational
4347
bindInterceptor(any(), annotatedWith(Transactional.class), getTransactionInterceptor());
4448
}
@@ -47,4 +51,12 @@ protected final void configure() {
4751
protected abstract void configurePersistence();
4852

4953
protected abstract MethodInterceptor getTransactionInterceptor();
54+
55+
private static final Matcher<Method> NOT_OBJECT_METHOD =
56+
new AbstractMatcher<Method>() {
57+
@Override
58+
public boolean matches(Method m) {
59+
return !Object.class.equals(m.getDeclaringClass());
60+
}
61+
};
5062
}

extensions/persist/test/com/google/inject/persist/jpa/ClassLevelManagedLocalTransactionsTest.java

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.Date;
2727
import java.util.List;
2828
import javax.persistence.EntityManager;
29+
import javax.persistence.EntityManagerFactory;
2930
import junit.framework.TestCase;
3031

3132
/**
@@ -48,7 +49,7 @@ public class ClassLevelManagedLocalTransactionsTest extends TestCase {
4849
public void setUp() {
4950
injector = Guice.createInjector(new JpaPersistModule("testUnit"));
5051

51-
//startup persistence
52+
// startup persistence
5253
injector.getInstance(PersistService.class).start();
5354
}
5455

@@ -66,7 +67,7 @@ public void testSimpleTransaction() {
6667
"EntityManager was not closed by transactional service",
6768
session.getTransaction().isActive());
6869

69-
//test that the data has been stored
70+
// test that the data has been stored
7071
session.getTransaction().begin();
7172
Object result =
7273
session
@@ -88,15 +89,15 @@ public void testSimpleTransactionRollbackOnChecked() {
8889
try {
8990
injector.getInstance(TransactionalObject2.class).runOperationInTxnThrowingChecked();
9091
} catch (IOException e) {
91-
//ignore
92+
// ignore
9293
}
9394

9495
EntityManager session = injector.getInstance(EntityManager.class);
9596
assertFalse(
9697
"EntityManager was not closed by transactional service (rollback didnt happen?)",
9798
session.getTransaction().isActive());
9899

99-
//test that the data has been stored
100+
// test that the data has been stored
100101
session.getTransaction().begin();
101102
List<?> result =
102103
session
@@ -115,15 +116,15 @@ public void testSimpleTransactionRollbackOnCheckedExcepting() {
115116
injector.getInstance(TransactionalObject3.class).runOperationInTxnThrowingCheckedExcepting();
116117
fail("Exception was not thrown by test txn-al method!");
117118
} catch (IOException e) {
118-
//ignored
119+
// ignored
119120
}
120121

121122
EntityManager session = injector.getInstance(EntityManager.class);
122123
assertFalse(
123124
"Txn was not closed by transactional service (commit didnt happen?)",
124125
session.getTransaction().isActive());
125126

126-
//test that the data has been stored
127+
// test that the data has been stored
127128
session.getTransaction().begin();
128129
Object result =
129130
session
@@ -140,15 +141,15 @@ public void testSimpleTransactionRollbackOnUnchecked() {
140141
try {
141142
injector.getInstance(TransactionalObject4.class).runOperationInTxnThrowingUnchecked();
142143
} catch (RuntimeException re) {
143-
//ignore
144+
// ignore
144145
}
145146

146147
EntityManager session = injector.getInstance(EntityManager.class);
147148
assertFalse(
148149
"EntityManager was not closed by transactional service (rollback didnt happen?)",
149150
session.getTransaction().isActive());
150151

151-
//test that the data has been stored
152+
// test that the data has been stored
152153
session.getTransaction().begin();
153154
List<?> result =
154155
session
@@ -161,6 +162,26 @@ public void testSimpleTransactionRollbackOnUnchecked() {
161162
assertTrue("a result was returned! rollback sure didnt happen!!!", result.isEmpty());
162163
}
163164

165+
public void testTransactionalDoesntAffectObjectMethods() {
166+
// Given a persist service that tracks when it's called
167+
JpaPersistService persistService = injector.getInstance(JpaPersistService.class);
168+
EntityManagerFactory originalEMF = injector.getInstance(EntityManagerFactory.class);
169+
TrackedEntityManagerFactory trackingEMF = new TrackedEntityManagerFactory(originalEMF);
170+
persistService.start(trackingEMF);
171+
172+
FakeTransactionalObject txnObj = injector.getInstance(FakeTransactionalObject.class);
173+
174+
String unused = txnObj.toString();
175+
assertFalse(
176+
"Should not have created a transaction for toString method",
177+
trackingEMF.hasCreatedSomeEntityManager());
178+
179+
txnObj.fakeTransactionalMethod();
180+
assertTrue(
181+
"Transaction should have been created for normal method",
182+
trackingEMF.hasCreatedSomeEntityManager());
183+
}
184+
164185
@Transactional
165186
public static class TransactionalObject {
166187
@Inject EntityManager session;
@@ -215,4 +236,9 @@ public void runOperationInTxnThrowingChecked() throws IOException {
215236
throw new IOException();
216237
}
217238
}
239+
240+
@Transactional
241+
public static class FakeTransactionalObject {
242+
public void fakeTransactionalMethod() {}
243+
}
218244
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package com.google.inject.persist.jpa;
2+
3+
import java.util.Map;
4+
import javax.persistence.Cache;
5+
import javax.persistence.EntityGraph;
6+
import javax.persistence.EntityManager;
7+
import javax.persistence.EntityManagerFactory;
8+
import javax.persistence.PersistenceUnitUtil;
9+
import javax.persistence.Query;
10+
import javax.persistence.SynchronizationType;
11+
import javax.persistence.criteria.CriteriaBuilder;
12+
import javax.persistence.metamodel.Metamodel;
13+
14+
/** Proxy EntityManager that adds tracking capabilities, keeping a count of created objects. */
15+
class TrackedEntityManagerFactory implements EntityManagerFactory {
16+
17+
private final EntityManagerFactory delegate;
18+
private int entityManagerCreatedCount = 0;
19+
20+
public TrackedEntityManagerFactory(EntityManagerFactory delegate) {
21+
this.delegate = delegate;
22+
}
23+
24+
public boolean hasCreatedSomeEntityManager() {
25+
return (entityManagerCreatedCount > 0);
26+
}
27+
28+
public int getEntityManagerCreatedCount() {
29+
return entityManagerCreatedCount;
30+
}
31+
32+
@Override
33+
public boolean isOpen() {
34+
return delegate.isOpen();
35+
}
36+
37+
@Override
38+
public Map<String, Object> getProperties() {
39+
return delegate.getProperties();
40+
}
41+
42+
@Override
43+
public PersistenceUnitUtil getPersistenceUnitUtil() {
44+
return delegate.getPersistenceUnitUtil();
45+
}
46+
47+
@Override
48+
public Metamodel getMetamodel() {
49+
return delegate.getMetamodel();
50+
}
51+
52+
@Override
53+
public CriteriaBuilder getCriteriaBuilder() {
54+
return delegate.getCriteriaBuilder();
55+
}
56+
57+
@Override
58+
public Cache getCache() {
59+
return delegate.getCache();
60+
}
61+
62+
@SuppressWarnings("rawtypes")
63+
@Override
64+
public EntityManager createEntityManager(Map arg0) {
65+
entityManagerCreatedCount++;
66+
return delegate.createEntityManager(arg0);
67+
}
68+
69+
@Override
70+
public EntityManager createEntityManager() {
71+
entityManagerCreatedCount++;
72+
return delegate.createEntityManager();
73+
}
74+
75+
@Override
76+
public EntityManager createEntityManager(SynchronizationType synchronizationType) {
77+
entityManagerCreatedCount++;
78+
return delegate.createEntityManager(synchronizationType);
79+
}
80+
81+
@SuppressWarnings("rawtypes")
82+
@Override
83+
public EntityManager createEntityManager(SynchronizationType synchronizationType, Map map) {
84+
entityManagerCreatedCount++;
85+
return delegate.createEntityManager(synchronizationType, map);
86+
}
87+
88+
@Override
89+
public void close() {
90+
delegate.close();
91+
}
92+
93+
@Override
94+
public void addNamedQuery(String name, Query query) {
95+
delegate.addNamedQuery(name, query);
96+
}
97+
98+
@Override
99+
public <T> T unwrap(Class<T> cls) {
100+
return delegate.unwrap(cls);
101+
}
102+
103+
@Override
104+
public <T> void addNamedEntityGraph(String graphName, EntityGraph<T> entityGraph) {
105+
delegate.addNamedEntityGraph(graphName, entityGraph);
106+
}
107+
}

0 commit comments

Comments
 (0)