dev@glassfish.java.net

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

From: Kenneth Saks <Kenneth.Saks_at_Sun.COM>
Date: Wed, 05 Apr 2006 09:35:16 -0400

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
>For additional commands, e-mail: dev-help_at_glassfish.dev.java.net
>