dev@glassfish.java.net

Re: Not detached after use EntityManager's query in non-transaction mode

From: Marina Vatkina <Marina.Vatkina_at_Sun.COM>
Date: Wed, 05 Apr 2006 12:31:01 -0700

Ken,

This is a bug. You can argue that the provider should figure the
mode out (can it?) and call em.close itself, or the container needs
to handle that.

thanks,
-marina

Wonseok Kim wrote On 04/05/06 10:27,:
> Hi, Ken. Thanks for your prompt answer.
>
> I think it's strange that entities got from EM.find() are detached, but
> entities got from EM.createQuery("...").getSingleResult() are managed
> because these seems to have same meaning. Users can be confused as me.
>
> Person person = em.find(Person.class , 1); //detached
> Person person = em.createQuery("SELECT p FROM Person p WHERE
> p.id=1").getSingleResult(); //managed
>
> The spec is not clear about this because it says only about
> EntityManager, not Query.
> Was this discussed here or JSR 220?
>
> But I guess that the original intention is that all entities got from
> either em.find or query are remained detached in non transaction mode.
>
> Another thought is,
> If user creates Query objects and reuses them to retreive entities, the
> number of managed entities will increase continuously. When doing just
> queries in Servlet, SLSB, it could be this case.
> Because the result of query can be thousands of, or more entities, it is
> not good to hold managed entities in aspect of memory.
>
> Of couse user can call explicitly em.clear() after query execution to
> avoid this. But em is container-managed EntityManager, container can do
> this elegantly.
>
> If you have thought about it, plz let me know...
>
> On 4/5/06, *Kenneth Saks* < Kenneth.Saks_at_sun.com
> <mailto:Kenneth.Saks_at_sun.com>> wrote:
>
> Wonseok Kim wrote:
>
> The transaction-scoped EntityManager can be used in non-transaction
> mode.
> And the loaded entities are detached after method call.(Java
> Persistence API spec 5.6.1)
>
> So if I get an entity by query as follows, the entity should be
> detached.
> Hi Wonseok,
>
> Spec 5.6.1 says "If the EntityManager is invoked outside of a
> transaction, any
> entities loaded from the database will immediately become detached
> at the
> end of the method call". Your example is making that assumption
> for the
> Query object, not the EntityManager. This issue was discussed
> with the
> spec leads and the current implementation was validated as compliant.
>
> It's true that there are possible alternative implementations that
> involve varying
> amounts of proxying the Query object. That's something we're
> considering
> for a future release.
>
> --ken
>
>
>
> @PersistenceContext(type = TRANSACTION)
> private EntityManager em1;
>
> Person person = (Person)em.createQuery("SELECT p FROM Person p WHERE
> p.id=1").getSingleResult();
> // person should be detached entity
>
> boolean managed = em.contains(person); // should be false
>
> But the result is true(managed) in current build.
>
> Because com.sun.enterprise.util.EntityManagerWrapper createQuery
> methods are implemented in a wrong way like:
>
> public Query createQuery(String ejbqlString) {
> Query returnValue = null;
> try {
> returnValue = _getDelegate().createQuery(ejbqlString);
> } finally {
> if( delegateInfo.nonTxEntityManager ) {
> nonTxEntityManager.clear();
> }
> }
> return returnValue;
> }
>
> As you see, after making Query object, it is clearing underlying
> persistence context - meaningless!
> It should be cleared after query execution like Query.getSingleResult().
>
> This can be fixed by using Query wrapper in case of non-tx mode.
>
> The proposed fix is this: (I attached source files)
>
> Index: src/java/com/sun/enterprise/util/EntityManagerWrapper.java
> ===================================================================
> RCS file: /cvs/glassfish/appserv-core/src/java/com/sun/enterprise/util/EntityManagerWrapper.java,v
>
>
>
> retrieving revision 1.9
> *diff -r1.9 EntityManagerWrapper.java*
> *260,265c260,262*
> < try {
> < returnValue = _getDelegate().createQuery(ejbqlString);
> < } finally {
> < if( delegateInfo.nonTxEntityManager ) {
> < nonTxEntityManager.clear();
> < }
> *---*
>> returnValue = _getDelegate().createQuery(ejbqlString);
>> if( delegateInfo.nonTxEntityManager ) {
>> returnValue = new QueryWrapper(returnValue, true, nonTxEntityManager);
> *272,277c269,271*
> < try {
> < returnValue = _getDelegate().createNamedQuery(name);
> < } finally {
> < if( delegateInfo.nonTxEntityManager ) {
> < nonTxEntityManager.clear();
> < }
> *---*
>> returnValue = _getDelegate().createNamedQuery(name);
>> if( delegateInfo.nonTxEntityManager ) {
>> returnValue = new QueryWrapper(returnValue, true, nonTxEntityManager);
> *284,289c278,280*
> < try {
> < returnValue = _getDelegate().createNativeQuery(sqlString);
> < } finally {
> < if( delegateInfo.nonTxEntityManager ) {
> < nonTxEntityManager.clear();
> < }
> *---*
>> returnValue = _getDelegate().createNativeQuery(sqlString);
>> if( delegateInfo.nonTxEntityManager ) {
>> returnValue = new QueryWrapper(returnValue, true, nonTxEntityManager);
> *296,302c287,290*
> < try {
> < returnValue = _getDelegate().createNativeQuery
> < (sqlString, resultClass);
> < } finally {
> < if( delegateInfo.nonTxEntityManager ) {
> < nonTxEntityManager.clear();
> < }
> *---*
>> returnValue = _getDelegate().createNativeQuery
>> (sqlString, resultClass);
>> if( delegateInfo.nonTxEntityManager ) {
>> returnValue = new QueryWrapper(returnValue, true, nonTxEntityManager);
> *309,315c297,300*
> < try {
> < returnValue = _getDelegate().createNativeQuery
> < (sqlString, resultSetMapping);
> < } finally {
> < if( delegateInfo.nonTxEntityManager ) {
> < nonTxEntityManager.clear();
> < }
> *---*
>> returnValue = _getDelegate().createNativeQuery
>> (sqlString, resultSetMapping);
>> if( delegateInfo.nonTxEntityManager ) {
>> returnValue = new QueryWrapper(returnValue, true, nonTxEntityManager);
>
>
> *QueryWrapper.java*
> package com.sun.enterprise.util;
>
> import javax.persistence.Query;
> import javax.persistence.TemporalType;
> import javax.persistence.FlushModeType;
> import javax.persistence.EntityManager;
> import java.util.List;
> import java.util.Date;
> import java.util.Calendar;
>
> /**
> * Query wrapper used in non transaction mode.
> *
> * @author Wonseok Kim (guruwons_at_tmax.co.kr
> <mailto:guruwons_at_tmax.co.kr>)
> */
> public class QueryWrapper implements Query {
> private Query delegate;
> private boolean nonTxMode;
> private EntityManager nonTxEntityManager;
>
> QueryWrapper(Query delegate, boolean nonTxMode, EntityManager
> nonTxEntityManager){
> this.delegate = delegate;
> this.nonTxMode = nonTxMode;
> this.nonTxEntityManager = nonTxEntityManager;
> }
>
> public List getResultList() {
> try {
> return delegate.getResultList();
> } finally {
> if(nonTxMode){
> nonTxEntityManager.clear();
> }
> }
> }
>
> public Object getSingleResult() {
> try {
> return delegate.getSingleResult();
> } finally {
> if(nonTxMode){
> nonTxEntityManager.clear();
> }
> }
> }
>
> public int executeUpdate() {
> return delegate.executeUpdate();
> }
>
> public Query setMaxResults(int maxResult) {
> delegate.setMaxResults(maxResult);
> return this;
> }
>
> public Query setFirstResult(int startPosition) {
> delegate.setFirstResult(startPosition);
> return this;
> }
>
> public Query setHint(String hintName, Object value) {
> delegate.setHint(hintName, value);
> return this;
> }
>
> public Query setParameter(String name, Object value) {
> delegate.setParameter(name, value);
> return this;
> }
>
> public Query setParameter(String name, Date value, TemporalType
> temporalType) {
> delegate.setParameter(name, value, temporalType);
> return this;
> }
>
> public Query setParameter(String name, Calendar value,
> TemporalType temporalType) {
> delegate.setParameter(name, value, temporalType);
> return this;
> }
>
> public Query setParameter(int position, Object value) {
> delegate.setParameter(position, value);
> return this;
> }
>
> public Query setParameter(int position, Date value, TemporalType
> temporalType) {
> delegate.setParameter(position, value, temporalType);
> return this;
> }
>
> public Query setParameter(int position, Calendar value,
> TemporalType temporalType) {
> delegate.setParameter(position, value, temporalType);
> return this;
> }
>
> public Query setFlushMode(FlushModeType flushMode) {
> delegate.setFlushMode(flushMode);
> return this;
> }
> }
>
>
> ------------------------------------------------------------------------
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail:
>
> dev-unsubscribe_at_glassfish.dev.java.net <mailto:dev-unsubscribe_at_glassfish.dev.java.net>
> For additional commands, e-mail: dev-help_at_glassfish.dev.java.net <mailto:dev-help_at_glassfish.dev.java.net>
>
>
>
>