问题
I am working on a multithreaded Java application that uses Hibernate. We are getting a org.hibernate.StaleObjectStateException
because we have optimistic locking in place and a certain entity (let's call it Pojo
) is updated from many application modules, which can and have collided in race conditions (I have not designed the application). The problematic code block looks something like this:
Transaction txn = null;
HibernateException ex = null
for(int retryAttempt = 0; retryAttempt < 5; retryAttempt++) {
try {
txn = hibnSessn.beginTransaction();
int pojoID = pojo.getID();
pojo = hibnSessn.get(Pojo.class, pojoID);
pojo.setSomeVar("xyz");
txn.commit();
ex = null;
} catch(HibernateException e) {
if (txn != null)
{
txn.rollback();
}
ex = e;
// Gonna keep retrying
continue;
}
break;
}
And Pojo.hbm.xml
starts something like this:
<?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="com.myproject.Pojo" table="pojo">
<id name="id" column="id" type="int">
<generator class="identity"/>
</id>
<!-- this is used to enforce optimistic locking -->
<version name="dbVersion" column="db_version"/>
...
The original author of this code put in the above retry loop precisely because there are other modules that also update Pojo
and if the initial commit fails due to a race condition, we go into additional attempts, hoping to succeed in a clean, unconflicted transaction. I dislike this pattern but have to work with it for the time being.
I have been able to instrument the race condition by breaking at line pojo.setSomeVar("xyz");
, updating the database from another thread/method, and proceeding, which fails the commit and goes into the catch, due to the optimistic locking, rolling back and going into the next iteration of the retry loop.
The problem is that, in the second iteration, the line pojo = hibnSessn.get(Pojo.class, pojoID);
does not refresh pojo
with the fresh data from DB but pulls the stale object from the session, defeating the purpose of the retry.
Another interesting thing is that hibnSessn.get(...)
does actually go into the DB (under normal conditions) because when I change the data from another thread/method before hibnSessn.get
in the first iteration before a commit failure, the object does get refreshed. It only fails to do so after a commit has failed and the transaction rolled back.
I am looking for ways to force Hibernate to go into the DB and refresh the object after an initial commit failure.
UPDATE: I tried evict/refresh
in the catch to refresh the object but that also resulted in a StaleObjectStateException
.
RELATED: https://stackoverflow.com/questions/19364343/staleobjectstateexception-when-evicting-refreshing-stale-object-from-hibernate-s
回答1:
You could try evicting the object from the session in the catch.
hibnSessn.evict(pojo);
The get on the retry should then get a fresh copy from the DB.
Refreshing it might also work.
hibnSessn.refresh(pojo);
回答2:
hibnSessn.flush() will remove the staleobject (every entity for that matter) from the Session. Thus retrieve from the db for e.g.
hibnSessn.flush()
pojo = hibnSessn.get(Pojo.class, pojoID);
来源:https://stackoverflow.com/questions/19329338/hibernate-session-not-refreshing-data-from-db-after-initial-commit-failure