You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@openjpa.apache.org by egoosen <eg...@metropolitan.co.za> on 2008/10/01 12:02:27 UTC

Re: Non dirty entity version field update SUPER URGENT!!!

I found the problem.

I performed a test using an entity with the least mapped relationships:
TblPdtcde.class:
//	Mapped Relationships
@OneToMany(mappedBy="tblPdtcde",fetch = FetchType.LAZY)
private Collection<TblScmpdt> tblScmpdts = new ArrayList<TblScmpdt>();

@Test
    public void testMergeUnchangedEntityAPIs() {
	EntityManagerFactory emf =
Persistence.createEntityManagerFactory("status");
	EntityManager em = emf.createEntityManager();

	TblPdtcde e = em.find(TblPdtcde.class, "F");
//	e.getTblScmpdts();
        e = ((OpenJPAEntityManager)em).detach(e);
        assertNotNull(e);
        assertFalse(em.contains(e));

        Object eOld = e;
        TblPdtcdeDTO dto = copyBean(e, TblPdtcdeDTO.class);
        e = copyBean(dto, TblPdtcde.class);
        assertNotSame(eOld, e);

        em.getTransaction().begin();
        em.merge(e);
        em.getTransaction().commit();

        em.close();
    }

The version number increments when I comment out "e.getTblScmpdts();" (which
loads the related TblScmpdt entities).
Copying from the DTO to the entity causes the entity to become dirty,
because the DTO sets the tblScmpdts collection to empty collection instead
of null.

TblPdtcdeDTO:
public Collection getTblScmpdts() { 
		if (tblScmpdts == null) {
			tblScmpdts = new ArrayList();
		}
		return tblScmpdts;
	}

StateManagerImpl.settingObjectField (line 1862) gets called curVal =
Collection<TblScmpdt>, newVal = empty collection.
This therefore triggers a version increment.

This is going to be a tricky one to fix...
-- 
View this message in context: http://n2.nabble.com/Non-dirty-entity-version-field-update-SUPER-URGENT%21%21%21-tp1120307p1131090.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.


Re: Non dirty entity version field update SUPER URGENT!!!

Posted by egoosen <eg...@metropolitan.co.za>.
Hi Mike,

Yeah, I haven't fully tested this, so I guess this change could introduce
other problems.
To answer your question regarding lazy initialization...
I mostly use lazy initialization, but during the process of copying model to
DTO, and back again, an initially null (lazy field) becomes in this case, an
empty collection, which OpenJPA sees as a dirty field.

For example:
TblFndmst has a Collection<TblScmpdt> tblScmpdts's. (@OneToMany)
TblFndmstDTO (GWT 1.4 friendly DTO) will have the exact same fields as
TblFndmst.
The getter method on TblFndmstDTO:
public Collection getTblScmpdts() {
		if (tblScmpdts == null) {
			tblScmpdts = new ArrayList();
		}
		return tblScmpdts;
	}
So when the DTO comes into the service layer from the GWT frontend, I use
Dozer to copy DTO fields to a "new" TblFndmst entity, to be merged back into
the datastore.
So during this copy process, the model that was initially loaded with a null
value for tblScmpdts, now has an empty collection value, since the getter
method never returns a null value.

Can you run your unit tests with my change, and see if it breaks anything?

Without this change, I will need to rewrite my DTO's to not return empty
collections instead of null, which will probably break my frontend code in
some places.

Kind regards,
Enrico


Michael Dick wrote:
> 
> Hi,
> 
> Calling merge on a 'new' entity is one of the more error prone aspects of
> JPA. The patch you've proposed will resolve the issue in your scenario but
> it could introduce problems in other scenarios.
> 
> The change you made deals with what assumptions OpenJPA can make when we
> find a field that is set to null in a 'new' entity.
> 
> Currently if the field is null and we can assume that null fields were not
> loaded we ignore the field. With your change we would ignore such a field
> if
> the collection is empty or null (and we can assuming nulls were not
> loaded).
> Where this goes wrong is if the application intended to set the field to
> an
> empty collection (if I'm reading the code correctly it's a bit more
> complicated than that, but bear with me). There's room for improvement in
> the way we handle merging new instances and we might be able to do
> something
> similar to your patch, but I'd have to take a closer look before I can
> sign
> off on it.
> 
> Instead of updating the OpenJPA code, have you tried using lazy
> initialization on the field (I'm assuming you initialize it to an empty
> collection).
> 
> -mike
> 

-- 
View this message in context: http://n2.nabble.com/Non-dirty-entity-version-field-update-SUPER-URGENT%21%21%21-tp1120307p1141645.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.


Re: Non dirty entity version field update SUPER URGENT!!!

Posted by Michael Dick <mi...@gmail.com>.
Hi,

Calling merge on a 'new' entity is one of the more error prone aspects of
JPA. The patch you've proposed will resolve the issue in your scenario but
it could introduce problems in other scenarios.

The change you made deals with what assumptions OpenJPA can make when we
find a field that is set to null in a 'new' entity.

