[
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.