Rollback a #Transactional annotated method - java

Good day. The following code:
class A{
private B b;
#Transactional
public SomeResult doSomething(){
SomeResult res = null;
try {
// do something
} catch (Exception e) {
res = b.saveResult();
}
return res ;
}
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
class B{
public SomeResult saveResult(){
// save in db
}
}
As I understand, if there is an exception in the method doSomething the transaction isn't rolled back. And how to make that it rolled? and returned SomeResult

You shouldn't call Rollback programmatically. The best way, as recommended by the docs, is to use declarative approach. To do so, you need to annotate which exceptions will trigger a Rollback.
In your case, something like this
#Transactional(rollbackFor={MyException.class, AnotherException.class})
public SomeResult doSomething(){
...
}
Take a look at the #Transaction API and the docs about rolling back a transaction.
If, despite the docs recommendation, you want to make a programmatic rollback, then you need to call it from TransactionAspectSupport as already suggested. This is from the docs:
public void resolvePosition() {
try {
// some business logic...
} catch (NoProductInStockException ex) {
// trigger rollback programmatically
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
There may be a architecture mistake though. If your method fails and you need to throw an exception, you shouldn't expect it to return anything. Maybe you're giving too much responsibilities to this method and should create a separated one that only model data, and throws an exception if something goes wrong, rolling back the transaction. Anyway, read the docs.

get TransactionStatus using TransactionAspectSupport.currentTransactionStatus()
ect transaction manager to your bean try to invoke Rollback(DefaultTransactionStatus status) in transaction manager.
refer to spring documentation
You are strongly encouraged to use the declarative approach to
rollback if at all possible. Programmatic rollback is available should
you absolutely need it, but its usage flies in the face of achieving a
clean POJO-based architecture.

Related

Spring boot #Transactional not rolling back the database inserts

Need some help here, I'm not able to understand why my transactions are not getting rolled back in an event of exception.
I will try to put my code as close to as It is on the project (cannot share on the internet)
This is my Service
#Service
#SL4j
#Transactional(propagation = propagation.SUPPORTS, readOnly=true)
public class PublicationServiceImpl implements PublicationService{
#Autowired
private PartnerRepo partnerRepo;
#Autowired
private FlowRepo flowRepo;
#Autowired
private PubRepo pubRepo;
#Override
#Transactional(propagation = propagation.REQUIRED, rollbackFor=Exception.class)
public int save(Request request) {
try{
int pk_id_partner = partnerRepo.save(request);
int pk_id_flow = flowRepo.save(request);
String publicationCode = generatePubCode(request);
int publicationCode= pubRepo.save(pk_id_partner, pk_id_flow, request);
}
catch(Exception e){
log.error("Exception in saving");
}
return 0;
}
}
This is my Repository (example of 1 , all 3 repos follow same coding standards)
#Repository
#Slf4j
public class PartnerRepo implemets PartnerRepo{
#Autowired
private NamedParamaterJDBCTemplate namedParamaterJDBCTemplate;
//String Declarations .....
private MapSqlParameterSource sqlParameterSource;
#Override
public int save(Request request){
sqlParamatersSource = new MapSqlParameterSource();
//sqlParamatersSource.addValue(.....)
//sqlParamatersSource.addValue(.....)
//sqlParamatersSource.addValue(.....)
return executeQuery();
}
private int executeQuery(){
try{
keyHolder = new GenerateKeyHolder();
namedParamaterJDBCTemplate.update(getInsertQuery(), sqlParamaterSource , kekHolder, new String[]{"pk_id"})
return keyHolder.getKey().intValue();
}catch(Exception e){
log.error("Exception while saving");
return 0;
}
}
}
So the problem is , Consider there is an exception in the method generatePubCode(request); , ideally since I have used #Transactional at class level and method level , The previous 2 repo transactions () should be rolled back right? However it isn't happening, Even After the code is finished execution I can see the records in DB (Postgres DB v10).
Please help figure out this issue , Am I doing something fundamentally wrong ?
Please do let me know in case you need further information that might help here!
P.S: I have tried all permutations of #Transactional , nothing works : ONLY having this in the catch block works! TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
I wonder If its the right approach for a springBoot project
Thanks in advance for the help!
Edit: as per suggestion made the PublicationServiceSaverImpl.save() public
Best reagards,
Bhargav.
There are several things that break proper transactions in Spring
Your service method is private
You are catching and swallowing exceptions
private method
The fact that your PublicationServiceImpl save method is private basically makes the #Transactional on that method useless. As a private method cannot be proxied, no transactions will apply. Even if it would be public it wouldn't work as you are calling the method from within the same object, hence the transactionality of that method applies.
To fix, make your method public and call the save method from an other class (or make the actual method that is calling save have the proper #Transactional.
The fact that is doesn't work is due to the type op AOP being used, by default Spring will use proxies and this is a drawback of using proxy based AOP.
Another solution to make it work with private methods is to switch to full-blown AspectJ with either compile-time or load-time weaving of the classes. Both require additional setup and that can be tedious.
Catch and swallow exceptions
You have in both your repository as well as your service a try/catch block. Each of those catches and swallows the exceptions (they are logged but not re-thrown).
For transactions to work properly it needs to see the exceptions. The fact that you are catching and swallowing them, makes the transaction aspect not see them and instead of doing a rollback, do a commit. For the transaction aspect everything is ok because there was no exception.
To fix, remove either the try/catch or rethrow the exceptions.
Annotations in general never work on methods called from the same class because of how proxies are created in Spring.
It has nothing to do with #Transaction in particular but with the fact that your methods is private and called from the within same object.
Please make the method public and move the #Transactional method in a separate class annoted with #Service and called it from outside of the instance of the class
#Service
public class PublicationServiceSaverImpl {
#Transactional
**public** int save(Request request) {
...
}
}
You must call the save method from outside of the class PublicationServiceSaverImpl, maybe from PublicationServiceImpl.
The method PublicationServiceImpl.save must be public if you want to use #Transactional.
As per Spring Documentation:
When you use transactional proxies with Spring’s standard configuration, you should apply the #Transactional annotation only to methods with public visibility. If you do annotate protected, private, or package-visible methods with the #Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings.
First of all: make your method public.
Second: you have to throw the exception. If you catch and not rethrow it, how do you expect the transactional processing to know that an error occured and then rollback?
You have two options: throw the Exception instead of catching it, or catch, do some further processing and then rethrow it.
So in your repository, just add a throws keyword and then rethrow the exception after the log statement:
public int executeQuery() throws Exception {
try {
keyHolder = new GenerateKeyHolder();
namedParamaterJDBCTemplate.update(getInsertQuery(), sqlParamaterSource, kekHolder, new String[] {
"pk_id"
})
return keyHolder.getKey().intValue();
} catch(Exception e) {
log.error("Exception while saving");
throw e;
}
}
Now, for your service:
Example 1 - use the throws keyword to propagate the checked exception:
#Override
#Transactional(propagation = propagation.REQUIRED, rollbackFor = Exception.class)
public int save(Request request) throws Exception {
int pk_id_partner = partnerRepo.save(request);
int pk_id_flow = flowRepo.save(request);
String publicationCode = generatePubCode(request);
int publicationCode = pubRepo.save(pk_id_partner, pk_id_flow, request);
return 0;
}
Example 2 - catch and rethrow it as an RuntimeException, which is unchecked.
#Override
#Transactional(propagation = propagation.REQUIRED)
public int save(Request request) {
try {
int pk_id_partner = partnerRepo.save(request);
int pk_id_flow = flowRepo.save(request);
String publicationCode = generatePubCode(request);
int publicationCode = pubRepo.save(pk_id_partner, pk_id_flow, request);
} catch(Exception ex) {
throw new RuntimeException(ex);
}
return 0;
}
Note that the second example doesn't need the rollbackFor argument to the #Transactional. By default, a transaction is rolled back if a unchecked exception occurs, so there's no need to explicitly use rollbackFor in cases of RuntimeExceptions.
Another verification that would have to be done if the solution does not work. It is to verify that the database tables allow the rollback. For this, the engine has to be in InnoDB and not in MyISAM and others.
In my case adding #EnableTransactionManagement annotation on Application class resolved the issue
In stead of #Transactional(propagation = propagation.REQUIRED provide #Transactional(propagation = propagation.REQUIRED_NEW
If you use the latter, it will use the parent transaction boundary, which is at class level.
And you don't need explicitly state rollbackFor=Exception.class. By default spring will roll back on exception
And do change private to public
Try this out

hibernate 4 handle unique index exception

I have a system based on hibernate 4. I have an unique constraint in a table and need to handle it the following way:
try{
getMyService().create(myobj);
}catch(PersistenceException p){
//constraint fails
myobj.setConstraintColumn("new non unique value");
getMyService().create(myobj);//should save it
}
unfortunately I can't change the design of the system so I need just to figure it out this way.
EDIT
I get the following exception:
org.hibernate.AssertionFailure: null id in entry (don't flush the Session after an exception occurs)
The code of create method:
public E create(E entity) {
entityManager.persist(entity);
entityManager.flush();
entityManager.refresh(entity);
return entity;
}
It is not clear where your transaction boundaries are.
When the exception is thrown, you will need to:
1) Ensure that the first transaction is closed (it should be, but not sure - see if you get a nested transaction trying #2 alone)
2) begin a new transaction before you are able to persist/flush again (and subsequently commit that).
I finally figured out the issue. So, let me explain one by one.
First of all, take a look at
Propagation.REQUIRES_NEW does not create a new transaction in Spring with JPA
Then, for example we have a class and method like:
public class MyClass{
#Transactional
public void myMethod(){
....
}
}
So, first of all lets consider that myMethod is in it's own transaction completely, because transactions are AOP based, and it will be committed when appropriate aspect will fire, but only after method completes, throws exception, etc. So we can't partially commit, partially rollback, rollback incompletely,etc. So, we need to do the following:
Start big outer transaction
Start a new nested transaction, try to insert a record.
If nested transaction will fail, it will be rolled back, but the outer one will still be running.
If the first nested transaction failed, then start a new nested transaction and insert a record with new data, which will prevent ConstaintViolationException from being thrown.
So, in this case, we create a class:
public class ServiceHelper{
#Transational(proparation = **Propagation.REQUIRED_NEW**)
public void tryConstraint throws MyConstraintException{
try{
//insert
}catch(ConstraintViolationException e){
throw new MyConstraintException(e);
}catch(Exception ex){
throw new Exception(ex);
}
}
#Transational(proparation = **Propagation.REQUIRED_NEW**)
public void insertWithNoConflict throws Exception {
//Set new data
//insert, if still CVE or anything other, just throw it , or leave it for unchecked exceptions then
}
}
And our service:
public class MyService{
#Autowired
private ServiceHelper serviceHelper;
#Transactional(propagation = **Propagation.REGUIRED_NEW**)
public void createWithCheck(){
try{
serviceHelper.tryConstraint();
}catch(MyConstraintException e){
serviceHelper.insertWithNoConflict();
}
}
}
But there is still a weird situation because I need to use MyService methods for records creation in ServiceHelper, but I can't obtain them there because it will cause circular injections, so I have to get them via services factory like:
MyService service = (MyService)ServicesFactory.getInstance().getBean(MyService.BEAN_ID)
And I don't like it. But this approach works, I checked it today.
We should know 2 things: first of all we can't do anything with a transaction inside a method, we can't start there any new transaction, etc. A transaction context is relevant to method completely, it will still be there before a method ends. And the second thing is required_new doesn't work when we launch the method from the method of the same proxy class.

How to return different value, when transaction rollback?

I use ssh framework to develop a web application.
There is an example of my transaction.
#Transactional
public StudentEntity addStudent(StudentEntity studentEntity) {
return studentDAO.save(studentEntity);
}
Now, I want to return null when exception is thrown and then transaction rollback.
In general it is not advised to return null.
If you anticipate any Exception from your logic you should inform the caller via throws clause so that they are prepared for such scenarios.
Regarding rollback you should consider below update to your #Transactional annotation
#Transactional(rollbackFor=Exception.class)
Do note that this will rollback transaction after throwing any exception.
To rollback transaction programatically take a look at TransactionAspectSupport class.
#Transactional
public StudentEntity addStudent(StudentEntity studentEntity) {
try {
return studentDAO.save(studentEntity);
}
catch(Exception ex) {
//set transaction for rollback.
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
You could do it declarative manner
#Transactional(rollbackFor={SomeSpecificException.class, SomeOtherSpecificException.class})

Spring transactions with caught Exceptions

Recently, I've been working with Spring boot + spring data jpa + hibernate. I faced one problem with spring transactions. Here is my service class and two questions:
#Transactional
#Service
class MyService {
#Autowired
private MyRepository myRep;
public void method_A() {
try {
method_C();
.....
method_B();
} catch(Exception e) {}
}
public void method_B() {
Entity e = new Entity();
e.set(...);
myRep.save(e);
}
public void method_C() throws Exception {
....
}
}
1.If method method_C() throws an Exception and I want to catch it and log it, the transaction is not rollbacked in method method_B(), because the Exception does not reach Spring framework. So how should I do in order to catch Exceptions from method_C() and at the same time do not lose capability of method method_B() be rollbacked?
2.Consider new method method_A().
public void method_A() {
for(...) {
...
...
method_B();
}
}
I want invoke method_B() in a loop. If an exception occurs in a method_B() I want transaction of method_B() be rollbacked but method_A() should not exit and the loop should continue excuting. How can I achieve this?
I solved my 2 problems this way: created another #Service class and moved method_B() into it. I've annotated this class as #Transactional. Now the method method_A() looks like this:
public void method_A() {
for(...) {
...
try {
anotherService.method_B();
} catch (Exception e) {
logger.error(...);
}
}
}
If RuntimeException occurs in the method_B() method, the exception is propertly logged, transaction of method_B() is rollbacked and the loop continuous. Thanks everybody for responses.
Instead of throwing exceptions do the following. (return error code).
Update: I read your question after posting. if you call method_b from method_A both are under same transaction. Unfortunately you cannot rollback the method_b changes alone. Spring considers it as one transaction if they are all under one service class. (all methods).
One thing you can try is the following.
request to--> Controller() ---> (spring opens transaction) service_method_a(); (spring closes transaction)
Controller() ---> (spring opens transaction) service_method_c(); (spring closes transaction)
Controller() ---> (spring opens transaction) service_method_b(); (spring closes transaction)
return <--
I hope it makes sense
Each of your methods a,b,c throw exceptions if it likes to be rolledback.
Update:
another approach. This one is much better.
If each of your method are in a different service then you can use the following annotations of the spring to run each of the method in a different transaction boundaries
p v serviceA{
#transactional
method_a(){
serviceb.method_b();
}
}
p v serviceB{
#Transactional(propagation=Propagation.REQUIRED)
method_b(){
}
}
more on it here
Spring transactional story here . Read the points below this article. Those are most important when developing the spring transactional app.

Hibernate Validator validation timing

I've been googling a bit and I'm running out of time so I'm throwing this out here in the hopes that someone just knows the answer. I've got a system where I have spring transaction boundaries at the Service layer. Below that lies a dao layer. I've got bean validation on my model objects and I've wrapped the DAO's in a compile time aspectj Around aspect like this:
#Aspect
public class ValidationCollectorAspect {
#Around("daoMethods()")
public Object collectDaoMessages(ProceedingJoinPoint thisJoinPoint) throws Throwable {
try {
return thisJoinPoint.proceed();
} catch (ConstraintViolationException e) {
Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
List<UserMessage> userMessages = ThreadContext.MESSAGES.get();
for (ConstraintViolation<?> constraintViolation : constraintViolations) {
userMessages.add(new UserMessage(constraintViolation.getMessage(), MessageType.VALIDATION));
}
throw new MyPersistenceException("Validation failures", e);
}
}
#Pointcut("call(public * *(..)) &&!call(* getEntityManager()) && within(com.myclient.dao.impl..*)")
public void daoMethods() {
}
}
The problem is the validation appears to happen when the transaction is committed, not prior to the save or update operation in the DAO. This means that the ConstraintViolationException from bean validation is not thrown until AFTER the service method returns and way after this join point. My evidence for this is that the stack trace does not contain any of the dao service methods. The first method for code I wrote is shown by
at com.myclient.servlet.rest.Rest.updateObjects(Rest.java:323)
But that's a method on a servlet name Rest, and the whole point is to NOT need to create join points for a whole bunch of specific methods on the various servlets in the system, and also to be able to process the Constraint violation before it's wrapped in arbitrary layers of spring exceptions.
I understand that sometimes it might be cool to validate the sum total of all the hibernate changes, just before commit, but that's not what I want. (although as second round of validation it would not be unwelcome) How can I tell hibernate validator to process the validation when I call the hibernate save or update methods in the dao, not when the transaction commits?
Here's the versions of stuff from my build:
compile 'org.hibernate:hibernate-entitymanager:4.2.2.Final'
compile 'org.hibernate:hibernate-validator:5.0.1.Final'
compile 'org.hibernate:hibernate-c3p0:4.2.2.Final'
compile 'org.springframework:spring-orm:3.2.3.RELEASE'
compile 'org.springframework:spring-context:3.2.3.RELEASE'
compile 'org.springframework:spring-web:3.2.3.RELEASE'
compile 'javax.inject:javax.inject:1'
compile 'org.aspectj:aspectjrt:1.7.3'
ajc "org.aspectj:aspectjtools:1.7.3"
Edit: One further note... I'm doing all this under JPA, so I'd prefer a non-hibernate specific solution if one exists.
How can I tell hibernate validator to process the validation when I call the hibernate save or update methods in the dao, not when the transaction commits?
Implement a hibernate entity listeners and register the event listener for every event type when you want to do the validation.
Take a look at this question on how to configure event listeners with hibernate 4.
(Stack Overflow says this is too long to post as a comment, so posting it as an answer instead.)
I thought a bit about what you're trying to do, and can't figure out what the motivation is. So now I'm curious.
If you're receiving the object from some other piece of code and want it to be #Valid, you should be validating it before doing anything with it; not when calling save() or update(). You can either directly pass it to a validator or use the new method constraint support in Hibernate Validator 5 which will give you method validation using proxies.
If you're constructing the object yourself and are trying to catch code errors when you persist, that's a code smell. Why don't you have constructors or builders that prevent the construction of an invalid entity in the first place? And if you do have them but just want the double check when you persist, what difference would it make if the failures happen at save() time or at transaction commit, since the whole thing should roll back anyway?
You should also keep in mind that Hibernate does transactional write-behind and saves are deferred in the hope that they can be merged together to reduce chatter with the database. So if you want validation as a side-effect of saving, you should also expect that validation to be deferred or you're messing with semantics.
Anyway, I'd try HV5 method constraints at the DAO level and skip the custom validator as a first approach.
I'm not 100% sure I like this, but here's how I solved it. Basically I stopped trying to find a hibernate solution and added a couple of extra methods...
In my service layer:
#Override
public void save(T r) {
// This allows us to post a join point just outside the transaction boundary
save0(r);
}
#Transactional
private void save0(T r) {
getDao().save(r);
}
#Override
public T update(T obj) throws IllegalArgumentException {
// This allows us to post a join point just outside the transaction boundary
return update0(obj);
}
#Transactional
private T update0(T obj) {
return getDao().update(obj);
}
In my aspectJ pointcut
#Around("daoMethods()")
public Object collectDaoMessages(ProceedingJoinPoint thisJoinPoint) throws Throwable {
try {
return thisJoinPoint.proceed();
} catch (Exception e) {
ConstraintViolationException cve;
Throwable cause = e;
while (cause != null && !(cause instanceof ConstraintViolationException)) {
cause = cause.getCause();
}
if (cause == null) {
throw e;
} else {
cve = (ConstraintViolationException) cause;
}
Set<ConstraintViolation<?>> constraintViolations = cve.getConstraintViolations();
List<UserMessage> userMessages = ThreadContext.MESSAGES.get();
for (ConstraintViolation<?> constraintViolation : constraintViolations) {
userMessages.add(new UserMessage(constraintViolation.getMessage(), MessageType.VALIDATION));
}
throw new MyPersistenceException("Validation failures", e);
}
}
#Pointcut(
"execution(public * *(..)) " +
"&& !execution(public * load*(..)) " +
"&& within(com.myclient.service.impl..*) "
)
public void daoMethods() {}

Categories

Resources