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

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.

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 causes dirty collection exception

I'm trying to reattach to the session an object from the HTTP session that was originally retrieved from the DB, I do this by calling session.lock(object, LockMode.None) and even though lock does not cascade this works all right for me because it does not push updates to the DB like merge does (the lock is required to open a detail view in a pop-up and the actual saving would occur later on the main window). Now to my surprise I have found that if my entity has a one to many relation any changes on that collection would cause a HibernateException "Reassociated object has dirty collection".
How am I supposed to reattach objects to the session without updating the DB or discarding the changes on the object then?
Here is the situation as code
EntityA t = createAnEntityA();
Session sess = factory.openSession();
sess.beginTransaction();
sess.save(t);
sess.getTransaction().commit();
sess.close();
// t is now saved on the DB but in dettached state
// change a simple property
sess = factory.openSession();
sess.beginTransaction();
t.setPropertyB("B");
sess.lock(t, LockMode.NONE);
// t is attached again, you won't get LazyInitializationException
// by calling its properties, although you have to be careful
// because the reattachment does not cascade to children
sess.getTransaction().commit();
sess.close();
// no updates went to the DB because setPropertyB was called
// when t was still dettached
// now change a collection
EntityC c = createAnEntityC();
t.getCollectionPropertyC().add(c);
sess = factory.openSession();
sess.beginTransaction();
sess.lock(t, LockMode.NONE);
// Exception is thrown :-(
sess.getTransaction().commit();
sess.close();
I'm afraid currently it's not possible without hitting DB.
Apparently, the exception is happening because of lock command.
There's a Jira reporting this behavior.
https://hibernate.atlassian.net/browse/HHH-511
It has 2 patches to fix the problem. You could try those patches.
But if you problem is only Lazy loading of collection, you could consider using Open Session In View pattern. (maybe not the best pattern, but it can work for your case)

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.

Hibernate pattern for transaction reuse

I have a base method that I'm writing in order to not repeat the same hibernate session/transaction logic over and over. It's fairly simple, but there's a specific issue that I'm not sure can be solved with this approach.
Imagine that you have a User entity and a Permission entity. If a request is made to save a user along with its matching permissions, then I think that it would make sense to perform both operations in a single transaction, since being able to save only one of those entities could be considered data corruption. For example, failing to save the user's permissions would warrant a rollback on previously inserted user data.
I made the following method to allow generic hibernate operations that could work with the current transaction if it were necessary, although I now think that in its current form it won't work since calling session.beginTransaction(); will probably return a new transaction even if the previous hasn't been commited (is this the case?). Suppose that I changed it in order to have it return the current session and transaction if it was specified that there would be more operations for the current transaction, do you think it would work? Would it be advisable to do something like this, or would you recommend a change of approach? Thanks
protected <T> void baseOperation(Class<T> entityClass, List<T> instances, BaseHibernateDAO.Operations operation, boolean isLastOperation) throws Exception
{
Session session = null;
Transaction transaction = null;
boolean caughtException = false;
//get session from factory
session = HibernateSessionFactory.getSession();
try
{
//get current transaction
transaction = session.beginTransaction();
for (Object instance : instances) //perform operation on all instances
{
log.debug(String.format("Will perform %s operation on %s instance.", operation.name(), entityClass.getName()));
switch (operation) //perform requested operation
{
case SAVE:
session.save(instance);
break;
case UPDATE:
session.update(instance);
break;
case SAVEORUPDATE:
session.saveOrUpdate(instance);
break;
case DELETE:
session.saveOrUpdate(instance);
break;
}
log.debug(String.format("%s operation on %s instance was succesful.", operation.name(), entityClass.getName()));
}
session.flush(); //synchronize
if (isLastOperation) //if this is the last operation of the transaction
{
transaction.commit();
log.debug("Transaction commited succesfully.");
}
}
catch (Exception e) //error occurred
{
caughtException = true;
//roll-back if transaction exists
if (transaction != null)
{
transaction.rollback();
}
//log and re-throw
log.error("An error occurred during transaction operation.", e);
throw e;
}
finally //cleanup tasks
{
if (isLastOperation || caughtException) //close session if there are no more pending operations or if an error occurred
{
HibernateSessionFactory.closeSession();
}
}
}
"Advisable" would be to stop trying to rewrite code that's already been written, debugged, dragged through the mud, debugged more, and deployed thousands of times. I.e, the issues and considerations you're encountering have been encountered and overcome before, and the solutions are proven. Further, having been extensively used and improved, they require much less effort to use than what you're putting into your custom solution. Check out Spring's Hibernate support, especially "Implementing DAOs based on plain Hibernate 3 API" and "Declarative transaction demarcation". For further reading, there's a whole chapter on transaction management.
I have a sample project on github where you can see a very simple example of using Spring to manage Hibernate Sessions and transactions in the context of a webapp (using Spring MVC).
Update: For those who come along later, so they don't have to dig through the comments:
There are three general ways to use Spring's transaction handling: declaratively defining which methods are transactional with XML, declaratively annotating methods as #Transactional, or programmatically using TransactionTemplate.

concurrency (stale data) problem in JPA

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.

Categories

Resources