dev@glassfish.java.net

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

From: Wonseok Kim <guruwons_at_tmax.co.kr>
Date: Thu, 6 Apr 2006 02:27:57 +0900

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> 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)
> */
> 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
>
>
>
>