提交 ff160f9a 编写于 作者: J Juergen Hoeller 提交者: unknown

JPA 2.1 support, in particular for SynchronizationType.UNSYNCHRONIZED

In the course of this effort, our joinTransaction() support has been overhauled to work for shared EntityManagers as well, since an unsynchronized shared EntityManager will nevertheless be scoped for the current transaction but not automatically join it (as per the JPA 2.1 spec). In the JTA case, we'll simply create an unsynchronized target EntityManager and will upgrade it to a synchronized one (later in the same transaction, if necessary) through an implicit joinTransaction() call. In the JpaTransactionManager case, we'll unbind the primary target EntityManager and will expose an unsynchronized EntityManager instead, upgrading it the same way as with JTA, with the primary EntityManager in that case just serving as a vehicle for transaction begin/commit/rollback calls.

For extended EntityManagers, we've just added further combinations to the existing variants: application-managed EntityManagers which may nevertheless auto-join transactions (EntityManagerFactory.createEntityManager with SynchronizationType.SYNCHRONIZED), and container-managed EntityManagers which might opt for not auto-joining transactions (@PersistenceContext with synchronizationType=UNSYNCHRONIZED).

Issue: SPR-8194
上级 a3d7dc09
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
......@@ -373,10 +373,22 @@ public abstract class AbstractEntityManagerFactoryBean implements
else if (method.getDeclaringClass().equals(EntityManagerFactoryPlusOperations.class)) {
return method.invoke(this.plusOperations, args);
}
else if (method.getName().equals("createEntityManager") && args != null && args.length > 0 &&
args[0] != null && args[0].getClass().isEnum() && "SYNCHRONIZED".equals(args[0].toString())) {
// JPA 2.1's createEntityManager(SynchronizationType, Map)
// Redirect to plain createEntityManager and add synchronization semantics through Spring proxy
EntityManager rawEntityManager = (args.length > 1 ?
this.nativeEntityManagerFactory.createEntityManager((Map) args[1]) :
this.nativeEntityManagerFactory.createEntityManager());
return ExtendedEntityManagerCreator.createApplicationManagedEntityManager(rawEntityManager, this, true);
}
// Standard delegation to the native factory, just post-processing EntityManager return values
Object retVal = method.invoke(this.nativeEntityManagerFactory, args);
if (retVal instanceof EntityManager) {
// Any other createEntityManager variant - expecting non-synchronized semantics
EntityManager rawEntityManager = (EntityManager) retVal;
retVal = ExtendedEntityManagerCreator.createApplicationManagedEntityManager(rawEntityManager, this);
retVal = ExtendedEntityManagerCreator.createApplicationManagedEntityManager(rawEntityManager, this, false);
}
return retVal;
}
......
......@@ -16,6 +16,7 @@
package org.springframework.orm.jpa;
import java.lang.reflect.Method;
import java.util.Map;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
......@@ -45,7 +46,9 @@ import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.support.ResourceHolderSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
......@@ -71,6 +74,25 @@ public abstract class EntityManagerFactoryUtils {
private static final Log logger = LogFactory.getLog(EntityManagerFactoryUtils.class);
private static Method createEntityManagerWithSynchronizationTypeMethod;
private static Object synchronizationTypeUnsynchronized;
static {
try {
@SuppressWarnings("unchecked")
Class<Enum> synchronizationTypeClass = (Class<Enum>) ClassUtils.forName(
"javax.persistence.SynchronizationType", EntityManagerFactoryUtils.class.getClassLoader());
createEntityManagerWithSynchronizationTypeMethod = EntityManagerFactory.class.getMethod(
"createEntityManager", synchronizationTypeClass, Map.class);
synchronizationTypeUnsynchronized = Enum.valueOf(synchronizationTypeClass, "UNSYNCHRONIZED");
}
catch (Exception ex) {
// No JPA 2.1 API available
createEntityManagerWithSynchronizationTypeMethod = null;
}
}
/**
* Find an EntityManagerFactory with the given name in the given
......@@ -146,7 +168,7 @@ public abstract class EntityManagerFactoryUtils {
public static EntityManager getTransactionalEntityManager(EntityManagerFactory emf, Map properties)
throws DataAccessResourceFailureException {
try {
return doGetTransactionalEntityManager(emf, properties);
return doGetTransactionalEntityManager(emf, properties, true);
}
catch (PersistenceException ex) {
throw new DataAccessResourceFailureException("Could not obtain JPA EntityManager", ex);
......@@ -166,53 +188,113 @@ public abstract class EntityManagerFactoryUtils {
* @see #getTransactionalEntityManager(javax.persistence.EntityManagerFactory)
* @see JpaTransactionManager
*/
public static EntityManager doGetTransactionalEntityManager(EntityManagerFactory emf, Map properties)
throws PersistenceException {
return doGetTransactionalEntityManager(emf, properties, true);
}
/**
* Obtain a JPA EntityManager from the given factory. Is aware of a
* corresponding EntityManager bound to the current thread,
* for example when using JpaTransactionManager.
* <p>Same as {@code getEntityManager}, but throwing the original PersistenceException.
* @param emf EntityManagerFactory to create the EntityManager with
* @param properties the properties to be passed into the {@code createEntityManager}
* call (may be {@code null})
* @param synchronizedWithTransaction whether to automatically join ongoing
* transactions (according to the JPA 2.1 SynchronizationType rules)
* @return the EntityManager, or {@code null} if none found
* @throws javax.persistence.PersistenceException if the EntityManager couldn't be created
* @see #getTransactionalEntityManager(javax.persistence.EntityManagerFactory)
* @see JpaTransactionManager
*/
public static EntityManager doGetTransactionalEntityManager(
EntityManagerFactory emf, Map properties) throws PersistenceException {
EntityManagerFactory emf, Map properties, boolean synchronizedWithTransaction) throws PersistenceException {
Assert.notNull(emf, "No EntityManagerFactory specified");
EntityManagerHolder emHolder =
(EntityManagerHolder) TransactionSynchronizationManager.getResource(emf);
if (emHolder != null) {
if (!emHolder.isSynchronizedWithTransaction() &&
TransactionSynchronizationManager.isSynchronizationActive()) {
// Try to explicitly synchronize the EntityManager itself
// with an ongoing JTA transaction, if any.
try {
emHolder.getEntityManager().joinTransaction();
if (synchronizedWithTransaction) {
if (!emHolder.isSynchronizedWithTransaction() &&
TransactionSynchronizationManager.isSynchronizationActive()) {
// Try to explicitly synchronize the EntityManager itself
// with an ongoing JTA transaction, if any.
try {
emHolder.getEntityManager().joinTransaction();
}
catch (TransactionRequiredException ex) {
logger.debug("Could not join transaction because none was actually active", ex);
}
Object transactionData = prepareTransaction(emHolder.getEntityManager(), emf);
TransactionSynchronizationManager.registerSynchronization(
new TransactionalEntityManagerSynchronization(emHolder, emf, transactionData, false));
emHolder.setSynchronizedWithTransaction(true);
}
catch (TransactionRequiredException ex) {
logger.debug("Could not join JTA transaction because none was active", ex);
// Use holder's reference count to track synchronizedWithTransaction access.
// isOpen() check used below to find out about it.
emHolder.requested();
return emHolder.getEntityManager();
}
else {
// unsynchronized EntityManager demanded
if (emHolder.isTransactionActive() && !emHolder.isOpen()) {
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
return null;
}
// EntityManagerHolder with an active transaction coming from JpaTransactionManager,
// with no synchronized EntityManager having been requested by application code before.
// Unbind in order to register a new unsynchronized EntityManager instead.
TransactionSynchronizationManager.unbindResource(emf);
}
else {
// Either a previously bound unsynchronized EntityManager, or the application
// has requested a synchronized EntityManager before and therefore upgraded
// this transaction's EntityManager to synchronized before.
return emHolder.getEntityManager();
}
Object transactionData = prepareTransaction(emHolder.getEntityManager(), emf);
TransactionSynchronizationManager.registerSynchronization(
new EntityManagerSynchronization(emHolder, emf, transactionData, false));
emHolder.setSynchronizedWithTransaction(true);
}
return emHolder.getEntityManager();
}
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
else if (!TransactionSynchronizationManager.isSynchronizationActive()) {
// Indicate that we can't obtain a transactional EntityManager.
return null;
}
// Create a new EntityManager for use within the current transaction.
logger.debug("Opening JPA EntityManager");
EntityManager em =
(!CollectionUtils.isEmpty(properties) ? emf.createEntityManager(properties) : emf.createEntityManager());
if (TransactionSynchronizationManager.isSynchronizationActive()) {
logger.debug("Registering transaction synchronization for JPA EntityManager");
// Use same EntityManager for further JPA actions within the transaction.
// Thread object will get removed by synchronization at transaction completion.
emHolder = new EntityManagerHolder(em);
EntityManager em = null;
if (!synchronizedWithTransaction && createEntityManagerWithSynchronizationTypeMethod != null) {
try {
em = (EntityManager) ReflectionUtils.invokeMethod(createEntityManagerWithSynchronizationTypeMethod,
emf, synchronizationTypeUnsynchronized, properties);
}
catch (AbstractMethodError err) {
// JPA 2.1 API available but method not actually implemented in persistence provider:
// falling back to regular createEntityManager method.
}
}
if (em == null) {
em = (!CollectionUtils.isEmpty(properties) ? emf.createEntityManager(properties) : emf.createEntityManager());
}
// Use same EntityManager for further JPA actions within the transaction.
// Thread object will get removed by synchronization at transaction completion.
logger.debug("Registering transaction synchronization for JPA EntityManager");
emHolder = new EntityManagerHolder(em);
if (synchronizedWithTransaction) {
Object transactionData = prepareTransaction(em, emf);
TransactionSynchronizationManager.registerSynchronization(
new EntityManagerSynchronization(emHolder, emf, transactionData, true));
new TransactionalEntityManagerSynchronization(emHolder, emf, transactionData, true));
emHolder.setSynchronizedWithTransaction(true);
TransactionSynchronizationManager.bindResource(emf, emHolder);
}
else {
// unsynchronized - just scope it for the transaction, as demanded by the JPA 2.1 spec
TransactionSynchronizationManager.registerSynchronization(
new TransactionScopedEntityManagerSynchronization(emHolder, emf));
}
TransactionSynchronizationManager.bindResource(emf, emHolder);
return em;
}
......@@ -356,7 +438,7 @@ public abstract class EntityManagerFactoryUtils {
* (e.g. when participating in a JtaTransactionManager transaction).
* @see org.springframework.transaction.jta.JtaTransactionManager
*/
private static class EntityManagerSynchronization
private static class TransactionalEntityManagerSynchronization
extends ResourceHolderSynchronization<EntityManagerHolder, EntityManagerFactory>
implements Ordered {
......@@ -366,7 +448,7 @@ public abstract class EntityManagerFactoryUtils {
private final boolean newEntityManager;
public EntityManagerSynchronization(
public TransactionalEntityManagerSynchronization(
EntityManagerHolder emHolder, EntityManagerFactory emf, Object txData, boolean newEm) {
super(emHolder, emf);
this.transactionData = txData;
......@@ -381,8 +463,17 @@ public abstract class EntityManagerFactoryUtils {
@Override
protected void flushResource(EntityManagerHolder resourceHolder) {
EntityManager em = resourceHolder.getEntityManager();
if (em instanceof EntityManagerProxy) {
EntityManager target = ((EntityManagerProxy) em).getTargetEntityManager();
if (TransactionSynchronizationManager.hasResource(target)) {
// ExtendedEntityManagerSynchronization active after joinTransaction() call:
// flush synchronization already registered.
return;
}
}
try {
resourceHolder.getEntityManager().flush();
em.flush();
}
catch (RuntimeException ex) {
if (this.jpaDialect != null) {
......@@ -415,4 +506,26 @@ public abstract class EntityManagerFactoryUtils {
}
}
/**
* Minimal callback that just closes the EntityManager at the end of the transaction.
*/
private static class TransactionScopedEntityManagerSynchronization
extends ResourceHolderSynchronization<EntityManagerHolder, EntityManagerFactory>
implements Ordered {
public TransactionScopedEntityManagerSynchronization(EntityManagerHolder emHolder, EntityManagerFactory emf) {
super(emHolder, emf);
}
public int getOrder() {
return ENTITY_MANAGER_SYNCHRONIZATION_ORDER + 1;
}
@Override
protected void releaseResource(EntityManagerHolder resourceHolder, EntityManagerFactory resourceKey) {
closeEntityManager(resourceHolder.getEntityManager());
}
}
}
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
......@@ -71,7 +71,7 @@ public abstract class ExtendedEntityManagerCreator {
public static EntityManager createApplicationManagedEntityManager(
EntityManager rawEntityManager, EntityManagerPlusOperations plusOperations) {
return createProxy(rawEntityManager, null, null, plusOperations, null, null, false);
return createProxy(rawEntityManager, null, null, plusOperations, null, null, false, false);
}
/**
......@@ -91,7 +91,7 @@ public abstract class ExtendedEntityManagerCreator {
EntityManager rawEntityManager, EntityManagerPlusOperations plusOperations,
PersistenceExceptionTranslator exceptionTranslator) {
return createProxy(rawEntityManager, null, null, plusOperations, exceptionTranslator, null, false);
return createProxy(rawEntityManager, null, null, plusOperations, exceptionTranslator, null, false, false);
}
/**
......@@ -107,13 +107,31 @@ public abstract class ExtendedEntityManagerCreator {
public static EntityManager createApplicationManagedEntityManager(
EntityManager rawEntityManager, EntityManagerFactoryInfo emfInfo) {
return createProxy(rawEntityManager, emfInfo, false);
return createProxy(rawEntityManager, emfInfo, false, false);
}
/**
* Create an EntityManager that can join transactions with the
* {@code joinTransaction()} method, but is not automatically
* managed by the container.
* @param rawEntityManager raw EntityManager
* @param emfInfo the EntityManagerFactoryInfo to obtain the
* EntityManagerPlusOperations and PersistenceUnitInfo from
* @param synchronizedWithTransaction whether to automatically join ongoing
* transactions (according to the JPA 2.1 SynchronizationType rules)
* @return an application-managed EntityManager that can join transactions
* but does not participate in them automatically
*/
public static EntityManager createApplicationManagedEntityManager(
EntityManager rawEntityManager, EntityManagerFactoryInfo emfInfo, boolean synchronizedWithTransaction) {
return createProxy(rawEntityManager, emfInfo, false, synchronizedWithTransaction);
}
/**
* Create an EntityManager that automatically joins transactions on each
* operation in a transaction.
* Create an EntityManager whose lifecycle is managed by the container and which
* automatically joins a transaction when being invoked within its scope.
* @param rawEntityManager raw EntityManager
* @param plusOperations an implementation of the EntityManagerPlusOperations
* interface, if those operations should be exposed (may be {@code null})
......@@ -123,12 +141,12 @@ public abstract class ExtendedEntityManagerCreator {
public static EntityManager createContainerManagedEntityManager(
EntityManager rawEntityManager, EntityManagerPlusOperations plusOperations) {
return createProxy(rawEntityManager, null, null, plusOperations, null, null, true);
return createProxy(rawEntityManager, null, null, plusOperations, null, null, true, true);
}
/**
* Create an EntityManager that automatically joins transactions on each
* operation in a transaction.
* Create an EntityManager whose lifecycle is managed by the container and which
* automatically joins a transaction when being invoked within its scope.
* @param rawEntityManager raw EntityManager
* @param plusOperations an implementation of the EntityManagerPlusOperations
* interface, if those operations should be exposed (may be {@code null})
......@@ -142,12 +160,12 @@ public abstract class ExtendedEntityManagerCreator {
EntityManager rawEntityManager, EntityManagerPlusOperations plusOperations,
PersistenceExceptionTranslator exceptionTranslator) {
return createProxy(rawEntityManager, null, null, plusOperations, exceptionTranslator, null, true);
return createProxy(rawEntityManager, null, null, plusOperations, exceptionTranslator, null, true, true);
}
/**
* Create an EntityManager that automatically joins transactions on each
* operation in a transaction.
* Create an EntityManager whose lifecycle is managed by the container and which
* automatically joins a transaction when being invoked within its scope.
* @param rawEntityManager raw EntityManager
* @param emfInfo the EntityManagerFactoryInfo to obtain the
* EntityManagerPlusOperations and PersistenceUnitInfo from
......@@ -157,13 +175,12 @@ public abstract class ExtendedEntityManagerCreator {
public static EntityManager createContainerManagedEntityManager(
EntityManager rawEntityManager, EntityManagerFactoryInfo emfInfo) {
return createProxy(rawEntityManager, emfInfo, true);
return createProxy(rawEntityManager, emfInfo, true, true);
}
/**
* Create an EntityManager that automatically joins transactions on each
* operation in a transaction.
* Create an EntityManager whose lifecycle is managed by the container and which
* automatically joins a transaction when being invoked within its scope.
* @param emf the EntityManagerFactory to create the EntityManager with.
* If this implements the EntityManagerFactoryInfo interface, appropriate handling
* of the native EntityManagerFactory and available EntityManagerPlusOperations
......@@ -173,12 +190,12 @@ public abstract class ExtendedEntityManagerCreator {
* @see javax.persistence.EntityManagerFactory#createEntityManager()
*/
public static EntityManager createContainerManagedEntityManager(EntityManagerFactory emf) {
return createContainerManagedEntityManager(emf, null);
return createContainerManagedEntityManager(emf, null, true);
}
/**
* Create an EntityManager that automatically joins transactions on each
* operation in a transaction.
* Create an EntityManager whose lifecycle is managed by the container and which
* automatically joins a transaction when being invoked within its scope.
* @param emf the EntityManagerFactory to create the EntityManager with.
* If this implements the EntityManagerFactoryInfo interface, appropriate handling
* of the native EntityManagerFactory and available EntityManagerPlusOperations
......@@ -190,18 +207,39 @@ public abstract class ExtendedEntityManagerCreator {
* @see javax.persistence.EntityManagerFactory#createEntityManager(java.util.Map)
*/
public static EntityManager createContainerManagedEntityManager(EntityManagerFactory emf, Map properties) {
return createContainerManagedEntityManager(emf, properties, true);
}
/**
* Create an EntityManager whose lifecycle is managed by the container and which
* may automatically join a transaction when being invoked within its scope.
* @param emf the EntityManagerFactory to create the EntityManager with.
* If this implements the EntityManagerFactoryInfo interface, appropriate handling
* of the native EntityManagerFactory and available EntityManagerPlusOperations
* will automatically apply.
* @param properties the properties to be passed into the {@code createEntityManager}
* call (may be {@code null})
* @param synchronizedWithTransaction whether to automatically join ongoing
* transactions (according to the JPA 2.1 SynchronizationType rules)
* @return a container-managed EntityManager that expects container-driven lifecycle
* management but may opt out of automatic transaction synchronization
* @see javax.persistence.EntityManagerFactory#createEntityManager(java.util.Map)
*/
public static EntityManager createContainerManagedEntityManager(
EntityManagerFactory emf, Map properties, boolean synchronizedWithTransaction) {
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());
return createProxy(rawEntityManager, emfInfo, true);
return createProxy(rawEntityManager, emfInfo, true, synchronizedWithTransaction);
}
else {
EntityManager rawEntityManager = (!CollectionUtils.isEmpty(properties) ?
emf.createEntityManager(properties) : emf.createEntityManager());
return createProxy(rawEntityManager, null, null, null, null, null, true);
return createProxy(rawEntityManager, null, null, null, null, null, true, synchronizedWithTransaction);
}
}
......@@ -213,10 +251,12 @@ public abstract class ExtendedEntityManagerCreator {
* EntityManagerPlusOperations and PersistenceUnitInfo from
* @param containerManaged whether to follow container-managed EntityManager
* or application-managed EntityManager semantics
* @param synchronizedWithTransaction whether to automatically join ongoing
* transactions (according to the JPA 2.1 SynchronizationType rules)
* @return the EntityManager proxy
*/
private static EntityManager createProxy(
EntityManager rawEntityManager, EntityManagerFactoryInfo emfInfo, boolean containerManaged) {
private static EntityManager createProxy(EntityManager rawEntityManager,
EntityManagerFactoryInfo emfInfo, boolean containerManaged, boolean synchronizedWithTransaction) {
Assert.notNull(emfInfo, "EntityManagerFactoryInfo must not be null");
JpaDialect jpaDialect = emfInfo.getJpaDialect();
......@@ -227,7 +267,7 @@ public abstract class ExtendedEntityManagerCreator {
PersistenceUnitInfo pui = emfInfo.getPersistenceUnitInfo();
Boolean jta = (pui != null ? pui.getTransactionType() == PersistenceUnitTransactionType.JTA : null);
return createProxy(rawEntityManager, emfInfo.getEntityManagerInterface(),
emfInfo.getBeanClassLoader(), plusOperations, jpaDialect, jta, containerManaged);
emfInfo.getBeanClassLoader(), plusOperations, jpaDialect, jta, containerManaged, synchronizedWithTransaction);
}
/**
......@@ -242,12 +282,14 @@ public abstract class ExtendedEntityManagerCreator {
* (or {@code null} if not known in advance)
* @param containerManaged whether to follow container-managed EntityManager
* or application-managed EntityManager semantics
* @param synchronizedWithTransaction whether to automatically join ongoing
* transactions (according to the JPA 2.1 SynchronizationType rules)
* @return the EntityManager proxy
*/
private static EntityManager createProxy(
EntityManager rawEm, Class<? extends EntityManager> emIfc, ClassLoader cl,
EntityManagerPlusOperations plusOperations, PersistenceExceptionTranslator exceptionTranslator,
Boolean jta, boolean containerManaged) {
Boolean jta, boolean containerManaged, boolean synchronizedWithTransaction) {
Assert.notNull(rawEm, "EntityManager must not be null");
Set<Class> ifcs = new LinkedHashSet<Class>();
......@@ -265,7 +307,7 @@ public abstract class ExtendedEntityManagerCreator {
(cl != null ? cl : ExtendedEntityManagerCreator.class.getClassLoader()),
ifcs.toArray(new Class[ifcs.size()]),
new ExtendedEntityManagerInvocationHandler(
rawEm, plusOperations, exceptionTranslator, jta, containerManaged));
rawEm, plusOperations, exceptionTranslator, jta, containerManaged, synchronizedWithTransaction));
}
......@@ -283,19 +325,23 @@ public abstract class ExtendedEntityManagerCreator {
private final PersistenceExceptionTranslator exceptionTranslator;
private final boolean jta;
private final boolean containerManaged;
private boolean jta;
private final boolean synchronizedWithTransaction;
private ExtendedEntityManagerInvocationHandler(
EntityManager target, EntityManagerPlusOperations plusOperations,
PersistenceExceptionTranslator exceptionTranslator, Boolean jta, boolean containerManaged) {
PersistenceExceptionTranslator exceptionTranslator, Boolean jta,
boolean containerManaged, boolean synchronizedWithTransaction) {
this.target = target;
this.plusOperations = plusOperations;
this.exceptionTranslator = exceptionTranslator;
this.jta = (jta != null ? jta : isJtaEntityManager());
this.containerManaged = containerManaged;
this.synchronizedWithTransaction = synchronizedWithTransaction;
}
private boolean isJtaEntityManager() {
......@@ -340,20 +386,33 @@ public abstract class ExtendedEntityManagerCreator {
if (this.containerManaged) {
throw new IllegalStateException("Invalid usage: Cannot close a container-managed EntityManager");
}
ExtendedEntityManagerSynchronization synch = (ExtendedEntityManagerSynchronization)
TransactionSynchronizationManager.getResource(this.target);
if (synch != null) {
// Local transaction joined - don't actually call close() before transaction completion
synch.closeOnCompletion = true;
return null;
}
}
else if (method.getName().equals("getTransaction")) {
if (this.containerManaged) {
if (this.synchronizedWithTransaction) {
throw new IllegalStateException(
"Cannot execute getTransaction() on a container-managed EntityManager");
"Cannot obtain local EntityTransaction from a transaction-synchronized EntityManager");
}
}
else if (method.getName().equals("joinTransaction")) {
doJoinTransaction(true);
return null;
}
else if (method.getName().equals("isJoinedToTransaction")) {
// Handle JPA 2.1 isJoinedToTransaction method for the non-JTA case.
if (!this.jta) {
return TransactionSynchronizationManager.hasResource(this.target);
}
}
// Do automatic joining if required.
if (this.containerManaged && method.getDeclaringClass().isInterface()) {
// Do automatic joining if required. Excludes toString, equals, hashCode calls.
if (this.synchronizedWithTransaction && method.getDeclaringClass().isInterface()) {
doJoinTransaction(false);
}
......@@ -416,18 +475,16 @@ public abstract class ExtendedEntityManagerCreator {
*/
private void enlistInCurrentTransaction() {
// Resource local transaction, need to acquire the EntityTransaction,
// start a transaction now and enlist a synchronization for
// commit or rollback later.
// start a transaction now and enlist a synchronization for commit or rollback later.
EntityTransaction et = this.target.getTransaction();
et.begin();
if (logger.isDebugEnabled()) {
logger.debug("Starting resource local transaction on application-managed " +
logger.debug("Starting resource-local transaction on application-managed " +
"EntityManager [" + this.target + "]");
}
ExtendedEntityManagerSynchronization extendedEntityManagerSynchronization =
new ExtendedEntityManagerSynchronization(this.target, this.exceptionTranslator);
TransactionSynchronizationManager.bindResource(this.target,
extendedEntityManagerSynchronization);
TransactionSynchronizationManager.bindResource(this.target, extendedEntityManagerSynchronization);
TransactionSynchronizationManager.registerSynchronization(extendedEntityManagerSynchronization);
}
}
......@@ -445,6 +502,8 @@ public abstract class ExtendedEntityManagerCreator {
private final PersistenceExceptionTranslator exceptionTranslator;
public volatile boolean closeOnCompletion = false;
public ExtendedEntityManagerSynchronization(
EntityManager em, PersistenceExceptionTranslator exceptionTranslator) {
super(new EntityManagerHolder(em), em);
......@@ -453,7 +512,7 @@ public abstract class ExtendedEntityManagerCreator {
}
public int getOrder() {
return EntityManagerFactoryUtils.ENTITY_MANAGER_SYNCHRONIZATION_ORDER + 1;
return EntityManagerFactoryUtils.ENTITY_MANAGER_SYNCHRONIZATION_ORDER - 1;
}
@Override
......@@ -485,14 +544,21 @@ public abstract class ExtendedEntityManagerCreator {
@Override
public void afterCompletion(int status) {
super.afterCompletion(status);
if (status != STATUS_COMMITTED) {
// Haven't had an afterCommit call: trigger a rollback.
try {
this.entityManager.getTransaction().rollback();
try {
super.afterCompletion(status);
if (status != STATUS_COMMITTED) {
// Haven't had an afterCommit call: trigger a rollback.
try {
this.entityManager.getTransaction().rollback();
}
catch (RuntimeException ex) {
throw convertException(ex);
}
}
catch (RuntimeException ex) {
throw convertException(ex);
}
finally {
if (this.closeOnCompletion) {
EntityManagerFactoryUtils.closeEntityManager(this.entityManager);
}
}
}
......
......@@ -17,7 +17,6 @@
package org.springframework.orm.jpa;
import java.sql.SQLException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceException;
......@@ -135,13 +134,15 @@ public interface JpaDialect extends PersistenceExceptionTranslator {
/**
* Prepare a JPA transaction, applying the specified semantics. Called by
* EntityManagerFactoryUtils when enlisting an EntityManager in a JTA transaction.
* EntityManagerFactoryUtils when enlisting an EntityManager in a JTA transaction
* or a locally joined transaction (e.g. after upgrading an unsynchronized
* EntityManager to a synchronized one).
* <p>An implementation can apply the read-only flag as flush mode. In that case,
* a transaction data object can be returned that holds the previous flush mode
* (and possibly other data), to be reset in {@code cleanupTransaction}.
* <p>Implementations can also use the Spring transaction name, as exposed by the
* passed-in TransactionDefinition, to optimize for specific data access use cases
* (effectively using the current transaction name as use case identifier).
* <p>Implementations can also use the Spring transaction name to optimize for
* specific data access use cases (effectively using the current transaction
* name as use case identifier).
* @param entityManager the EntityManager to begin a JPA transaction on
* @param readOnly whether the transaction is supposed to be read-only
* @param name the name of the transaction (if any)
......
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
......@@ -566,9 +566,11 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
protected void doCleanupAfterCompletion(Object transaction) {
JpaTransactionObject txObject = (JpaTransactionObject) transaction;
// Remove the entity manager holder from the thread.
// Remove the entity manager holder from the thread, if still there.
// (Could have been removed by EntityManagerFactoryUtils in order
// to replace it with an unsynchronized EntityManager).
if (txObject.isNewEntityManagerHolder()) {
TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
TransactionSynchronizationManager.unbindResourceIfPossible(getEntityManagerFactory());
}
txObject.getEntityManagerHolder().clear();
......
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
......@@ -53,7 +53,8 @@ import org.springframework.util.CollectionUtils;
public abstract class SharedEntityManagerCreator {
/**
* Create a transactional EntityManager proxy for the given EntityManagerFactory.
* Create a transactional EntityManager proxy for the given EntityManagerFactory,
* automatically joining ongoing transactions.
* @param emf the EntityManagerFactory to delegate to.
* If this implements the {@link EntityManagerFactoryInfo} interface,
* appropriate handling of the native EntityManagerFactory and available
......@@ -61,7 +62,7 @@ public abstract class SharedEntityManagerCreator {
* @return a shareable transaction EntityManager proxy
*/
public static EntityManager createSharedEntityManager(EntityManagerFactory emf) {
return createSharedEntityManager(emf, null);
return createSharedEntityManager(emf, null, true);
}
/**
......@@ -75,6 +76,24 @@ public abstract class SharedEntityManagerCreator {
* @return a shareable transaction EntityManager proxy
*/
public static EntityManager createSharedEntityManager(EntityManagerFactory emf, Map properties) {
return createSharedEntityManager(emf, properties, true);
}
/**
* Create a transactional EntityManager proxy for the given EntityManagerFactory.
* @param emf the EntityManagerFactory to delegate to.
* If this implements the {@link EntityManagerFactoryInfo} interface,
* appropriate handling of the native EntityManagerFactory and available
* {@link EntityManagerPlusOperations} will automatically apply.
* @param properties the properties to be passed into the
* {@code createEntityManager} call (may be {@code null})
* @param synchronizedWithTransaction whether to automatically join ongoing
* transactions (according to the JPA 2.1 SynchronizationType rules)
* @return a shareable transaction EntityManager proxy
*/
public static EntityManager createSharedEntityManager(
EntityManagerFactory emf, Map properties, boolean synchronizedWithTransaction) {
Class[] emIfcs;
if (emf instanceof EntityManagerFactoryInfo) {
EntityManagerFactoryInfo emfInfo = (EntityManagerFactoryInfo) emf;
......@@ -93,7 +112,7 @@ public abstract class SharedEntityManagerCreator {
else {
emIfcs = new Class[] {EntityManager.class};
}
return createSharedEntityManager(emf, properties, emIfcs);
return createSharedEntityManager(emf, properties, synchronizedWithTransaction, emIfcs);
}
/**
......@@ -108,6 +127,23 @@ public abstract class SharedEntityManagerCreator {
public static EntityManager createSharedEntityManager(
EntityManagerFactory emf, Map properties, Class... entityManagerInterfaces) {
return createSharedEntityManager(emf, properties, true, entityManagerInterfaces);
}
/**
* Create a transactional EntityManager proxy for the given EntityManagerFactory.
* @param emf EntityManagerFactory to obtain EntityManagers from as needed
* @param properties the properties to be passed into the
* {@code createEntityManager} call (may be {@code null})
* @param synchronizedWithTransaction whether to automatically join ongoing
* transactions (according to the JPA 2.1 SynchronizationType rules)
* @param entityManagerInterfaces the interfaces to be implemented by the
* EntityManager. Allows the addition or specification of proprietary interfaces.
* @return a shareable transactional EntityManager proxy
*/
public static EntityManager createSharedEntityManager(EntityManagerFactory emf, Map properties,
boolean synchronizedWithTransaction, Class... entityManagerInterfaces) {
ClassLoader cl = null;
if (emf instanceof EntityManagerFactoryInfo) {
cl = ((EntityManagerFactoryInfo) emf).getBeanClassLoader();
......@@ -117,7 +153,7 @@ public abstract class SharedEntityManagerCreator {
ifcs[entityManagerInterfaces.length] = EntityManagerProxy.class;
return (EntityManager) Proxy.newProxyInstance(
(cl != null ? cl : SharedEntityManagerCreator.class.getClassLoader()),
ifcs, new SharedEntityManagerInvocationHandler(emf, properties));
ifcs, new SharedEntityManagerInvocationHandler(emf, properties, synchronizedWithTransaction));
}
......@@ -135,11 +171,15 @@ public abstract class SharedEntityManagerCreator {
private final Map properties;
private final boolean synchronizedWithTransaction;
private transient volatile ClassLoader proxyClassLoader;
public SharedEntityManagerInvocationHandler(EntityManagerFactory target, Map properties) {
public SharedEntityManagerInvocationHandler(
EntityManagerFactory target, Map properties, boolean synchronizedWithTransaction) {
this.targetFactory = target;
this.properties = properties;
this.synchronizedWithTransaction = synchronizedWithTransaction;
initProxyClassLoader();
}
......@@ -200,16 +240,11 @@ public abstract class SharedEntityManagerCreator {
"Not allowed to create transaction on shared EntityManager - " +
"use Spring transactions or EJB CMT instead");
}
else if (method.getName().equals("joinTransaction")) {
throw new IllegalStateException(
"Not allowed to join transaction on shared EntityManager - " +
"use Spring transactions or EJB CMT instead");
}
// Determine current EntityManager: either the transactional one
// managed by the factory or a temporary one for the given invocation.
EntityManager target =
EntityManagerFactoryUtils.doGetTransactionalEntityManager(this.targetFactory, this.properties);
EntityManager target = EntityManagerFactoryUtils.doGetTransactionalEntityManager(
this.targetFactory, this.properties, this.synchronizedWithTransaction);
if (method.getName().equals("getTargetEntityManager")) {
// Handle EntityManagerProxy interface.
......
......@@ -61,6 +61,7 @@ import org.springframework.orm.jpa.ExtendedEntityManagerCreator;
import org.springframework.orm.jpa.SharedEntityManagerCreator;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
/**
* BeanPostProcessor that processes {@link javax.persistence.PersistenceUnit}
......@@ -165,6 +166,11 @@ public class PersistenceAnnotationBeanPostProcessor
implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor,
MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware, Serializable {
/* Check JPA 2.1 PersistenceContext.synchronizationType attribute */
private static final Method synchronizationTypeAttribute =
ClassUtils.getMethodIfAvailable(PersistenceContext.class, "synchronizationType");
private Object jndiEnvironment;
private boolean resourceRef = true;
......@@ -589,6 +595,8 @@ public class PersistenceAnnotationBeanPostProcessor
private PersistenceContextType type;
private boolean synchronizedWithTransaction = false;
private Properties properties;
public PersistenceElement(Member member, PropertyDescriptor pd) {
......@@ -612,6 +620,8 @@ public class PersistenceAnnotationBeanPostProcessor
}
this.unitName = pc.unitName();
this.type = pc.type();
this.synchronizedWithTransaction = (synchronizationTypeAttribute == null ||
"SYNCHRONIZED".equals(ReflectionUtils.invokeMethod(synchronizationTypeAttribute, pc).toString()));
this.properties = properties;
}
else {
......@@ -664,11 +674,13 @@ public class PersistenceAnnotationBeanPostProcessor
((EntityManagerFactoryInfo) emf).getEntityManagerInterface() != null) {
// Create EntityManager based on the info's vendor-specific type
// (which might be more specific than the field's type).
em = SharedEntityManagerCreator.createSharedEntityManager(emf, this.properties);
em = SharedEntityManagerCreator.createSharedEntityManager(
emf, this.properties, this.synchronizedWithTransaction);
}
else {
// Create EntityManager based on the field's type.
em = SharedEntityManagerCreator.createSharedEntityManager(emf, this.properties, getResourceType());
em = SharedEntityManagerCreator.createSharedEntityManager(
emf, this.properties, this.synchronizedWithTransaction, getResourceType());
}
}
return em;
......@@ -686,7 +698,8 @@ public class PersistenceAnnotationBeanPostProcessor
emf = findEntityManagerFactory(this.unitName, requestingBeanName);
}
// Inject a container-managed extended EntityManager.
em = ExtendedEntityManagerCreator.createContainerManagedEntityManager(emf, this.properties);
em = ExtendedEntityManagerCreator.createContainerManagedEntityManager(
emf, this.properties, this.synchronizedWithTransaction);
}
if (em instanceof EntityManagerProxy &&
beanFactory != null && !beanFactory.isPrototype(requestingBeanName)) {
......
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
......@@ -55,6 +55,8 @@ public class SharedEntityManagerBean extends EntityManagerFactoryAccessor
private Class<? extends EntityManager> entityManagerInterface;
private boolean synchronizedWithTransaction = true;
private EntityManager shared;
......@@ -72,6 +74,14 @@ public class SharedEntityManagerBean extends EntityManagerFactoryAccessor
this.entityManagerInterface = entityManagerInterface;
}
/**
* Set whether to automatically join ongoing transactions (according
* to the JPA 2.1 SynchronizationType rules). Default is "true".
*/
public void setSynchronizedWithTransaction(boolean synchronizedWithTransaction) {
this.synchronizedWithTransaction = synchronizedWithTransaction;
}
public final void afterPropertiesSet() {
EntityManagerFactory emf = getEntityManagerFactory();
......@@ -101,7 +111,8 @@ public class SharedEntityManagerBean extends EntityManagerFactoryAccessor
}
ifcs = new Class[] {this.entityManagerInterface};
}
this.shared = SharedEntityManagerCreator.createSharedEntityManager(emf, getJpaPropertyMap(), ifcs);
this.shared = SharedEntityManagerCreator.createSharedEntityManager(
emf, getJpaPropertyMap(), this.synchronizedWithTransaction, ifcs);
}
......
......@@ -156,15 +156,6 @@ public abstract class AbstractContainerEntityManagerFactoryIntegrationTests
}
}
public void testSharedEntityManagerProxyRejectsProgrammaticTxJoining() {
try {
sharedEntityManager.joinTransaction();
fail("Should not be able to join transactions with container managed EntityManager");
}
catch (IllegalStateException ex) {
}
}
// public void testAspectJInjectionOfConfigurableEntity() {
// Person p = new Person();
// System.err.println(p);
......
......@@ -323,6 +323,16 @@ public class LocalContainerEntityManagerFactoryBeanTests extends AbstractEntityM
public ProviderUtil getProviderUtil() {
throw new UnsupportedOperationException();
}
@Override
public void generateSchema(PersistenceUnitInfo persistenceUnitInfo, Map map) {
throw new UnsupportedOperationException();
}
@Override
public boolean generateSchema(String persistenceUnitName, Map map) {
throw new UnsupportedOperationException();
}
}
......
......@@ -100,6 +100,16 @@ public class LocalEntityManagerFactoryBeanTests extends AbstractEntityManagerFac
public ProviderUtil getProviderUtil() {
throw new UnsupportedOperationException();
}
@Override
public void generateSchema(PersistenceUnitInfo persistenceUnitInfo, Map map) {
throw new UnsupportedOperationException();
}
@Override
public boolean generateSchema(String persistenceUnitName, Map map) {
throw new UnsupportedOperationException();
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册