I'm running certain process that inserts new elements in DB from a for loop. I'm using JPA (Eclipselink), and sometimes there's a problem with the transation status. This is the case:
One of the INSERTS doesn't work (Primary Key duplicated)
After that, all the inserts will fail (Exception Description: Transaction is currently active).
for (Element l:e){
try{
//Should I add: if(!em.getTransaction().isActive())
em.getTransaction().begin();
em.createNativeQuery("INSERT INTO...").executeUpdate();
em.getTransaction().commit();
}
catch(Exception ep)
{
//right now I don't do anything here
}
}
I get that what is happening is that, since the commit in 1) didn't work, the transaction didn't finish, so the next em.getTransaction().begin() will find an already active transaction.
I have two ideas:
A) Before em.getTransaction().begin(), check if the transcation is active, and only if it is not, call begin(); otherwise, create query and commit.
B) Do something within the catch block. And here's my doubt... Should I call clear()? flush()? close()?
Which one looks better?
Thanks!
An exception thrown by ElementManager.Query does not rollback the active transaction. I see two options here:
Rollback the transaction by yourself within the catch clause with use of em.getTransaction().rollback().
Instead of inserting data with query use the preferred way based on EntityManager.persist whose exceptions cause an automatic rollback (in your particular case this will lead to javax.persistence.EntityExistsException).
Related
in updateUser method: If an exception occurs when calling one of the macro services (like : updateUserContact,updateAccountContact), the updateUser operation must be rollback.
How do I perform a transaction operation to create, update and delete manually in Java?
In the creation method, when an event exception occurs, I delete the related records.
But I do not know what to do in the update and delete.
if invoke userContactStub.grpcUpdate has Exception , i must rollback userAcount.
Does anyone have any suggestions about the rollback transaction in the update ?
I use jpa, grpc(To connect micro services),springBoot.
each micro service has a datasource.
//updateUser
AdminUser adminUser = findById();
adminUser.setFirstName(adminUserModel.getFirstName());
adminUser.setLastName(adminUserModel.getLastName());
adminUser.setPassword(PasswordEncoderGenerator.generate(adminUserModel.getPassword()));
adminUser.setUsername(adminUserModel.getUsername());
adminUser.setDateOfBirth(CalendarTools.getDateFromCustomDate(adminUserModel.getDateOfBirth()));
adminUser.setGender(etcItemService.findByIdAndCheckEntity(adminUserModel.getGender_id(), GenderEnum.class,null,true));
adminUser = adminUserRepository.save(adminUser);
//update userAcount For Admin
//call grpcUpdate
this.userAcountStub.grpcUpdate(createRequestModel);
//update UserContact For Admin
//call grpcUpdate
this.userContactStub.grpcUpdate(createRequestModel);
adminUserModel.setId(adminUser.getId());
return adminUserModel;
What framework are you using? Are u using JPA?
Assume you are using JPA, you don't have to worry about it. JPA shell pretty much guarantee your data integrity if exception occurred (usually it rollback manually).
However I am not sure about how to rollback a database translation if one of the micoservice you called has thrown an exception.
if you are using JPa (hibernate?), you can simply add #Transactional annotation for rollback on top of create , update methods etc. İt handles this job and solves the problem.
try(Connection conn = DriverManager.getConnection(dbURL,dbUser,dbPassword);){
conn.setAutoCommit(false);
// perform operations such as insert, update, delete here
// ..
// if everything is OK, commit the transaction
conn.commit();
} catch(SQLException e) {
// in case of exception, rollback the transaction
conn.rollback();
}
I have the following code (simplified for the sake of the question)
EntityManager em = EMF.get().createEntityManager();
TypedQuery<T> query = em.createQuery...
for(T result : em.getResultlist()) {
try {
em.getTransaction().begin();
// do some stuff, update the T object
em.getTransaction().commit();
} catch(Exception e) {
// something has gone wrong, rollback the current transaction
if(em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
}
}
em.close();
I am using JPA EclipseLink.
Basically, I want to update a set of tasks, take action and update their statuses. Sometimes the task action fails and I need to revert the change.
This works perfectly fine UNTIL something goes wrong with one of the transactions and the roll back is called. At that point any subsequent transaction commit IS NOT performed, ie the database is not updated.
I read "on rollback all objects managed are detached". I guess this is where the problem is... if correct how could I implement the desired behaviour?
Any help would be vastly appreciated!
I read that JPA caches SQL instructions to improve performance:
JPA providers like Hibernate can cache the SQL instructions they are
supposed to send to the database, often until you actually commit the
transaction. For example, you call em.persist(), Hibernate remembers
it has to make a database INSERT, but does not actually execute the
instruction until you commit the transaction.
I have a Java EE 6 application deployed to a Glassfish cluster with two instances. In the application there is a race condition where two Singletons do some expensive queries and then cache the results in a database table. They're doing the same work and trying to write the same record, so I sometimes get an exception:
java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (SOMESCHEMA.SOMETABLE_PK) violated
I decided the easiest way to deal with this would be to catch and ignore the exception:
// In a EJB with container-managed transactions.
public Entity getExpensiveEntity(int entityId) {
Entity entity = entityManager.find(Entity.class, entityId);
if (entity == null) {
try {
result = expensiveQueries();
entityManager.persist(result);
entityManager.flush();
} catch (SQLIntegrityConstraintViolationException ex) {
// The other instance already created the result, so get it.
result = jpa.find(result.getId());
}
}
return result;
}
I think the call to flush is necessary because otherwise the SQLIntegrityConstraintViolationException won't occur until the transaction ends somewhere up the EJB call stack, past catching and ignoring. Am I correct, is this a valid use case for flush? Is there a better way to handle this?
Reference
Correct use of flush() in JPA/Hibernate
A beginner’s guide to flush strategies in JPA and Hibernate
JPA and CMT -- Why Catching Persistence Exception is Not Enough?
JPA - create-if-not-exists entity? (see top-voted answer)
JPA Query javadoc (see http://docs.oracle.com/javaee/6/api/javax/persistence/Query.html#executeUpdate()) says
int executeUpdate() Execute an update or delete statement.
Returns: the number of entities updated or deleted
Throws:
IllegalStateException - if called for a Java Persistence query language SELECT statement or for a criteria query
TransactionRequiredException - if there is no transaction
QueryTimeoutException - if the statement execution exceeds
the query timeout value set and only the statement is rolled back
PersistenceException - if the query execution exceeds
the query timeout value set and the transaction is rolled back
What's the difference between rolling back a statemente and a transaction? I mean, rolling back a transaction is pretty obvious, it will set the transaction to rollback and all the operations will be undone. But if the statement is rolled back (since it's an update/delete/insert operation), won't the whole transaction be also rolled back in this situation?
Was this QueryTimeoutException designed to be caught and allow the user to retry on timeout without affecting the transaction?
[A QueryTimeoutException is] thrown by the persistence provider when a query times out and only the statement is rolled back. The current transaction, if one is active, will be not be marked for rollback. [QueryTimeoutException]
The QueryTimeoutException is a specialization of the PersistenceException.
[A PersistenceException is] thrown by the persistence provider when a problem occurs. All instances of PersistenceException except for instances of NoResultException, NonUniqueResultException, LockTimeoutException, and QueryTimeoutException will cause the current transaction, if one is active and the persistence context has been joined to it, to be marked for rollback. [PersistenceException]
Therefore if a query times out don't matter which it will not cause a rollback of the transaction by default. That's why you have to do it always explicitly. For example if you want to rollback the transaction regardless which PersistenceException occurs.
catch(PersistenceException e) { ... tx.rollback(); ... }
But sometimes it makes sense to continue a transaction even a statement was not successful and a QueryTimeoutException occurs.
An example scenario is the time out during the execution of a statement which only persist an additional log record. Depending on your use case the time out of executing the log statement could not be critical otherwise it's critical if the core business process for example persisting the order times out. Therefore you don't want that the failed log statement will affect the persisting of the order. On the other hand if the persisting of the order fails the persisting of the log record should be roll backed. So you can always decide which query time out should cause a rollback.
A schematically example would be
...
try {
...
queryNonCritical.execute(...);
}
catch(QueryTimeoutException e) {
// not critical move on
...
}
...
try {
...
queryCritical.execute(...);
}
catch(QueryTimeoutException e) {
...
tx.rollback();
...
}
...
A(){
con.begin;
.........
.........
B();
........
........(con.rollback;)
con.commit;
}
B{
con.begin;
.......
.......
con.commit;
}
In the above code, I begin a new DB transaction at A(). It executes some transaction successfully. After that B() starts executing and it also executes some transaction successfully and now the control returns to A(). At this point some exception occurs and I do a rollback. I would like to know whether the transaction which succeeded in B() will rollback or not.
The short answer, no. The long answer is as follows.
Support for nested transactions in Java depends on various variables at play.
Support for Nested transactions in JTA
First and foremost, if you are using JTA, it is upto to the Transaction Manager to support nested transactions. Any attempt to begin a transaction may result in a NotSupportedException being thrown by a Transaction Manager (that does not support nested transactions) if there is an attempt to start a new transaction in a thread that is already associated with a transaction.
From the Java Transaction API 1.1 specification:
3.2.1 Starting a Transaction
The TransactionManager.begin method starts
a global transaction and associates
the transaction context with the
calling thread. If the Transaction
Manager implementation does not
support nested transactions, the
TransactionManager.begin
methodthrowsthe NotSupportedException
whenthe calling thread is already
associated with a transaction.
Support for Nested transactions in JDBC
JDBC 3.0 introduces the Savepoint class, which is more or less similar to the concept of savepoints in the database. Savepoints have to be initialized using the Connection.setSavepoint() method that returns an instance of a Savepoint. One can roll back to this savepoint at a later point in time using the Connection.rollback(Savepoint svpt) method. All of this, of course, depends on whether you are using a JDBC 3.0 compliant driver that supports setting of savepoints and rolling back to them.
Impact of Auto-Commit
By default, all connections obtained are set to auto-commit, unless there is a clear deviation on this front by the JDBC driver. This feature, if enabled, automatically rules out the scope of having nested transactions, for all changes made in the database via the connection are committed automatically on execution.
If you disable the auto-commit feature, and choose to explicitly commit and rollback transactions, then committing a transaction always commits all changes performed by a connection until that point in time. Note, that the changes chosen for commit cannot be defined by a programmer - all changes until that instant are chosen for commit, whether they have been performed in one method or another. The only way out is to define savepoints, or hack your way past the JDBC driver - the driver usually commits all changes performed by a connection associated with a thread, so starting a new thread (this is bad) and obtaining a new connection in it, often gives you a new transaction context.
You might also want to check how your framework offers support for nested transactions, especially if you're isolated from the JDBC API or from starting new JTA transactions on your own.
Based on the above description of how nested transaction support is possibly achieved in various scenarios, it appears that a rollback in your code will rollback all changes associated with the Connection object.
That looks like poor transaction management i'm afraid. It would be good if you handle the commits and rollbacks from the callers to A and B instead.
A()
{
//business code A
B();
//more business code A
}
B()
{
//business code B
}
DoA()
{
try
{
con.begin();
A();
con.commit();
}
catch(Exception e)
{
con.rollback();
}
}
DoB()
{
try
{
con.begin();
B();
con.commit();
}
catch(Exception e)
{
con.rollback();
}
}
As per your code, in A() you are starting transaction. Then jump to B() where you start transaction again, which in turn will commit all previous transaction. Then at end of B(), transaction is explicitly committed. At this point, all your code is committed. Now the code return to A() and remaining code is processed. In case of exception, only this part after B() call will be rolled back.
You can use Java.SQL's built-in SavePoint function in Postgres 8 and up.
Connection conn = null;
Savepoint save = null;
DatabaseManager mgr = DatabaseManager.getInstance();
try {
conn = mgr.getConnection();
proc = conn.prepareCall("{ call writeStuff(?, ?) }");
//Set DB parameters
proc.setInt(1, stuffToSave);
proc.setString(2, moreStuff);
//Set savepoint here:
save = conn.setSavepoint();
//Try to execute the query
proc.execute();
//Release the savepoint, otherwise buffer memory will be eaten
conn.releaseSavepoint(save);
} catch (SQLException e) {
//You may want to log the first one only.
//This block will attempt to rollback
try {
//Rollback to the Savepoint of prior transaction:
conn.rollback(save);
} catch (SQLException e1) {
e1.printStackTrace();
}
}
When a SQL-exception occurs, the current transaction is rolled-back to the SavePoint, and the remaining transactions may occur. Without the roll-back, subsequent transactions will fail.