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;
}
}