Currently if the field is null and we can assume that null fields were not
loaded we ignore the field. With your change we would ignore such a field if
the collection is empty or null (and we can assuming nulls were not loaded).
Where this goes wrong is if the application intended to set the field to an
empty collection (if I'm reading the code correctly it's a bit more
complicated than that, but bear with me). There's room for improvement in
the way we handle merging new instances and we might be able to do something
similar to your patch, but I'd have to take a closer look before I can sign
off on it.

Instead of updating the OpenJPA code, have you tried using lazy
initialization on the field (I'm assuming you initialize it to an empty
collection).

-mike


On Thu, Oct 2, 2008 at 4:14 AM, egoosen <eg...@metropolitan.co.za> wrote:

>
> Fixed the compilation problem in my previous post, by running: mvn clean
> package
> --
> View this message in context:
> http://n2.nabble.com/Non-dirty-entity-version-field-update-SUPER-URGENT%21%21%21-tp1120307p1133460.html
> Sent from the OpenJPA Users mailing list archive at Nabble.com.
>
>

Re: Non dirty entity version field update SUPER URGENT!!!

Posted by egoosen <eg...@metropolitan.co.za>.
Fixed the compilation problem in my previous post, by running: mvn clean
package
-- 
View this message in context: http://n2.nabble.com/Non-dirty-entity-version-field-update-SUPER-URGENT%21%21%21-tp1120307p1133460.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.


Re: Non dirty entity version field update SUPER URGENT!!!

Posted by egoosen <eg...@metropolitan.co.za>.
Further to my previous post, I've altered one line of code in
org.apache.openjpa.kernel.AttachStrategy (line 210):
if ((frmc == null || frmc.isEmpty()) && !nullLoaded) //changed from: if
(frmc == null && !nullLoaded)

...and this seems to fix my problem, but I just tested standalone, not via
my web app.

When I re-built my web app, and ran it, I got this exception on startup, and
I have no idea how to fix it:

SEVERE: StandardWrapper.Throwable
java.lang.Error: Unresolved compilation problems: 
	JPQLTreeConstants cannot be resolved to a type
	JJTFROMITEM cannot be resolved
	JJTSUBSELECT cannot be resolved
	JJTFROM cannot be resolved
	JJTABSTRACTSCHEMANAME cannot be resolved
	JJTSUBSELECT cannot be resolved
	JJTINNERJOIN cannot be resolved
	JJTPATH cannot be resolved
	JPQL cannot be resolved to a type
	JPQL cannot be resolved to a type
	JJTSELECT cannot be resolved
	JJTSELECT cannot be resolved
	JJTSUBSELECT cannot be resolved
	JJTDELETE cannot be resolved
	JJTUPDATE cannot be resolved
	JJTGROUPBY cannot be resolved
	JJTHAVING cannot be resolved
	JJTORDERBY cannot be resolved
	JJTASCENDING cannot be resolved
	JJTSELECTCLAUSE cannot be resolved
	JJTDISTINCT cannot be resolved
	JJTCONSTRUCTOR cannot be resolved
	JJTSELECTEXPRESSIONS cannot be resolved
	JJTOUTERFETCHJOIN cannot be resolved
	JJTINNERFETCHJOIN cannot be resolved
	JJTUPDATEITEM cannot be resolved
	JJTWHERE cannot be resolved
	JJTFROM cannot be resolved
	JJTFROMITEM cannot be resolved
	JJTOUTERJOIN cannot be resolved
	JJTINNERJOIN cannot be resolved
	JJTINNERFETCHJOIN cannot be resolved
	JJTOUTERFETCHJOIN cannot be resolved
	JJTABSTRACTSCHEMANAME cannot be resolved
	JJTWHERE cannot be resolved
	JJTBOOLEANLITERAL cannot be resolved
	JJTINTEGERLITERAL cannot be resolved
	JJTDECIMALLITERAL cannot be resolved
	JJTSTRINGLITERAL cannot be resolved
	JJTTRIMCHARACTER cannot be resolved
	JJTESCAPECHARACTER cannot be resolved
	JJTPATTERNVALUE cannot be resolved
	JJTNAMEDINPUTPARAMETER cannot be resolved
	JJTPOSITIONALINPUTPARAMETER cannot be resolved
	JJTOR cannot be resolved
	JJTAND cannot be resolved
	JJTEQUALS cannot be resolved
	JJTNOTEQUALS cannot be resolved
	JJTLESSTHAN cannot be resolved
	JJTLESSOREQUAL cannot be resolved
	JJTGREATERTHAN cannot be resolved
	JJTGREATEROREQUAL cannot be resolved
	JJTADD cannot be resolved
	JJTSUBTRACT cannot be resolved
	JJTMULTIPLY cannot be resolved
	JJTDIVIDE cannot be resolved
	JJTBETWEEN cannot be resolved
	JJTIN cannot be resolved
	JJTISNULL cannot be resolved
	JJTPATH cannot be resolved
	JJTIDENTIFIER cannot be resolved
	JJTIDENTIFICATIONVARIABLE cannot be resolved
	JJTNOT cannot be resolved
	JJTLIKE cannot be resolved
	JJTESCAPECHARACTER cannot be resolved
	JJTISEMPTY cannot be resolved
	JJTSIZE cannot be resolved
	JJTUPPER cannot be resolved
	JJTLOWER cannot be resolved
	JJTLENGTH cannot be resolved
	JJTABS cannot be resolved
	JJTSQRT cannot be resolved
	JJTMOD cannot be resolved
	JJTTRIM cannot be resolved
	JJTTRIMLEADING cannot be resolved
	JJTTRIMTRAILING cannot be resolved
	JJTTRIMLEADING cannot be resolved
	JJTTRIMTRAILING cannot be resolved
	JJTTRIMBOTH cannot be resolved
	JJTCONCAT cannot be resolved
	JJTSUBSTRING cannot be resolved
	JJTLOCATE cannot be resolved
	JJTAGGREGATE cannot be resolved
	JJTCOUNT cannot be resolved
	JJTMAX cannot be resolved
	JJTMIN cannot be resolved
	JJTSUM cannot be resolved
	JPQL cannot be resolved to a type
	JJTNEGATIVE cannot be resolved
	Node cannot be resolved to a type
	JPQL cannot be resolved to a type
	JPQL cannot be resolved to a type
	Node cannot be resolved to a type
	Node cannot be resolved to a type
	Node cannot be resolved to a type
	Node cannot be resolved to a type
	The method jjtGetChild(int) from the type JPQLExpressionBuilder.JPQLNode
