Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [eclipselink-users] @ManyToOne and hashCode

What seems to be occurring is EclipseLink is merging into the shared cache
and is encountering a cycle of objects so is adding to the Set before the
object has been completely built.

Solutions include:
- change your hashCode() to only hon basic attributes (name), not on
relationships (group).
- use a List instead of a Set
- use the default hashCode/equals
- change your hashCode to check for null
- you can configure your descriptor to order your mappings differently so
that the group/owner are processed (merged) first using the setWeight()
method on the ManyToOneMapping using a DescriptorCustomizer.


NBW wrote:
> 
> Hi Phillip,
> 
> On Jun 28, 2012, at 7:20 PM, Phillip Ross wrote:
> 
>> Ah good old hashCode fun :)
> 
> Yea, a great mind bending exercise when you throw JPA in the mix.
> 
> This is an entertaining read:
> http://stackoverflow.com/questions/5031614/the-jpa-hashcode-equals-dilemma
> 
>> 
>> Your stack trace indicates that the NPE is happening in the Kitten
>> hashCode function when the instance is being placed in the collection.
>> You may just have to use more aggressive null checking in your
>> hashCode method(s).
> 
> Here's the odd thing. When I run a unit test which persists 3 kittens to
> the same owner in the same TX, the NPE does not happen on the same Kitten
> every time. It almost seems random.
> 
> In regard to your point about more aggressive null checks in Kitten's
> hashCode(); I could check for a null Owner. However, that persists a new
> Kitten only if it can look up the owner so the owner that is being set is
> never NULL.  In fact when I step through this code when it experiences the
> NPE I can see that the owner is not null and then I can see at some
> eclipselink code calls Kitten.hashCode and owner on that kitten object is
> indeed NULL! 
> 
> I guess to truly see what is happening here I will need to get the
> eclipselink 2.3.2 source and step through all of this. Judging from the
> stack trace this seems to be some sort of post transactional part of the
> lifecycle. I'm guess from the method names that this part of the lifecycle
> is merging the new changes back into the session. This is what lead to my
> speculation that the new Kitten had been associated with an Owner whose
> hash code was different then it was at the start of the session (perhaps
> due to the olversion reving) and therefore this code looked it up with the
> old code and got a NULL and that's why Kitten sees its owner as NULL at
> this point in the lifecycle. That's my best guess short of downloading the
> Eclipselink 2.3.2 source and stepping through all of this. :-)
> 
> -Noah
> 
> 
>> 
>> In my projects I absolutely exclude the version field form the hash
>> code method.  I exclude the id field as well.  Actually, I only
>> compute it once, store it in a transient property in the entity and
>> return the cached value on subsequent calls to hash code.  It looks
>> and feels convoluted but it works for my projects :)
>> 
>> 
>> On Thu, Jun 28, 2012 at 1:12 PM, Noah White <emailnbw@xxxxxxxxx> wrote:
>>> I have an entity which has a ManyToOne relationship expressed like so:
>>> 
>>> Kitten:
>>> 
>>> @ManyToOne(optional = false)
>>> @JoinColumn(name = "USER_USERID", nullable = false)
>>> private User owner;
>>> 
>>> 
>>> On the other side of the relationship I have:
>>> 
>>> Owner:
>>> 
>>> @OneToMany(mappedBy = "owner")
>>> @PrivateOwned
>>> private Set<Kittens> kittens;
>>> 
>>> The Kitten class has a hashCode that looks like:
>>> 
>>> @Override
>>> public int hashCode() {
>>>        int result = name.hashCode();
>>>        result = 31 * result + (group != null ? group.hashCode() : 0);
>>>        result = 31 * result + owner.hashCode();
>>>        return result;
>>> }
>>> 
>>> I have a unit test that exercises creating some new Kittens. I am seeing
>>> NPEs in its hashCode when eclipselink is inside IndirectSet.add.  I'm
>>> not sure what part of the eclipselink lifecycle this is or why this is
>>> happening since on entity creation the user being assigned the ownership
>>> if fetched, assigned, and then the new kitten is added to the owner
>>> entities set of kittens. That code looks like this and is inside an EJB
>>> method:
>>> 
>>> Set targetOwnerKittens = owner.getKittens();  //owner is NOT NULL and is
>>> a managed object here which was previously looked up with em.find
>>> 
>>> //kitten was passed into the method and these are just there to insure
>>> that id and olversion are clear prior to persist
>>> kitten.setKittenId(0);   // id - sequence generated long - this just
>>> makes sure it is zero prior to persist
>>> kitten.setOLVersion(0);
>>> 
>>> kitten.setOwner(owner);
>>> em.persist(kitten);
>>> targetOwnerKittens.add(kitten);
>>> 
>>> The exception I get is below. Could this be an issue with Owner's hash
>>> code and Eclipselink trying to look it up from its cache which is keyed
>>> off a hashcode that has since changed this it returns a null? The only
>>> thing in the Owner hashCode that might change is the olVersion.  Should
>>> the optimistic lock field not be included in hashCode?
>>> 
>>> TIA,
>>> 
>>> -Noah
>>> 
>>> java.lang.NullPointerException
>>>   at com.foo.KittenEntity.hashCode(KittenEntity.java:130)
>>>   at java.util.HashMap.put(HashMap.java:389)
>>>   at java.util.HashSet.add(HashSet.java:217)
>>>   at
>>> org.eclipse.persistence.indirection.IndirectSet.add(IndirectSet.java:173)
>>>   at
>>> org.eclipse.persistence.internal.queries.CollectionContainerPolicy.addInto(CollectionContainerPolicy.java:68)
>>>   at
>>> org.eclipse.persistence.internal.queries.ContainerPolicy.mergeChanges(ContainerPolicy.java:1093)
>>>   at
>>> org.eclipse.persistence.mappings.CollectionMapping.mergeChangesIntoObject(CollectionMapping.java:1334)
>>>   at
>>> org.eclipse.persistence.internal.descriptors.ObjectBuilder.mergeChangesIntoObject(ObjectBuilder.java:3409)
>>>   at
>>> org.eclipse.persistence.internal.sessions.MergeManager.mergeChangesOfWorkingCopyIntoOriginal(MergeManager.java:744)
>>>   at
>>> org.eclipse.persistence.internal.sessions.MergeManager.mergeChangesOfWorkingCopyIntoOriginal(MergeManager.java:617)
>>>   at
>>> org.eclipse.persistence.internal.sessions.MergeManager.mergeChanges(MergeManager.java:267)
>>>   at
>>> org.eclipse.persistence.mappings.ObjectReferenceMapping.mergeIntoObject(ObjectReferenceMapping.java:462)
>>>   at
>>> org.eclipse.persistence.internal.descriptors.ObjectBuilder.mergeIntoObject(ObjectBuilder.java:3466)
>>>   at
>>> org.eclipse.persistence.internal.descriptors.ObjectBuilder.mergeChangesIntoObject(ObjectBuilder.java:3400)
>>>   at
>>> org.eclipse.persistence.internal.sessions.MergeManager.mergeChangesOfWorkingCopyIntoOriginal(MergeManager.java:744)
>>>   at
>>> org.eclipse.persistence.internal.sessions.MergeManager.mergeChangesOfWorkingCopyIntoOriginal(MergeManager.java:617)
>>>   at
>>> org.eclipse.persistence.internal.sessions.MergeManager.mergeChanges(MergeManager.java:267)
>>>   at
>>> org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.mergeChangesIntoParent(UnitOfWorkImpl.java:3254)
>>>   at
>>> org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.mergeChangesIntoParent(RepeatableWriteUnitOfWork.java:370)
>>>   at
>>> org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.mergeClonesAfterCompletion(UnitOfWorkImpl.java:3386)
>>>   at
>>> org.eclipse.persistence.transaction.AbstractSynchronizationListener.afterCompletion(AbstractSynchronizationListener.java:213)
>>>   at
>>> org.eclipse.persistence.transaction.JTASynchronizationListener.afterCompletion(JTASynchronizationListener.java:79)
>>>   at
>>> com.sun.enterprise.transaction.JavaEETransactionImpl.commit(JavaEETransactionImpl.java:537)
>>>   at
>>> com.sun.enterprise.transaction.JavaEETransactionManagerSimplified.commit(JavaEETransactionManagerSimplified.java:855)
>>>   at
>>> com.sun.ejb.containers.BaseContainer.completeNewTx(BaseContainer.java:5136)
>>>   at
>>> com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4901)
>>>   at
>>> com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:2045)
>>>   at
>>> com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1994)
>>>   at
>>> com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:222)
>>>   at
>>> com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:88)
> 
> 
> 


-----
http://wiki.eclipse.org/User:James.sutherland.oracle.com James Sutherland 
http://www.eclipse.org/eclipselink/
 EclipseLink ,  http://www.oracle.com/technology/products/ias/toplink/
TopLink 
Wiki:  http://wiki.eclipse.org/EclipseLink EclipseLink , 
http://wiki.oracle.com/page/TopLink TopLink 
Forums:  http://forums.oracle.com/forums/forum.jspa?forumID=48 TopLink , 
http://www.eclipse.org/forums/index.php?t=thread&frm_id=111&S=1b00bfd151289b297688823a00683aca
EclipseLink 
Book:  http://en.wikibooks.org/wiki/Java_Persistence Java Persistence 
Blog:  http://java-persistence-performance.blogspot.com/ Java Persistence
Performance 
-- 
View this message in context: http://old.nabble.com/%40ManyToOne-and-hashCode-tp34087433p34119170.html
Sent from the EclipseLink - Users mailing list archive at Nabble.com.



Back to the top