hibernate/hibernate-orm — pull request #750 by sumeetchhetri (2014-05-21 14:41:07+00:00)
Title: Fixed NPE at TwoPhaseLoad.doInitializeEntity:167

This fixes the NPE in TwoPhaseLoad.doInitializeEntity:167
Difficult to reproduce, hence explaining the issue in detail here,
Consider 5 concurrent threads using the same EntityManager (injected
using @PersistenceContext)
All 5 threads execute native queries(targeted towards the same tables)
mapping the result to an Object (marked with @Entity)
The Object implements the equals and hashCode only with the id
property(marked with @Id)
There are chances that the queries may fetch the same objects
concurrently
I was getting an NPE at TwoPhaseLoad.doInitializeEntity:167 as (final
Object[] hydratedState = entityEntry.getLoadedState();)
hydratedState is null at this point

Now looking at org.hibernate.loader.Loader.loadFromResultSet, there are
2 places where an EntityEntry is added
to the Session PersistenceContext, one at
TwoPhaseLoad.addUninitializedEntity which initializes EntityEntry with
loadedState property to null, and the other one at
TwoPhaseLoad.postHydrate where it updates the loadedState for that
EntityEntry.

The problem occurs exactly between these 2 steps,
Consider the first thread has finished executing
TwoPhaseLoad.addUninitializedEntity and hence there is an entry with
similar identifier in the PersistenceContext, but has not yet updated
the loadedState property. Now if another thread
visiting the TwoPhaseLoad.doInitializeEntity method at this time will
find the loadedState=null property of an entity
with a similar identifier. This is where the code throws an NPE.

To fix this we need to synchronize access to the PersistenceContext,
which will make sure all EntityEntry records will
always have non null loadedState property. There may be issues with this
i guess. Not sure what other things this may impact.

I have fixed this issue in our application with this change and i'm not
getting any NPE's any more.
Can't share the code in a public forum, but the equals and hashCode
method for the Entity is outlined here,

@Entity
public class SomeTntity implements Serializable
{
private String id;

@Id
@Column(name="ID")
public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

...other properties follow...

@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}

@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SomeTntity other = (SomeTntity) obj;
if (id == null)
{
if (other.id != null)
return false;
}
else if (!id.equals(other.id))
return false;
return true;
}
}