concurrency (stale data) problem in JPA - java

Let's say I have methods with following signature
Object getData(int id) {
//create a entity manager
//get data frm db
//return data
}
updateData() {
Object obj = getData(id)
//get entity manager
//start transcation tx
//update
//commit tx
}
Now will it cause concurrency issue? Can data be stale in worst case? E.g.
if I getData and by the time I update, if someone updates the data will my updateData will have stale data?
Now can i use following:Will i solve the problem?
Object getData(int id,Entitymanager em) {
//get data frm db using em
//return data
}
updateData() {
Object obj = getData(id)
//get entity manager em
//start transcation tx
//getdata using getData(id,em)
//commit tx
}

Yes, that can happen.
If you get an entity (version 1), someone else modifies it (creating version 2), then you modify version 1 and save it, any changes in version 2 will be lost.
To stop that from happening, use optimistic concurrency by adding a #Version attribute to your entity. If a commit has occurred between your get and update, an exception will be thrown. At the point you can choose your best option to deal with it.

There can also be a problem in multi-threaded environment, if multiple access the same piece of code at the same time, it can create lock, for this you can use row level locking technique, that will help you.check out the below link.
http://download.oracle.com/docs/cd/B19306_01/server.102/b14220/consist.htm

If two separate requests access updateData() concurrently you may get stale data. You may handle the staleness by locking the fetched data in updateData(). If you're using Hibernate as your JPA provider you can lock the data as follows:
updateData() {
Object obj = getData(id);
Session session = (Session) em.getDelegate();
session.refresh(obj, LockMode.UPGRADE);
}
The refresh is necessary because it may happen that between the fetching and the locking of the data another transaction completes in updateData.
Please keep in mind that the entity manager used in getData and updateData must be the same.

Related

Multiple entityManager in Spring application. Persistence of duplicate objects issue

My Spring component gets a request from a client, asks a web-service about some data and saves received objects to a database.
I identify all objects and save only new ones.
The issue occurs when the client makes two or more same requests in the same time (or due to even different user requests I receive same objects from web-service).
To describe the issue with persistence here some details. For each client request my component starts execution in a separate thread, I get a new entityManager, begin a transaction, receive a data from web-service, then I identify objects and persist new ones using given entityManager in a current transaction.
If in separate transactions I receive the same objects from web-service and if they are new ones that are not yet in database I am not able to identify them in not-commited transactions and so they are persisted in all transactions. Then all duplicate objects will be commited and saved to database.
What could be good solutions in this case? Is there any way to identify new objects properly even in different transactions? Or what approaches can be applied?
May be Spring provides some approaches to manage transactions or entityManagers so that it can help with this issue...
Note. Of course I can use database instruments to avoid saving duplicate objects but in this case it is not a very good solution.
Check if objects are present in a database before saving.
Use #UniqueConstraint or #Column(unique = true) to prevent duplicate rows, handle exceptions appropriately.
Use #Version to manage concurrent modification for existing entities. More about optimistic and pesimistic locking: Chapter 5. Locking. Related discussions: Hibernate Automatic Versioning and When to use #Version and #Audited in Hibernate?
You may use thread locks / synchronization mechanisms to ensure that requests for the same user will happen in order. However, this won't work if your service in running on more than 1 node.
So the solution in my case is the following:
Make transactions pretty small and commit every object separately.
Make unique constraints in database to prevent duplicating of
objects. This point will not help us a lot but needed for point 3.
Every commit() method we insert in try-catch block. If we try to
commit duplicate object in parallel transactions then we will receive an exception and in catch block we can check the database, select the object that is already there and work with it futher.
The example:
boolean reidentifyNeed = false;
try {
DofinService.getEntityManagerThreadLocal().getTransaction().begin();
DofinService.getEntityManagerThreadLocal().persist(entity);
try {
DofinService.getEntityManagerThreadLocal().getTransaction().commit();
//if commit is successfull
entityIdInDB = (long) entity.getId();
DofinService.getEntityManagerThreadLocal().clear();
} catch (Exception ex) {
logger.error("Error committing " + entity.getClass().getSimpleName() + " in DB. Possibly duplicate object. Will try to re-identify object. Error: " + ex.toString());
reidentifyNeed = true;
}
if(reidentifyNeed){
//need clear entityManager, because if duplicated object was persisted then during *select* an object flush() method will be executed and it will thrown ConstrainViolationException
DofinService.getEntityManagerThreadLocal().clear();
CheckSimilarObject checkSimilarObject = new CheckSimilarObject();
long objectId = checkSimilarObject.checkObject(dofinObject);
logger.warn("Re-identifying was done. EntityId = " + objectId);
entityIdInDB = objectId;
}
} catch (Exception ex) {
logger.error("Error persisting and commiting object: " + ex.toString());
}

