JPA / #PostPersist #PostUpdate - transaction - java

I am currently working with #PostPersist and #PostUpdate, and in those triggers I am persisting additional entities. The question is, are those triggers in the same transaction and if not is it possible to force it ?
For me it works this way.
While I was looking through the logs the transaction isn't existing ( it's commited just before the trigger is launched ) which prevents me ( without REQUIRES_NEW on the persisting method from injected bean ) from saving the additional entities in database.
REQUIRED attribute is totally ignored, and MANDATORY attribute do not throw an exception.
Can it be the problem with JUnit ( since I am in the dev. phase and did not test the behavior on full env. ) ?
If extending the transaction on this triggers is not possible, how to ensure that if the rollback occurs before the #PostPersist and #PostUpdate, those operations also will be rollbacked.

If you're using Spring you could always register a TransactionSynchronization with your current transaction manager to be called back on events such as commits of your currently running transaction:
#PostPersist
void onPersist() {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
#Override
public void beforeCommit(boolean readOnly) {
// do work
}
});
}
}
A TransactionSynchronization also provides callbacks after a transaction has committed successfully and before/after a transaction completes.
If you need to inspect if the transaction was committed or rolled back, use afterCompletion(int status).
For details have a look at TransactionSynchronization's JavaDoc.

The firing of a PostPersist event does not indicate that the entity has done a successful commit. The transaction may be rolled back after the firing of the event but before the successful commit.
If you in the PostPersist get the entity manager used in the transaction and then do somehting like this:
#PostPersist
void someMethod() {
EntityManager em = null;
em = getEntityManagerUsedInTransaction();
EntityTransaction et = em.getTransaction(); // should return the current transaction
if (et.isActive() ) {
// do more db stuff
}
}
NB: I haven't tried this so it's only speculation (tho' I have used the lifetime event trigger extensively for other stuff).
I have to add that I don't think this is a good idea. Use the PostPersist to flag that other entities should be persisted and do it in another transaction.

Related

Rollback for doubly nested transaction bypasses savepoint

It's not exactly as the title says, but close to. Consider these Spring beans:
#Bean
class BeanA {
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = EvilException.class)
public void methodA() {
/* ... some actions */
if (condition) {
throw new EvilException();
}
}
}
#Bean
class BeanB {
#Autowired private BeanA beanA;
final int MAX_TRIES = 3;
#Transactional(propagation = Propagation.NESTED)
public void methodB() {
// prepare to call Bean A
try {
beanA.methodA();
/* maybe do some more things */
}
catch (EvilException e) {
/* recover from evil */
}
}
}
#Bean
class MainWorkerBean {
#Autowired private BeanB beanB;
#Autowired private OtherBean otherBean;
#Transactional(propagation = Propagation.REQUIRED)
public void doSomeWork() {
beanB.methodB();
otherBean.doSomeWork();
}
}
Important note: I'm using JDBC transaction manager that supports savepoints.
What I'm expecting this to do is, when EvilException is thrown, the transaction of the BeanA is rolled back, which with this setup happens to be the savepoint created by starting methodB. However, this appears to not be the case.
When going over with debugging tools, what I'm seeing is this:
When doSomeWork of MainWorkerBean starts, new transaction is created
When methodB starts, transaction manager properly initializes a savepoint and hands it to TransactionInterceptor
When methodA starts, transaction manager sees Propagation.REQUIRED again, and hands out a clean reference to the actual JDBC transaction again, that has no knowledge of the savepoint
This means that when exception is thrown, TransactionStatus::hasSavepoint return false, which leads to roll back of the whole global transaction, so recovery and further steps are as good as lost, but my actual code has no knowledge of the rollback (since I've written recovery for it).
For now, I can't consider changing BeanA's transaction to Propagation.NESTED. Admittedly, looks like it's going to allow me to have the more local rollback, but it's going to be too local, because as I understand it, Spring then will have two savepoints, and only roll back the BeanA savepoint, not BeanB one, as I'd like.
Is there anything else I'm missing, such as a configuration option, that would make internal transaction with Propagation.REQUIRED consider that it is running inside a savepoint, and roll back to savepoint, not the whole thing?
Right now we're using Spring 4.3.24, but I already crawled through their code and can't spot any relevant changes, so I don't think upgrading will help me.
As described in this bug ticket: https://github.com/spring-projects/spring-framework/issues/11234
For spring versions < 5.0, in the situation described, the global transaction is set to 'rollback-only'.
In this transaction I am processing several tasks. If an error should occur during a single task, I do not want the whole transaction to be rolled back, therefore I wrap the task processing in another transaction boundary with a propagation of PROPAGATION_NESTED.
The problem comes when, during task processing, calls are made to existing service methods defined with a transaction boundary of PROPAGATION_REQUIRED. Any runtime exceptions thrown from these methods cause the underlying connection to be marked as rollback-only, rather than respecting the current parent transaction nested propagation.
[...]
As of Spring Framework 5.0, nested transactions resolve their rollback-only status on a rollback to a savepoint, not applying it to the global transaction anymore.
On older versions, the recommended workaround is to switch globalRollbackOnParticipationFailure to false in such scenarios.
However, even for Spring5, I noticed when reproducing the problem, that the nested transaction may be rolled back, including all things done in the catch block of methodB(). So your recover code might not work inside methodB(), depending on what your recovery looks like. If methodA() was not transactional, that would not happen. Just something to watch out for.
Some more details to be found here: https://github.com/spring-projects/spring-framework/issues/8135

JPA EntityTransaction flush

I would like to flush all the entites stored in the current transaction to the database (without ending the current transaction via commit).
Do I need to check if the transaction is Active before doing so?
if (this.entityTransaction.isActive())
{
this.entityManager.flush();
}
Thank you
According to the javadoc of the method flush of eclipselink's EntityManager class:
https://www.eclipse.org/eclipselink/api/2.6/javax/persistence/EntityManager.html#flush()
void flush()
Synchronize the persistence context to the underlying database.
Throws:
TransactionRequiredException - if there is no transaction or if the entity manager has not been joined to the current transaction
PersistenceException - if the flush fails
So yes it looks like you need to check, other ways you might get an exception of type "TransactionRequiredException"

Does Spring MVC issue a commit after calling SpringStoredProcedure.execute()

Does Spring issue a commit to the database after each package call, or does it only commit after everything is done?
I am using Spring and Struts. In my controller class, I am calling many database packages with spring stored procedure in a DAO class. each call has its only method in the DAO class
My question is, will Spring commit only after all calls have been made from the controller class and the controller has finished, or will it commit after each execution of a SpringStoredProcedure?
my_package_getusers = SpringStoredProcedure.getStoredProcedureCompiled(getJdbcTemplate()
....
my_package_getusers.execute(params).get("result")
Spring utility classes from spring-jdbc don't make any commits. The responsibility for commiting transaction belongs to the spring-tx (transaction support.
This is usually made by placing #Transactional annotation on the method that should be run in transaction scope. So if you have method
#Transactional
public void doSomething(SomeClass arg) {...}
entering that method will create new transaction, and that transaction will be commited after leaving the transaction, if the transactional context doesn't exist. However, if that method is called from other method annotated with #Transactional, the parent transaction context will be used.
As alternative, you can use org.springframework.transaction.support.TransactionTemplate to gain more control over transactions (for example, launching separate transaction from transactional context).
Example:
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// do something in trancation)
}
});
Note, that on some databases, such as Oracle, you can commit transaction within stored procedure, which will persist all changes made in existing transaction, and the new transaction will follow.

