The docs say that to make sure objects from different sessions behave correctly in respect to hashCode() and equals() methods one should override these methods and use hashCode() and equals() on the business keys.
Now this is exactly what I do but I'm running into the following problem:
I have a class that contains a set of items of the same class (class and mapping file included below for clarity). When I load instances of this class I get a NullPointer exception in the hashCode() method because apparently one of the business fields is null. This call however happens because the object is added to the set but is apparently not completely loaded yet.
Is there anyone who has ran into similar problems and found a solution for it? Or does anyone know how to solve this?
The executed code:
Code:
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
DataItem dataItem = Data.ACCENT.add("test"); // creates and saves a new DataItem
dataItem.addSubData("subdata 1");
dataItem.addSubData("subdata 2");
session.getTransaction().commit();
HibernateUtil.closeSessionFactory();
HibernateUtil.getSessionFactory();
session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
List<DataItem> results = session.createQuery("from DataItem").list(); // This line produces the error
The stack trace:
Code:
org.hibernate.PropertyAccessException: Exception occurred inside setter of be.bloso.vetrasoft.model.application.DataItem.allSubData
at org.hibernate.property.BasicPropertyAccessor$BasicSetter.set(BasicPropertyAccessor.java:89)
at org.hibernate.tuple.entity.AbstractEntityTuplizer.setPropertyValues(AbstractEntityTuplizer.java:352)
at org.hibernate.tuple.entity.PojoEntityTuplizer.setPropertyValues(PojoEntityTuplizer.java:232)
at org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:3580)
at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:152)
at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:877)
at org.hibernate.loader.Loader.doQuery(Loader.java:752)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
at org.hibernate.loader.Loader.doList(Loader.java:2232)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2129)
at org.hibernate.loader.Loader.list(Loader.java:2124)
at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:401)
at org.hibernate.hql.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:363)
at org.hibernate.engine.query.HQLQueryPlan.performList(HQLQueryPlan.java:196)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1149)
at org.hibernate.impl.QueryImpl.list(QueryImpl.java:102)
at be.bloso.vetrasoft.model.application.DatabaseTest.testDataItem(DatabaseTest.java:115)
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:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:73)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:46)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:46)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.reflect.InvocationTargetException
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:597)
at org.hibernate.property.BasicPropertyAccessor$BasicSetter.set(BasicPropertyAccessor.java:66)
... 40 more
Caused by: java.lang.NullPointerException
at be.bloso.vetrasoft.model.application.DataItem.hashCode(DataItem.java:293)
at java.util.HashMap.put(HashMap.java:372)
at java.util.HashSet.add(HashSet.java:200)
at java.util.AbstractCollection.addAll(AbstractCollection.java:305)
at org.hibernate.collection.PersistentSet.endRead(PersistentSet.java:352)
at org.hibernate.engine.loading.CollectionLoadContext.endLoadingCollection(CollectionLoadContext.java:260)
at org.hibernate.engine.loading.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:245)
at org.hibernate.engine.loading.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:218)
at org.hibernate.loader.Loader.endCollectionLoad(Loader.java:900)
at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:888)
at org.hibernate.loader.Loader.doQuery(Loader.java:752)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
at org.hibernate.loader.Loader.loadCollection(Loader.java:2019)
at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:59)
at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:587)
at org.hibernate.event.def.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:83)
at org.hibernate.impl.SessionImpl.initializeCollection(SessionImpl.java:1744)
at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:366)
at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:108)
at org.hibernate.collection.PersistentSet.iterator(PersistentSet.java:186)
at java.util.AbstractCollection.addAll(AbstractCollection.java:303)
at be.bloso.vetrasoft.model.application.DataItem.setAllSubData(DataItem.java:179)
... 45 more
Class source
Code:
public class DataItem implements Comparable<DataItem>, Serializable, DataChangeListener, Lifecycle {
private static final long serialVersionUID = 1L;
private Long id;
private Data rootData; // Data is an Enum
private DataItem parent;
private String data;
private final Set<DataItem> subData = new HashSet<DataItem>();
private int level;
private boolean deprecated = false;
private Integer version;
private transient boolean isLoaded = false;
protected DataItem() {
super(); // Used by Hibernate
}
public Long getId() {
return id;
}
protected void setVersion(Integer version) {
this.version = version;
}
protected Integer getVersion() {
return version;
}
public void save() {
Session session = HibernateUtil.getCurrentSession();
try {
if (getId() != null) {
session.refresh(this);
}
}
catch (HibernateException h) {
// Object not in db yet
}
session.saveOrUpdate(this);
for (DataItem subDataItem : getAllSubData()) {
subDataItem.save();
}
}
protected void delete() {
for (DataItem item : getAllSubData()) {
item.delete();
}
HibernateUtil.getCurrentSession().delete(this);
}
protected void setId(Long id) {
this.id = id;
}
public Data getRootData() {
return rootData;
}
protected void setRootData(Data rootData) {
this.rootData = rootData;
}
public void setDeprecated(boolean deprecated) {
this.deprecated = deprecated;
if (isLoaded()) {
for (DataItem item : getAllSubData()) {
item.setDeprecated(deprecated);
}
}
}
public boolean isDeprecated() {
return deprecated;
}
public DataItem getParent() {
return parent;
}
protected void setParent(DataItem parent) {
this.parent = parent;
}
public int getLevel() {
return level;
}
protected void setLevel(int level) {
this.level = level;
}
public String getData() {
return data;
}
public void setData(String string) {
this.data = string;
}
@Override
public String toString() {
return data;
}
public Set<DataItem> getAllSubData() {
return subData;
}
protected void setAllSubData(Set<DataItem> subData) {
this.subData.addAll(subData);
}
public DataItem getSubData(String string) {
for (DataItem item : getAllSubData()) {
if (item.toString().equals(string)) {
return item;
}
}
return null;
}
public DataItem getSubData(Long itemId) {
for (DataItem item : getAllSubData()) {
if (item.getId().equals(itemId)) {
return item;
}
}
return null;
}
public DataItem removeSubData(String string) {
DataItem removedItem = getSubData(string);
getAllSubData().remove(removedItem);
removedItem.delete();
save();
return removedItem;
}
public DataItem addSubData(String string) {
DataItem item = getSubData(string);
if (item == null) {
item = new DataItem(this, string);
subData.add(item);
item.addDataChangeListener(this);
}
return item;
}
public boolean hasSubData(String string) {
return getSubData(string) != null;
}
public void clearSubData() {
for (DataItem item : subData) {
item.removeDataChangeListener(this);
}
subData.clear();
}
public int compareTo(DataItem item) {
if (item.getRootData() != getRootData()) {
throw new IllegalArgumentException("Can not compare data items from different data sets.");
}
return toString().compareTo(item.toString());
}
@Override
public boolean equals(Object object) {
if (object instanceof DataItem) {
DataItem item = (DataItem) object;
return item.getData().equals(getData()) && item.getRootData().equals(getRootData());
} else {
return false;
}
}
@Override
public int hashCode() {
return getRootData().hashCode() + 29 * getData().hashCode(); // Both getRootData() and getData() are null
}
private boolean isLoaded() {
return isLoaded;
}
@Override
public boolean onDelete(Session session) throws CallbackException {
return NO_VETO;
}
@Override
public void onLoad(Session session, Serializable serializable) {
isLoaded = true;
}
@Override
public boolean onSave(Session session) throws CallbackException {
return NO_VETO;
}
@Override
public boolean onUpdate(Session session) throws CallbackException {
return NO_VETO;
}
}
Mapping files (DataItem is a subclass of PersistentObject):
Code:
<hibernate-mapping package="be.bloso.vetrasoft.model">
<class name="PersistentObject" abstract="true">
<id name="id" column="id">
<generator class="hilo" />
</id>
<version name="version" />
</class>
</hibernate-mapping>
<hibernate-mapping package="be.bloso.vetrasoft.model.application">
<class name="DataItem" table="DATAITEM">
<id name="id" column="DATAITEM_ID">
<generator class="hilo" />
</id>
<version name="version" />
<property name="rootData" />
<property name="data" />
<property name="level" />
<property name="deprecated" />
<many-to-one name="parent" class="DataItem" />
<set name="allSubData" inverse="true" lazy="true" table="DATAITEM">
<key column="parent" />
<one-to-many class="DataItem" />
</set>
</class>
</hibernate-mapping>
Kind regards,
Stef Kuypers