I used tables with native (identity) generator on both hsql and MSSQL 2005 Database.
Error occures randomly on Hibernate 3.3.1GA & 3.2.6GA therefore it's difficult to debug
I am experiencing a problem when persisting a large amount of Serialized objects within a single Transaction. (But not all objects of same type/mapping)
Sometimes the savOrUpdate doesn't actually perform an insert (skips an insert) - but assigns the bean in the persistence context the wrong generated Id.
The assigned generated id is actually genereted by an insert that should have been executed later in the batch.
The Insert that followed and got the correctly generated id, is inserted correctly to the database according to the transaction logs.
But the check of uniqueness fails because the bean in the persistencecontext which has NOT bean inserted properly (according to the transaction logs of the database and !!without!! errormessage) has the given id already assigned (in session context).
Maybe an example will make more clear what happens.
4 objects A,B,C,D will get persisted with saveOrUpdate the generated ids should be 0,1,2,3
Iteration 1 (OK) :
A -> saveOrUpdate -> Insert A into xyz (persist in Db) -> DB has Transact logs -> call identity delivers ID 0 -> bean in sessioncontext has id 0
Iteration 2 (something wents wrong) :
B -> saveOrUpdate -> Insert B into xyz (persist in Db) -> DB has NO Transact logs -> somehow bean gets id 2
Iteration 3 (OK) :
C -> saveOrUpdate -> Insert C into xyz (persist in Db) -> DB has Transact logs -> call identity delivers ID 1 because previous insert went wriong without error
-> bean in sessioncontext has id 1
Iteration 4 (Error happens) :
D -> saveOrUpdate -> Insert D into xyz (persist in Db) -> DB has Transact logs -> call identity delivers ID 2 -> bean in sessioncontext has id 2
-> checkOfuniqueness throws exception because a bean with the id is already in the session.
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [de.tts.bd.model.HistoryEntry#4223]
at org.hibernate.engine.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:613)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:326)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:117)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:534)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:526)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:522)
at de.tts.bd.business.exchange.serialize.ImportSerializable.persistEntity(ImportSerializable.java:260)
The mapping is straight forward - just a primary key no foreign keys:
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="de.tts.bd.model.HistoryEntry" table="ttbd_history" mutable="true">
<id name="id" column="history_id" type="long"><generator class="native"/></id>
<property name="timestamp" type="timestamp" column="event_time" not-null="true" unique="false"/>
<property name="event" type="character" column="event_type" length="16" not-null="true" unique="false"/> <!-- "+", "-", "#", ">" -->
<property name="objectID" type="long" column="object_id" not-null="true" unique="false"/>
<property name="objectType" type="string" column="object_type" length="16" not-null="true" unique="false"/>
<property name="objectUUID" type="string" column="object_uuid" length="40" not-null="true" unique="false"/>
<property name="userName" type="string" column="username" length="64" not-null="false" unique="false"/>
<property name="userProfileID" type="long" column="user_profile_id" not-null="false"/>
<property name="changes" type="text" column="changes" not-null="false" unique="false"/>
</class>
</hibernate-mapping>
Code:
...
public Long getId() { return this.id; }
public void setId(Long id) { this.id = id; }
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if(obj instanceof Decorator) {
obj=((Decorator)obj).getDecorated();
}
if(obj == null || !this.getClass().isAssignableFrom(obj.getClass())) {
return super.equals(obj);
} else if(this.getId() == null || ((Entity)obj).getId() == null) {
return super.equals(obj);
} else {
return this.getId().equals(((Entity)obj).getId());
}
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return this.getId() != null ? this.getId().hashCode() : super.hashCode();
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return new StringBuffer()
.append(this.getClass())
.append("(")
.append(this.getId())
.append(")").toString();
}
...
Does someone have an idea.
I already tried to reduce the amount of data per transaction by splitting and persisting data in multiple transactions.
Also I tried to isolate the problem with load tests -> massive saveOrUpdates outside the Webcontext of single Entitytypes but wasn't reproducable.
Any ideas or help appreciated.