To reproduce the optimistic locking exception, try to run code from a method that is not itself transactional. For example call a method that reads and modifies an entity, and returns a detached copy.
Then call the same method to modify the same entity, and return a new detached copy. The first copy now is stale compared to the database. Try to merge the first stale copy into a session, it will throw the optimistic locking exception.
This is some sample code for a versioned entity:
@Entity
public class MyEntity {
...
@Version
private Long version;
}
and this an example of a service that reads and returns an entity detached:
@Repository
public class SomeRepository {
@PersistenceContext
EntityManager em;
@Transactional
public MyEntity readAndModify(Long key) {
MyEntity myEntity = em.find( key, MyEntity.class);
myEntity.setSomeField(...); // this will trigger update via dirty checking
return myEntity;
}
}
This code in a test can then trigger the optimistic locking exception:
public someNonTransactionalTest() {
//read and modify entity, get detached copy
MyEntity detachedCopy1 = someRepository.readAndModify(1L);
//read and modify entity, get another detached copy - copy 1 is stale now
MyEntity detachedCopy2 = someRepository.readAndModify(1L);
// try to merge the stale copy to a session, the optimistic locking exception is thrown
detachedCopy1 = entityManager.merge(detachedCopy1);
... assertions ...
}