Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>You have two ways to handle this situation: either with <em>pessimist</em> locking or with <em>optimist</em> locking. But you seem to use neither of both, which explain probably the incorrect behaviour. </p> <ul> <li><p>With optimistic locking, Hibernate will check that the user account wasn't altered between the time it was read and saved. A concurrent transaction may then fail and be rolled back.</p></li> <li><p>With pessimistic locking, you lock the row when you read it and it's unlocked only when transaction completes. This prevent a concurrent transaction to read data that would become stale.</p></li> </ul> <p>Refreshing the entity may read new data or not depending whether the current transaction has already been committed or not, but is not a solution neither. Because you seem to also create the user account if it doesn't exist, you can't apply pessimist locking so easily. I would suggest you use optimistic locking then (and use for instance a timestamp to detect concurrent modifications). </p> <p>Read this <a href="https://stackoverflow.com/questions/129329/optimistic-vs-pessimistic-locking">other question</a> on SO about pessimist and optimist locking. Have also a look at hibernate chapter "<a href="http://docs.jboss.org/hibernate/core/3.3/reference/en/html/transactions.html" rel="nofollow noreferrer">transaction and concurrency</a>" and "<a href="http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-mapping-entity-version" rel="nofollow noreferrer">hibernate annotations</a>". </p> <p>It should be as simple as adding <code>@Version</code> on the corresponding field, the <code>optimisticLockStrategy</code> <a href="http://docs.jboss.org/hibernate/stable/annotations/api/org/hibernate/annotations/Entity.html#optimisticLock()" rel="nofollow noreferrer">default value</a> is <code>VERSION</code> (a separate column is used).</p> <p>-- UPDATE --</p> <p>You can test whether it works in a test case. I've created a simple entity <code>Counter</code> with an <code>ID</code>, <code>value</code>, and <code>version</code> fields.</p> <pre><code> public class Counter implements Serializable { @Id @GeneratedValue(strategy=GenerationType.AUTO) @Basic(optional = false) @Column(name = "ID") private Integer id; @Column(name = "VALUE") private Integer value; @Column(name = "VERSION") @Version private Integer version; ... } </code></pre> <p>If you update one entity <strong>sequentially</strong> it works:</p> <pre><code> id = insertEntity( ... ); em1.getTransaction().begin(); Counter c1 = em1.find( Counter.class, id ); c1.setValue( c1.getValue() + 1 ); em1.flush(); em1.getTransaction().commit(); em2.getTransaction().begin(); Counter c2 = em2.find( Counter.class, id ); c2.setValue( c2.getValue() + 1 ); em2.flush(); // OK em2.getTransaction().commit(); </code></pre> <p>I get one entity with <code>value=2</code> and <code>version=2</code>.</p> <p>If I simulate two <strong>concurrent</strong> updates:</p> <pre><code>id = insertEntity( ... ); em1.getTransaction().begin(); em2.getTransaction().begin(); Counter c1 = em1.find( Counter.class, id ); Counter c2 = em2.find( Counter.class, id ); c1.setValue( c1.getValue() + 1 ); em1.flush(); em1.getTransaction().commit(); c2.setValue( c2.getValue() + 1 ); em2.flush(); // fail em2.getTransaction().commit(); </code></pre> <p>then the 2nd flush fails:</p> <pre><code>Hibernate: update COUNTER set VALUE=?, VERSION=? where ID=? and VERSION=? Hibernate: update COUNTER set VALUE=?, VERSION=? where ID=? and VERSION=? Dec 23, 2009 11:08:46 AM org.hibernate.event.def.AbstractFlushingEventListener performExecutions SEVERE: Could not synchronize database state with session org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [org.ewe.Counter#15] at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1765) </code></pre> <p>This is so because the actual parameters in the SQL statements are:</p> <pre><code> update COUNTER set VALUE=1, VERSION=1 where ID=xxx and VERSION=0 --&gt; 1 row updated update COUNTER set VALUE=1, VERSION=1 where ID=xxx and VERSION=0 --&gt; 0 row updated, because version has been changed in between </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload