From a8577da30cec91272ef7f367ab5f393d232a1ee1 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 17 Mar 2014 13:53:38 +0100 Subject: [PATCH] DeferredQueryInvocationHandler explicitly closes its EntityManager on garbage collection Includes javadoc revision covering all supported EntityManager types as of JPA 2.1. Issue: SPR-11451 --- .../orm/jpa/ExtendedEntityManagerCreator.java | 54 +++++++++++-------- .../orm/jpa/SharedEntityManagerCreator.java | 43 ++++++++++----- 2 files changed, 61 insertions(+), 36 deletions(-) diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/ExtendedEntityManagerCreator.java b/spring-orm/src/main/java/org/springframework/orm/jpa/ExtendedEntityManagerCreator.java index 5176794667..e70845a20b 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/ExtendedEntityManagerCreator.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/ExtendedEntityManagerCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -44,24 +44,37 @@ import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; /** - * Factory for dynamic EntityManager proxies that follow the JPA spec's - * semantics for "extended" EntityManagers. + * Delegate for creating a variety of {@link javax.persistence.EntityManager} + * proxies that follow the JPA spec's semantics for "extended" EntityManagers. * - *

Supports explicit joining of a transaction through the - * {@code joinTransaction()} method ("application-managed extended - * EntityManager") as well as automatic joining on each operation - * ("container-managed extended EntityManager"). + *

Supports several different variants of "extended" EntityManagers: + * in particular, an "application-managed extended EntityManager", as defined + * by {@link javax.persistence.EntityManagerFactory#createEntityManager()}, + * as well as a "container-managed extended EntityManager", as defined by + * {@link javax.persistence.PersistenceContextType#EXTENDED}. + * + *

The original difference between "application-managed" and "container-managed" + * was the need for explicit joining of an externally managed transaction through + * the {@link EntityManager#joinTransaction()} method in the "application" case + * versus the automatic joining on each user-level EntityManager operation in the + * "container" case. As of JPA 2.1, both join modes are available with both kinds of + * EntityManagers, so the difference between "application-" and "container-managed" + * is now primarily in the join mode default and in the restricted lifecycle of a + * container-managed EntityManager (i.e. tied to the object that it is injected into). * - * @author Rod Johnson * @author Juergen Hoeller + * @author Rod Johnson * @since 2.0 + * @see javax.persistence.EntityManagerFactory#createEntityManager() + * @see javax.persistence.PersistenceContextType#EXTENDED + * @see javax.persistence.EntityManager#joinTransaction() + * @see SharedEntityManagerCreator */ public abstract class ExtendedEntityManagerCreator { /** - * Create an EntityManager that can join transactions with the {@code joinTransaction()} - * method, but is not automatically managed by the container. - * @param rawEntityManager raw EntityManager + * Create an application-managed extended EntityManager proxy. + * @param rawEntityManager the raw EntityManager to decorate * @param emfInfo the EntityManagerFactoryInfo to obtain the JpaDialect * and PersistenceUnitInfo from * @return an application-managed EntityManager that can join transactions @@ -74,9 +87,8 @@ public abstract class ExtendedEntityManagerCreator { } /** - * Create an EntityManager that can join transactions with the {@code joinTransaction()} - * method, but is not automatically managed by the container. - * @param rawEntityManager raw EntityManager + * Create an application-managed extended EntityManager proxy. + * @param rawEntityManager the raw EntityManager to decorate * @param emfInfo the EntityManagerFactoryInfo to obtain the JpaDialect * and PersistenceUnitInfo from * @param synchronizedWithTransaction whether to automatically join ongoing @@ -91,9 +103,8 @@ public abstract class ExtendedEntityManagerCreator { } /** - * 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 + * Create a container-managed extended EntityManager proxy. + * @param rawEntityManager the raw EntityManager to decorate * @param emfInfo the EntityManagerFactoryInfo to obtain the JpaDialect * and PersistenceUnitInfo from * @return a container-managed EntityManager that will automatically participate @@ -106,8 +117,7 @@ public abstract class ExtendedEntityManagerCreator { } /** - * Create an EntityManager whose lifecycle is managed by the container and which - * automatically joins a transaction when being invoked within its scope. + * Create a container-managed extended EntityManager proxy. * @param emf the EntityManagerFactory to create the EntityManager with. * If this implements the EntityManagerFactoryInfo interface, the corresponding * JpaDialect and PersistenceUnitInfo will be detected accordingly. @@ -120,8 +130,7 @@ public abstract class ExtendedEntityManagerCreator { } /** - * Create an EntityManager whose lifecycle is managed by the container and which - * automatically joins a transaction when being invoked within its scope. + * Create a container-managed extended EntityManager proxy. * @param emf the EntityManagerFactory to create the EntityManager with. * If this implements the EntityManagerFactoryInfo interface, the corresponding * JpaDialect and PersistenceUnitInfo will be detected accordingly. @@ -136,8 +145,7 @@ public abstract class ExtendedEntityManagerCreator { } /** - * Create an EntityManager whose lifecycle is managed by the container and which - * may automatically join a transaction when being invoked within its scope. + * Create a container-managed extended EntityManager proxy. * @param emf the EntityManagerFactory to create the EntityManager with. * If this implements the EntityManagerFactoryInfo interface, the corresponding * JpaDialect and PersistenceUnitInfo will be detected accordingly. diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java b/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java index 966cd50851..88352be494 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -24,32 +24,38 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Map; - import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Query; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; /** - * Factory for a shareable JPA {@link javax.persistence.EntityManager} - * for a given {@link javax.persistence.EntityManagerFactory}. + * Delegate for creating a shareable JPA {@link javax.persistence.EntityManager} + * reference for a given {@link javax.persistence.EntityManagerFactory}. * - *

The shareable EntityManager will behave just like an EntityManager fetched - * from an application server's JNDI environment, as defined by the JPA - * specification. It will delegate all calls to the current transactional - * EntityManager, if any; otherwise it will fall back to a newly created - * EntityManager per operation. + *

A shared EntityManager will behave just like an EntityManager fetched from + * an application server's JNDI environment, as defined by the JPA specification. + * It will delegate all calls to the current transactional EntityManager, if any; + * otherwise it will fall back to a newly created EntityManager per operation. + * + *

For a behavioral definition of such a shared transactional EntityManager, + * see {@link javax.persistence.PersistenceContextType#TRANSACTION} and its + * discussion in the JPA spec document. This is also the default being used + * for the annotation-based {@link javax.persistence.PersistenceContext#type()}. * * @author Juergen Hoeller * @author Rod Johnson * @author Oliver Gierke * @since 2.0 - * @see org.springframework.orm.jpa.LocalEntityManagerFactoryBean + * @see javax.persistence.PersistenceContext + * @see javax.persistence.PersistenceContextType#TRANSACTION * @see org.springframework.orm.jpa.JpaTransactionManager + * @see ExtendedEntityManagerCreator */ public abstract class SharedEntityManagerCreator { @@ -57,8 +63,7 @@ public abstract class SharedEntityManagerCreator { /** - * Create a transactional EntityManager proxy for the given EntityManagerFactory, - * automatically joining ongoing transactions. + * Create a transactional EntityManager proxy for the given EntityManagerFactory. * @param emf the EntityManagerFactory to delegate to. * @return a shareable transaction EntityManager proxy */ @@ -296,7 +301,7 @@ public abstract class SharedEntityManagerCreator { private final Query target; - private final EntityManager em; + private EntityManager em; public DeferredQueryInvocationHandler(Query target, EntityManager em) { this.target = target; @@ -334,10 +339,22 @@ public abstract class SharedEntityManagerCreator { finally { if (method.getName().equals("getResultList") || method.getName().equals("getSingleResult") || method.getName().equals("executeUpdate")) { + // Actual execution of the query: close the EntityManager right + // afterwards, since that was the only reason we kept it open. EntityManagerFactoryUtils.closeEntityManager(this.em); + this.em = null; } } } + + @Override + protected void finalize() { + // Trigger explicit EntityManager.close() call on garbage collection, + // in particular for open/close statistics to be in sync. This is + // only relevant if the Query object has not been executed, e.g. + // when just used for the early validation of query definitions. + EntityManagerFactoryUtils.closeEntityManager(this.em); + } } }