I have a web service method that uses EclipseLink JPA. This method basically inserts data into the database. Something like :
public void insert(parameters....)
{
em.getTransaction().begin();
em.persist(employee);
em.getTransaction().commit();
// em.close();
}
Now at the client end, i have a loop that performs inserts per iteration :
for(......)
{
websrvice.insert(parameters...)
}
After the first iteration, i get a "No transaction is currently active" exception.
Then i thought of putting a Thread.sleep(2000) after each iteration which solves the problem.
So intuitively, this could be because another transaction is trying to commit before the latter is completed which causes the error. But then again i think, why would the second insert even start when the first isn't completed ? confused.
You shouldnt close the entity manager at the end of your transaction.
Remove this line:
em.close();
http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#close()
"Close an application-managed entity manager. After the close method has been invoked, all methods on theEntityManager instance and anyQuery and TypedQuery objects obtained from it will throw the IllegalStateException except forgetProperties, getTransaction,and isOpen (which will return false)"
Edit:
Try using the transaction returned by the first getTransaction call, rather than invoking this method twice:
EntityTransaction transaction = em.getTransaction();
transaction.begin();
em.persist(employee);
transaction.commit();
Related
The following SQL if run in MSSQL will insert the 1st and 3rd rows successfully:
BEGIN TRAN
INSERT ... -- valid data
INSERT ... -- invalid data (e.g. over column width)
INSERT ... -- valid data
COMMIT
Even though the second row fails within the transaction, you can still see the two rows with some valid data after the commit in the table.
However, when trying something similar in Hibernate, it rollbacks the whole transaction. Is there a way to tell Hibernate not to rollback on failed rows and commit the rest as same as how MSSQL does it?
e.g.
EntityTransaction transaction = em.getTransaction();
transaction.begin();
em.persist(new MyEntity("good"));
em.persist(new MyEntity("too long"));
em.persist(new MyEntity("good"));
transaction.commit();
This is not possible within the same transaction. Hibernate simply doesn't allow this. An error in a statement leads to an exception, which Hibernate cannot recover from. From the manual:
If the JPA EntityManager or the Hibernate-specific Session throws an exception, including any JDBC SQLException, you have to immediately rollback the database
transaction and close the current EntityManager or Session.
Certain methods of the JPA EntityManager or the Hibernate Session will not leave the Persistence Context in a consistent state. As a rule of thumb, no exception thrown by Hibernate can be treated as recoverable. Ensure that the Session will be closed by calling the close() method in a finally block.
Now this is a restriction (design decision) of Hibernate and not of the underlying JDBC or database stack. So what you want is perfectly possible using JDBC directly. If it is really important for you to get that behaviour, you might consider using JDBC calls for this section of the code. There you can do it exactly like in the SQL client: open transaction, issue statements, catching any exceptions manually and "ignoring" them, and at the end committing the transaction.
Example code:
Session session = em.unwrap(Session.class);
session.doWork(connection -> {
// manual commit mode
connection.setAutoCommit(false);
executeInsertIgnoringError(connection, new Object[]{123, null, "abc"});
executeInsertIgnoringError(connection, new Object[]{....});
...
connection.commit();
});
private void executeInsertIgnoringError(Connection connection, Object[] values) {
try (PreparedStatement stmt =
connection.prepareStatement("INSERT INTO MY_ENTITY VALUES (?, ?, ?, ...)")) {
for (int i = 0; i < values.length; i++) {
// PreparedStatement is indexed from 1
stmt.setObject(i+1, values[i]);
}
stmt.executeUpdate();
} catch (Exception e) {
log.warn("Error occurred, continuing.");
}
}
The way i did it is to divide your logic into diferent functions, and open the transaction inside the persisting function instead of the main one.
The main problem I see in your code is that you're defining a block transaction insead of opening a transaction for each operation.
Here's my snippet:
persistEntity(new MyEntity("good"));
persistEntity(new MyEntity("bad"));
persistEntity(new MyEntity("good"));
...
private void persistEntity(MyEntity entity){
EntityTransaction transaction = em.getTransaction();
transaction.begin();
em.persist(entity);
transaction.commit();
}
This way it will rollback just for the bad entity and keep going with the other. You can also add a try catch inside the persistEntity method, if you want to log the exception.
Fun fact, If you're using Spring you could create another #Component for the persist operations and only add #Transactional to the persisting method, this way you don't have to manage the transactions yourself.
Don't do so, that is idiomatically wrong, at first just review the real scope of your transactions.
You could write the code to run one statement at a time with autocommit on and not use #Transactional... Then perhaps catch any exceptions and throw them away as you go. But pretty much everything in that sentence is troublesome to even think about as a responsible developer and it would affect your entire app. Flavius's post would be a little more granular in doing something similar with explicitly smaller transactions and is a good way to go about it too.
As others have been commenting it's not a long term great plan and goes against so many ways to write programs correctly and the benefits and purpose of transactions. Perhaps if you plan to only use this as a one off data ingestion plan you could but again be very wary of using these patterns in a production grade app.
Having been sufficiently alarmed, you can read more about auto commit here and also be sure to read through the post links on why you probably shouldn't use it.
Spring JPA - No transaction set autocommit 'true'
You can do that by adding below property in hibernate config xml file
<property name="hibernate.connection.autocommit" value="true"/>
If you could use #Transactional annotation then
#Transactional(dontRollbackOn={SQLException.class, NOResultException.class})
Then I would suggest one some change in your code. It's better if you add your entities in a loop and catch exception on each transaction.
Is the below statement a valid one?
persist() also guarantees that it will not execute an INSERT statement if it is called outside of transaction boundaries
When I try the below code using persist; then the row is getting inserted without any transaction (It is commented out).
SessionFactory sessionFactory = new Configuration().configure("student.cfg.xml").buildSessionFactory();
Session session = sessionFactory.openSession();
//Transaction tran = session.beginTransaction();
/*
* Persist is working without transaction boundaries ===> why?
*/
Student student = new Student();
student.setFirstName("xxx");
student.setLastName("yyy");
student.setCity("zzz");
student.setState("ppp");
student.setCountry("###");
student.setId("123456");
session.persist(student);
//tran.commit();
session.flush();
session.close();
persist() also guarantees that it will not execute an INSERT statement if it is called outside of transaction boundaries
This statement is correct. When control returns from persist() back to your code, no INSERT statements have been executed. These statements are guaranteed to be deferred until session flushing. Note that persist() would be a pointless method if no insert happened ever.
AFAIK data is saving because of session.flush(), try after removing this, mostly you will get an error.
Hibernate persist
Diff. save & persist
I read this in doc:
persist() also guarantees that it will not execute an INSERT statement if it is called outside of transaction boundaries.
But when I try this code:
SessionFactory sessionFactory = new Configuration().configure("student.cfg.xml").buildSessionFactory();
Session session = sessionFactory.openSession();
Student student = new Student();
student.setFirstName("XXX");
student.setLastName("YYY");
student.setCity("ZZZ");
student.setState("PPP");
student.setCountry("XXX");
student.setId("NNN");
session.persist(student);
session.flush();
session.close();
The record is getting inserted. As you can see in the above code, I have not used any transaction. Then in that case according to the doc, the data should not be inserted in the DB right?
Transaction boundaries means any operation between Transaction tran = session.beginTransaction(); and tran.commit(); right?
Please let me know where am I making the mistake.
Regards,
If you remove session.flush() and session.close(), you'll observe that no insert statement was executed. The point of that guarantee is that the persist call itself won't execute any statements; it doesn't say anything about the implication on the behavior of flush and close. Indeed, flush flushes all persistent objects to the datastore.
This is old magic!
if (getTransactionIsolation()==Connection.TRANSACTION_NONE
|| getAutoCommit()==true)
The persist() ignores the transaction boundary!
Why? persist() does not know about support transactions or not. In second case its committed because he didnt expected to need a transaction.
Maybe a stupid question, but is it necessary to make a rollback on a transaction in the catch-block if the EntityManager.merge() throws an exception?
Or does the exception itself mean that the merge won´t work so that next time I run commit the previous changes that throwed the exception won´t apply?
Example:
public void setPerson(Person person) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("MyLib");
EntityManager em = emf.createEntityManager();
try {
if(!em.getTransaction().isActive()){
em.getTransaction().begin();
}
em.merge(person);
em.getTransaction().commit();
emf.getCache().evict(Person.class); // clear Person cache
} catch (Exception ex){
em.getTransaction().rollback(); // Is this necessary?
} finally {
em.close();
}
}
The answer depends on the details of em.merge(person) method and the implementation of your database driver.
If that method only performs one update statement, then the rollback is superfluous. If however it may run multiple updates, then it's not that clear.
I personally would keep it there
If the rollback is removed and your merge method errors our after some updates are done but others are not, then closing a database connection without explicit commit or rollback will either commit or rollback the transaction, depending on the driver implementation. According to the javadoc for java.sql.Connection, the behaviour depends on the implementation. Hence you may end up committing partial updates if you do not rollback yourself on error.
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.