Skip to content

Commit 045d735

Browse files
committed
SharedEntityManagerCreator immediately throws TransactionRequiredException on persist, merge, remove etc (as required by JPA spec)
Issue: SPR-11923
1 parent cab0b97 commit 045d735

File tree

2 files changed

+73
-3
lines changed

2 files changed

+73
-3
lines changed

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

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@
2323
import java.lang.reflect.InvocationTargetException;
2424
import java.lang.reflect.Method;
2525
import java.lang.reflect.Proxy;
26+
import java.util.HashSet;
2627
import java.util.Map;
28+
import java.util.Set;
2729
import javax.persistence.EntityManager;
2830
import javax.persistence.EntityManagerFactory;
2931
import javax.persistence.Query;
32+
import javax.persistence.TransactionRequiredException;
3033

3134
import org.apache.commons.logging.Log;
3235
import org.apache.commons.logging.LogFactory;
@@ -61,6 +64,23 @@ public abstract class SharedEntityManagerCreator {
6164

6265
private static final Class<?>[] NO_ENTITY_MANAGER_INTERFACES = new Class<?>[0];
6366

67+
private static final Set<String> transactionRequiringMethods = new HashSet<String>(6);
68+
69+
private static final Set<String> queryTerminationMethods = new HashSet<String>(3);
70+
71+
static {
72+
transactionRequiringMethods.add("joinTransaction");
73+
transactionRequiringMethods.add("flush");
74+
transactionRequiringMethods.add("persist");
75+
transactionRequiringMethods.add("merge");
76+
transactionRequiringMethods.add("remove");
77+
transactionRequiringMethods.add("refresh");
78+
79+
queryTerminationMethods.add("getResultList");
80+
queryTerminationMethods.add("getSingleResult");
81+
queryTerminationMethods.add("executeUpdate");
82+
}
83+
6484

6585
/**
6686
* Create a transactional EntityManager proxy for the given EntityManagerFactory.
@@ -246,6 +266,13 @@ else if (method.getName().equals("unwrap")) {
246266
}
247267
// Still perform unwrap call on target EntityManager.
248268
}
269+
else if (transactionRequiringMethods.contains(method.getName())) {
270+
// We need a transactional target now, according to the JPA spec.
271+
// Otherwise, the operation would get accepted but remain unflushed...
272+
if (target == null) {
273+
throw new TransactionRequiredException("No transactional EntityManager available");
274+
}
275+
}
249276

250277
// Regular EntityManager operations.
251278
boolean isNewEm = false;
@@ -337,8 +364,7 @@ else if (method.getName().equals("unwrap")) {
337364
throw ex.getTargetException();
338365
}
339366
finally {
340-
if (method.getName().equals("getResultList") || method.getName().equals("getSingleResult") ||
341-
method.getName().equals("executeUpdate")) {
367+
if (queryTerminationMethods.contains(method.getName())) {
342368
// Actual execution of the query: close the EntityManager right
343369
// afterwards, since that was the only reason we kept it open.
344370
EntityManagerFactoryUtils.closeEntityManager(this.em);

spring-orm/src/test/java/org/springframework/orm/jpa/SharedEntityManagerCreatorTests.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 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,7 +16,9 @@
1616

1717
package org.springframework.orm.jpa;
1818

19+
import javax.persistence.EntityManager;
1920
import javax.persistence.EntityManagerFactory;
21+
import javax.persistence.TransactionRequiredException;
2022

2123
import org.junit.Test;
2224

@@ -40,4 +42,46 @@ public void proxyingWorksIfInfoReturnsNullEntityManagerInterface() {
4042
is(notNullValue()));
4143
}
4244

45+
@Test(expected = TransactionRequiredException.class)
46+
public void transactionRequiredExceptionOnJoinTransaction() {
47+
EntityManagerFactory emf = mock(EntityManagerFactory.class);
48+
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
49+
em.joinTransaction();
50+
}
51+
52+
@Test(expected = TransactionRequiredException.class)
53+
public void transactionRequiredExceptionOnFlush() {
54+
EntityManagerFactory emf = mock(EntityManagerFactory.class);
55+
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
56+
em.flush();
57+
}
58+
59+
@Test(expected = TransactionRequiredException.class)
60+
public void transactionRequiredExceptionOnPersist() {
61+
EntityManagerFactory emf = mock(EntityManagerFactory.class);
62+
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
63+
em.persist(new Object());
64+
}
65+
66+
@Test(expected = TransactionRequiredException.class)
67+
public void transactionRequiredExceptionOnMerge() {
68+
EntityManagerFactory emf = mock(EntityManagerFactory.class);
69+
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
70+
em.merge(new Object());
71+
}
72+
73+
@Test(expected = TransactionRequiredException.class)
74+
public void transactionRequiredExceptionOnRemove() {
75+
EntityManagerFactory emf = mock(EntityManagerFactory.class);
76+
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
77+
em.remove(new Object());
78+
}
79+
80+
@Test(expected = TransactionRequiredException.class)
81+
public void transactionRequiredExceptionOnRefresh() {
82+
EntityManagerFactory emf = mock(EntityManagerFactory.class);
83+
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
84+
em.refresh(new Object());
85+
}
86+
4387
}

0 commit comments

Comments
 (0)