In Spring, how will we make sure the atomicity of operations

In Spring how can we make sure that certain operations are always executed together. If any one of them fails, the entire transaction needs to be rolled back. I searched this a lot and found #Transactional(propagation = Propagation.REQUIRED) annotations and TransactionTemplate.execute() methods close to my problem. Kindly clarify and help.
Both #Transactional and TransactionTemplate ensure atomicity. #Transactional is for declarative transaction management, TransactionTemplate is for programmatic transaction management. You should choose one.
The idea of transaction propagation applies only to declarative transaction management and defines a transaction behaviour when it is executed in more than one method. Note that Propagation.REQUIRED is default for Transactional.propagation. It means Support a current transaction (that is if a transaction was already started in the calling method) or create a new one if none exists.
#Transactional(propagation = Propagation.REQUIRED
May solve your problem.
Suppose in your Impl there is a method Excecute.Inside Excecute method there are other M1(),M2(),M3(),M4(),M5() methods.
May be you trying to say if for M1(),M2().M3().M4() methods Db operation succedded and at last for M5() it throws some exception then M1() to M5() all db operation should be rollback
Execute(){
M1();
M2();
M3();
M4();
M5();
if(Any error in any methods transaction will be roll back).As single trasaction object is used for all methods i.e(M1 to M5) when #Transactional(propagation = Propagation.REQUIRED is used.
}
You can create a single method that delegates to the two database calls and annotate that with #Transactional, e.g.
#Transactional
public void atomicOperation(...) {
dbCall();
logicOperation();
}
If either one fails, e.g. an exception is thrown, the entire operation will rollback. The Spring Reference Documentation has dedicated a chapter for transactions, for example there is information about #Transactional settings and Transaction propagation.

hibernate jpa: Session is closed!

Application is based on Spring 2.5.5 and hibernate 3.2.0 GA.
I have the following method in my DAO that gets MessageEntities attached to the specified User:
public MessageEntity findByUserId(int userId) {
List<MessageEntity> result = (List<MessageEntity>) em.createNamedQuery(MessageEntity.Q_BY_USER_ID).setParameter("userId", userId).getResultList();
if (!result.isEmpty()) {
return result.get(0);
} else {
return null;
}
}
I need to call this method from my integration test to check whether system's behaviour is valid. As long as this method is not transactional, all I get is org.hibernate.SessionException: Session is closed!. The easiest way to avoid this is to mark findByUserId method with #Transactional(readOnly = true). But as I understand, transaction management should be the duty of service tier to avoid unnecessary transactions creation. So, my question is: how can I properly get away from SessionException?
You need to perform all your database actions within a transaction scope. As you identified its usually considered good design to let the service layer of your database model deal with transactions. The only constraint then becomes that you must invoke your service model to get within the transaction scope, which might be undesirable during test.
I would recommend to make use of the testing fascilites provided by spring. See 9.3.2.3 Transaction management
You could also manually create a transaction before testing your method, e.g., by
Session sess = factory.openSession();
Transaction tx = null;
// try catch
tx = sess.beginTransaction();
findByUserId(userId);
tx.commit();
tx.rollBack();
Put the following annotations on the top of your test class.
#RunWith(SpringJUnit4ClassRunner.class)
#Transactional
#ContextConfiguration(locations = "classpath:/META-INF/spring/applicationContext.xml")
Also I wouldn't worry about putting additional #Transactional in DAOs.
Spring usually checks to see if you are already in a transaction (with in the same thread) before it creates another.
"But as I understand, transaction
management should be the duty of
service tier to avoid unnecessary
transactions creation."
This is more of a design choice (Spring Roo for example violates this)
You can use this annotation on your controller method:
#Transactional(readOnly = true)

Categories

Resources