dev@glassfish.java.net

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

From: Wonseok Kim <guruwons_at_tmax.co.kr>
Date: Wed, 5 Apr 2006 23:00:22 +0900

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.

@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)
 */
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;
    }
}