I don't have a lot to say, but this is my little bit.

Monday, July 1, 2013

A NullPointerException was thrown while extracting a value from the instance variable [FIELDNAME] in the object [ENTITYNAME]

When using EclipseLink to persist an object with an Embedded field into MongoDB, I got a null pointer exception when I tried to persist() and commit(). This was frustrating because the error message was misleading. The short answer is that my embeddable was not marked with <no-sql />, but following is the long story.

My code did the standard thing with EntityManager:

EntityManager em = factory.createEntityManager(props);
em.getTransaction().begin();
em.persist(object);
em.getTransaction().commit();

But deep inside of commit() a Null Pointer Exception occurred:

Exception Description: A NullPointerException was thrown while extracting a value from the instance variable [FIELDNAME] in the object [ENTITYNAME].
Internal Exception: java.lang.NullPointerException
Mapping: org.eclipse.persistence.eis.mappings.EISDirectMapping[FIELDNAME-->FIELDNAME]
Descriptor: RelationalDescriptor(ENTITYNAME --> [])
at org.eclipse.persistence.exceptions.DescriptorException.nullPointerWhileGettingValueThruInstanceVariableAccessor(DescriptorException.java:1275)

The embeddable object was not null and the fields on it were not null. The "caused by" error was...

Caused by: java.lang.NullPointerException
at org.eclipse.persistence.internal.descriptors.InstanceVariableAttributeAccessor.getAttributeValueFromObject(InstanceVariableAttributeAccessor.java:76)

I looked up that line of code:

return this.attributeField.get(anObject);

And sure enough, if I put a breakpoint on that line, "attributeField" is null for the field on my embeddable. This was an investigative dead end because I didn't know what could cause "attributeField" to be null. I turned on validation for my mapping file and it validated just fine.

The "attributeField" is set only in the setAttributeField() method, so I put a breakpoint there and watched as the method was called for all the other (entity) fields in my mapping, but not for this (embedded) one particular field.

I had already scoured my mapping file, which contained this: 

<embeddable class="ENTITYNAME">
    <attributes>
        <basic name="FIELDNAME" />
    </attributes>
</embeddable>

So on a lark I repeated a fix I had tried earlier which was to put <no-sql data-format="MAPPED" /> in there. I don't know why it didn't work for me the first time I tried that, but that did the trick. EclipseLink then called setAttributeField() for the field, so this.attributeField.get(anObject) returned correctly, and my object persisted to MongoDB.

<embeddable class="com.x.uuid.UUID">
    <no-sql data-format="MAPPED" />
    <attributes>
        <basic name="clockSeqAndNode" />
    </attributes>
</embeddable>

The bug here is the bad error message. The error should say "could not find field 'FIELD NAME' on object 'ENTITYNAME'", which would immediately clue the programmer that the problem is in the mapping file. Even better would be if the XML validator could check that any <embeddable> embedded in an <entity> marked with <no-sql /> must also be marked with <no-sql />.

No comments:

Post a Comment