Hibernate : Lock a row while update, so user's don't retrieve a counter from it

I am working on a Spring-MVC project in which I am using Hibernate as the ORM, PostgreSQL as our DB and in one of our Objects(GroupCanvas), we have a number which is incremented everytime when user takes some action, and then the GroupCanvas object is updated in DB, and it should be unique.
THe problem we have currently is, if multiple users take action in front-end, some of them are getting duplicate numbers. We are working on fixing this now, so later we can implement a sequence and are assured that the numbers are unique.
How can I ensure that when I am updating the row, other users are waiting till the row is updated. I tried LockMode.Pessimistic_write, and a few others, none helped.
Code :
#Override
public void incrementNoteCounterForGroupCanvas(int canvasId) {
Session session = this.sessionFactory.getCurrentSession();
session.flush();
Query query = session.createQuery("update GroupCanvas as gc set gc.noteCount=gc.noteCount+1 where gc.mcanvasid=:canvasId");
query.setParameter("canvasId",canvasId);
query.executeUpdate();
session.flush();
}
#Override
public GroupCanvas getCanvasById(int mcanvasid) {
Session session = this.sessionFactory.getCurrentSession();
session.flush();
return (GroupCanvas) session.get(GroupCanvas.class, mcanvasid,LockMode.PESSIMISTIC_WRITE);
}
Both methods are in DAO, which has #Transactional annotation, and annotation present in service layer as well.
Thank you.
Looking at the method you have posted the usage if the 'LOCKING' technique is not quite correct. In order for a lock to end up with the result you are looking for the sequence of actions should be similar to the ones below (in the nutshell it is similar to the Double-Checked Locking but implemented using DB locks - https://en.wikipedia.org/wiki/Double-checked_locking).
Start the transaction (eg #Transactional annotation on your service method)
Retrieve entity from database with the PESSIMISTIC_WRITE lock mode (make sure to indicate hibernate that fresh copy should be read instead of the one stored in session cache)
If required check the current value of the target field if it meets your invariants
Perform the change/update on the field (eg, increment the value of a field )
Save the entity (and make sure to flush the value to the DB if you do not want to wait for the auto-flush)
Commit the transaction (done automatically when using #Transactional)
The essential difference of this sequence when compared with the posted method is that the update of the property value is performed while your transaction holds a lock on the target entity/db row, hence preventing other transactions from reading it while your update is in progress.
Hope this helps .
UPDATE:
I believe something like the code snippet bellow should work as expected :
#Transactional
#Override
public void incrementNoteCounterForGroupCanvas(int canvasId) {
final Session session = this.sessionFactory.getCurrentSession();
final GroupCanvas groupCanvas = session.get(GroupCanvas.class, canvasId,LockMode.PESSIMISTIC_WRITE);
session.refresh(groupCanvas);
groupCanvas.setNoteCount(groupCanvas.getNoteCount()+1);
session.saveOrUpdate(groupCanvas);
session.flush();
}

Should I commit hibernate transaction before calling session.evict(obj)

When should I commit a transaction in hibernate after saving it. Is it before or after I call session.evict(obj). Currently my code looks like this(only required parts).
Session session = connector.getSession();
Transaction tx = session.beginTransaction();
try {
Criteria crit = session.createCriteria(ST_CODE_SETTINGS_STORE.class).add(Restrictions.eq("TYPE", "issueno"));
List<ST_CODE_SETTINGS_STORE> ls = crit.list();
if (ls.size() < 1) {
session.save(st_code_settings_store);
session.evict(st_code_settings_store);
msg = "insert";
}
else {
Long Id = null;
ST_CODE_SETTINGS_STORE st_code_settings_store1 = ls.get(0);
Id = st_code_settings_store1.getCODE_ID();
Object o = session.get(ST_CODE_SETTINGS_STORE.class, Id);
ST_CODE_SETTINGS_STORE update = (ST_CODE_SETTINGS_STORE) o;
session.update(update);
}
tx.commit();
} catch (Exception e) {
if (tx != null) {
tx.rollback();
}
System.out.println("Error: " + e.getMessage());
connector.printStack(e);
throw e;
} finally {
session.close();
}
Sometimes if I commit after evicting, the data does not get saved in the database. Is it the right way to code??
The method evict() removes a single object from Session cache. So
before you call evict() the object should be there in the Session
cache. Therefore if you save the object first time, you have to save
the object via Session.save(object). Subsequent update calls should
follow through session.saveOrUpdate(object) or session.update(object)
before calling evict() to remove the loaded object from the cache.(reference )
From Hibernate Docs
Ending a Session usually involves four distinct phases:
flush the session
commit the transaction
close the session
handle exceptions
Do not use the anti-patterns session-per-user-session or
session-per-application (there are, however, rare exceptions to this
rule). Some of the following issues might also arise within the
recommended patterns, so ensure that you understand the implications
before making a design decision:
A Session is not thread-safe. Things that work concurrently, like HTTP
requests, session beans, or Swing workers, will cause race conditions
if a Session instance is shared. If you keep your Hibernate Session in
your HttpSession (this is discussed later in the chapter), you should
consider synchronizing access to your Http session. Otherwise, a user
that clicks reload fast enough can use the same Session in two
concurrently running threads.
An exception thrown by Hibernate means
you have to rollback your database transaction and close the Session
immediately (this is discussed in more detail later in the chapter).
If your Session is bound to the application, you have to stop the
application. Rolling back the database transaction does not put your
business objects back into the state they were at the start of the
transaction. This means that the database state and the business
objects will be out of sync. Usually this is not a problem, because
exceptions are not recoverable and you will have to start over after
rollback anyway.
The Session caches every object that is in a
persistent state (watched and checked for dirty state by Hibernate).
If you keep it open for a long time or simply load too much data, it
will grow endlessly until you get an OutOfMemoryException. One
solution is to call clear() and evict() to manage the Session cache,
but you should consider a Stored Procedure if you need mass data
operations.

hibernate multiple threads prevent multiple save(), JTA necessary?

I'm using a hibernate session per request model for my web application. My jdbc transaction begins at the beginning of each web request and commited at the end.
// Non-managed environment idiom
Session sess = factory.openSession();
Transaction tx = null;
try {
tx = sess.beginTransaction();
// do some work
...
tx.commit();
}
catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // or display error message
}
finally {
sess.close();
}
I'm faced with the problem where I am testing for existence of an entity (A) based on several parameters and doing an insert only if it doesn't exist.
public synchronized myMethod(param1, param2) {
MyEntityA entity = MyEntityADAO.findEntity(param1, param2)
if (entity == null) {
entity = .../create entity
MyEntityADAO.save(entity);
}
}
the problem is that synchronization does not help because the call to MyEntityADAO.save() does not actually write to the database when the currently running thread exits the method and releases the lock, the write to the database occurs after the transaction is commited which is generally what I need for my application except for a few scenarios. The code above causes multiple records saved with same parameters in a multithreaded environment.
I've tried to execute the save code in its own new session and transaction:
public synchronized myMethod(param1, param2) {
MyEntityA entity = MyEntityADAO.findEntity(param1, param2)
if (entity == null) {
entity = .../create entity
Session session = HibernateUtil.createSession();
MyEntityADAO.save(entity);
Transaction t = session.beginTransaction();
}
}
the above causes problems with 2 open sessions loading the same collection with hibernate in some instances.
Should I enclose every DAO call in its own transaction and use transaction propagation with JTA? Is there a way to avoid JTA? Is it alright to commit transaction associated with the main session after the call to MyEntityADAO.save() and call beginTransaction on the main session right after and have the transaction commited at the end of the request as it does now?
The coherence of the data in database should not be compromised by doing only some part of an atomic change in its own transaction. And although some synchronization might work on your environment, if you need to cluster your app, or if several applications acces the database, it won't solve the problem.
What you should do is to put a unique constraint in the database on [param1 - param2]. That will cause one of the two transactions to rollback if there is a race condition.
If you choose to still isolate the check/insert code in its own transaction (because it's not a problem if that succeeds and the outer transaction fails), I don't see how JTA would be a problem. Supposing you're using EJBs or Spring, just put this method in its own EJB/bean, and mark the method as transactional, with the REQUIRES_NEW propagation.
The code would thus look like this:
// some code
Long id = myBean.checkIfExistOrCreate(param1, param2); // this methos call starts a new transaction
// now we're sure that the entity exists. Load it in the current session.
MyEntity e = em.find(MyEntity.class, id);
If you can't synchronize checkIfExistOrCreate, then try calling it, catch any exception that it could throw, and retry calling it:
Long id = null;
try {
id = myBean.checkIfExistOrCreate(param1, param2);
}
catch (Exception e) { // a well-defined exception would be better
// the transaction roled back: retry
id = myBean.checkIfExistOrCreate(param1, param2);
}
// now we're sure that the entity exists. Load it in the current session.
MyEntity e = em.find(MyEntity.class, id);
The solution that worked for me and my particular app requirements trying to avoid JTA and nested transactions:
Using ManagedSessionContext because org.hibernate.context.ThreadLocalSessionContext will close and create a new session for each transaction. You will run into problems with entities that have collections associated if you load those entities in multiple open sessions (when you will create multiple transactions for one request).
I open a hibernate session and bind it to the context in the beginning of my web request
Any service layer method that needs test for existence prior to insert is marked synchronized, the global transaction is commited with the insert statement and a new transaction is started
At the end the request the transaction bound to the session is commited
public synchronized myMethod(param1, param2) {
MyEntityA entity = MyEntityADAO.findEntity(param1, param2)
if (entity == null) {
entity = .../create entity
MyEntityADAO.save(entity);
HibernateUtil.getCurrentSession().getTransaction().commit();
HibernateUtil.getCurrentSession().getTransaction().begin();
}
}
I know its ugly and will not work for everybody in every scenerio, but after doing a very intense search on transaction management, isolation levels, locking, versioning that is the only solution I have found that worked for me. I am not using Spring, and I'm not using a Java EE container, using Tomcat 6.

Working of hibernate. Is it necessary to use transaction every time? Will it cause any issue if i dont use it while retieving data?

I wrote the below code to retrieve data from the data base, in that do we need to begin the transaction? Because it runs without any issue. Is it necessary to use it every time? Will it cause any problem in future without that?
public static Student getStudentById(long id) {
Session session = null;
Student student = null;
//Transaction transaction=null;
try {
session = HibernateUtil.getSessionFactory().openSession();
//transaction = session.getTransaction();
//transaction.begin();
/**
* names in the query should match the related class name and variable names.
*/
Query query = session.createQuery("from Student where studentId = :id");
query.setLong("id", id);
student = (Student) query.uniqueResult();
//transaction.commit();
} catch (HibernateException e) {
//transaction.rollback();
e.printStackTrace();
} finally {
session.close();
}
return student;
}
According to hibernate documentation Database, or system, transaction boundaries are always necessary. No communication with the database can occur outside of a database transaction (this seems to confuse many developers who are used to the auto-commit mode). Always use clear transaction boundaries, even for read-only operations. Depending on your isolation level and database capabilities this might not be required, but there is no downside if you always demarcate transactions explicitly. Certainly, a single database transaction is going to perform better than many small transactions, even for reading data.
You can refer hibernate documentation here.
No, you don't need to use transaction unless and until you are planning to persist the data inside the db. And In your question you are not persisting the date you are just fetching the records from the db. So here not mandatory to use transaction.

Categories

Resources