refers to the missing type Node
	Token cannot be resolved to a type
	JPQLTreeConstants cannot be resolved
	JPQL cannot be resolved to a type

	at
org.apache.openjpa.kernel.jpql.JPQLExpressionBuilder$ParsedJPQL.<init>(JPQLExpressionBuilder.java:71)
	at org.apache.openjpa.kernel.jpql.JPQLParser.parse(JPQLParser.java:48)
	at
org.apache.openjpa.kernel.ExpressionStoreQuery.newCompilation(ExpressionStoreQuery.java:149)
	at org.apache.openjpa.kernel.QueryImpl.newCompilation(QueryImpl.java:657)
	at
org.apache.openjpa.kernel.QueryImpl.compilationFromCache(QueryImpl.java:639)
	at
org.apache.openjpa.kernel.QueryImpl.compileForCompilation(QueryImpl.java:605)
	at
org.apache.openjpa.kernel.QueryImpl.compileForExecutor(QueryImpl.java:667)
	at org.apache.openjpa.kernel.QueryImpl.compile(QueryImpl.java:574)
	at
org.apache.openjpa.persistence.EntityManagerImpl.createNamedQuery(EntityManagerImpl.java:901)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:585)
	at
org.springframework.orm.jpa.JpaTemplate$CloseSuppressingInvocationHandler.invoke(JpaTemplate.java:400)
	at $Proxy72.createNamedQuery(Unknown Source)
	at za.co.metcapri.dao.jpa.BaseJPADAO$2.doInJpa(BaseJPADAO.java:201)
	at org.springframework.orm.jpa.JpaTemplate.execute(JpaTemplate.java:191)
	at
org.springframework.orm.jpa.JpaTemplate.executeFind(JpaTemplate.java:158)
	at za.co.metcapri.dao.jpa.BaseJPADAO.findRecordsByName(BaseJPADAO.java:199)
	at
za.co.metcapri.dao.jpa.codedes.TblCdedesJPADAO.findTblCdedessByCdetypTxt(TblCdedesJPADAO.java:28)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:585)
	at
org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:299)
	at
org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:172)
	at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:139)
	at
org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:54)
	at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
	at
org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:54)
	at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
	at
org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
	at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
	at
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
	at $Proxy35.findTblCdedessByCdetypTxt(Unknown Source)
	at
za.co.metcapri.status.business.DataLookupManagerBean.getLookupData(DataLookupManagerBean.java:95)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:585)
	at
org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:299)
	at
org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:172)
	at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:139)
	at
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:107)
	at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
	at
org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:54)
	at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
	at
org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
	at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
	at
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
	at $Proxy48.getLookupData(Unknown Source)
	at
za.co.metcapri.status.server.services.AppInitServiceBean.init(AppInitServiceBean.java:65)
	at javax.servlet.GenericServlet.init(GenericServlet.java:212)
	at
org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1161)
	at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:981)
	at
org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4045)
	at
org.apache.catalina.core.StandardContext.start(StandardContext.java:4351)
	at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
	at org.apache.catalina.core.StandardHost.start(StandardHost.java:719)
	at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
	at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
	at org.apache.catalina.core.StandardService.start(StandardService.java:516)
	at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
	at org.apache.catalina.startup.Catalina.start(Catalina.java:566)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:585)
	at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:288)
	at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)
-- 
View this message in context: http://n2.nabble.com/Non-dirty-entity-version-field-update-SUPER-URGENT%21%21%21-tp1120307p1131754.html
Sent from the OpenJPA Users mailing list archive at Nabble.com.