How to call a custom rollback method in Spring Transaction Management? - java

Environment: Spring 3, Custom Transaction Management, JDBC Transactions
I just read the Spring docs on using the transaction template to handle transaction management. It seemed overly complex so I want to ask:
Most of my transactions are JDBC related, meaning I just declare an #Transactional on my service. But now I am making a REST service call to another site which needs to rollback if any of the following JDBC operations fail, I'll provide the rollback code in this case.
As I progress in my method, in my transaction - I want to save a reference to the REST service call (needed to roll back that action), and upon exception I just want a method myCustomRollback() called which can access the previously stored object.
Why not just provide a map in the transactionTemplate for storing stuff and define a custom rollback method on the #Transactional annotation?
This is the way I think about it, I'm not following the way Spring thinks about this. Can someone help me bridge the gap between what I want and how I accomplish it most efficiently in Spring? I only need to do this for a few special case operations.

To anyone still reading this:
I solved a similar problem with spring events - as suggested by Den Roman in option 3.
Here's the basic idea (scenario is fictional):
Whenever I perform external operations that need to be rolled back together with the transaction, I publish an event inside my #Transactional method using support from spring (org.springframework.context.ApplicationEventPublisher):
#Transactional
public String placeOrder(Order order) {
String orderId = orderServiceGateway.createOrder(order);
applicationEventPublisher.publishEvent(new OrderCreatedEvent(orderId));
workflowService.startWorkflow(orderId);
return orderId;
}
The event itself can be any object - I created a POJO with details about the remote entity to be deleted.
Then I registered a special event listener that is bound to a transaction phase - in my case to the rollback:
#TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void rollBackOrder(OrderCreatedEvent orderCreatedEvent) {
String orderId = orderCreatedEvent.getOrderId();
orderServiceGateway.deleteOrder(orderId);
}
Of course, it's recommended to catch & log the exception from rollback operation, not to lose the original exception from the placeOrder() method.
By default these events are synchronous, but they can be made async by additional configuration.
Here's a very good article on this mechanism, including detailed configuration and pitfalls: Transaction Synchronization and Spring Application Events (DZone)
While I don't like the solution 100% because it clutters the business logic with event publishing stuff and binds to spring, it definitely does what I expect it to do and makes it possible to pass context from the transactional method to the rollback method - which is not available through a traditional try/catch block outside of the transactional method (unless you put your context in the exception itself, which is not very nice).

I've re-read your question a few times and am not sure I understand your question completely. I assume your executing someCode and if that fails you would like to execute myCustomRollback which has some information about someCode. So I'll try to provide a Generic answer.
If you want spring to rollback some code. It will only rollback that which is rollBackAble, like jdbc transactions. Assume you have a method which performs 2 calls.
#Transactional
public void doStuff(SomeEntity entity, File file) {
persist(entity);
customFileService.createOnFileSystem(file);
throw new RunTimeException();
}
So the code above will always rollback. It will undo the persisting of your entity, but not the creation of your file, since that is not managed by Spring transactions, unless you provide custom implementation for it to be.
Second, Spring provides 2 ways of working with transactions:
Spring AOP: a proxy is created at runtime which will decorate your code with transactional stuff. If your class would be named MyClass, then Spring will create a class names MyClassProxy, which will wrap your code in transactional code.
AspectJ: at compile time your .class file will be adjusted and transactional code will be embedded inside your method.
The aspectJ approach seems harder to configure, but isn't so much and is way easier to use. Since anything which is annotated with #Transactional will be embedded (weaved) with code. For Spring AOP this is not the case. Transactional inner method calls for instance in Spring will be ignored! So aspectJ provides a more intuitive approach.
Back to what I think your question is (the code is all in 1 class):
public void doSomeCode() {
Object restCall = initialize();
try {
execute(restCall);
} catch (CustomException e) {
myCustomRollback(restCall; e);
}
}
#Transactional(rollbackFor = CustomException.class)
private void execute(Object restCall) throws CustomException {
// jdbc calls..
restCall = callRest(restCall);
throw new CustomException();
}
void myCustomRollback(Object restCall, CustomException e) {
...
}
The code above will only work with AspectJ! Since your making inner method calls which also seems to be private! AOP at runtime cannot handle this.
So what happens is everything (which is rollbackAble) in execute will be rollbacked. And in doStuff you have information about the objects which were used in execute, you now can use in myCustomRollback to rollback your REST stuff manually.
Not sure if I answered this question properly, but I hope it helps someone with a similar problem.

1 solution is to implement your own transactional manager by extending a one
2 solution is to use TransactionSynchronizationManager class
3 solution is to use #TransactionalEventListener in case you have Spring 4

Spring transaction management the default behavior for automatic rollback is for unchecked exceptions
so for a custom exception,
#Transactional(rollbackFor = CustomException.class, noRollbackFor = RuntimeException.class)
public void doSomething(...
)
the transaction be rolled back if it there is an exception that matches the specified. If an exception not matches, it is propagated to caller of the service or TransactionRolledBackException wrapper
if you use use the org.springframework.transaction.PlatformTransactionManager it is more manageable handling exceptions than template
check the documentation http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html

you can use the AfterThrowing advice (when an exception is thrown) & call your method (myCustmRollback()) there, you can use TransactionSynchronizationManager class to get thecurrent transaction & roll it back...
alternatively.. you can use the AroundAdvice to begin & commit/rollback your transaction (this way you can use the spring provided transaction manager by using the TransactionSynchronizationManager class)

Related

Rolling back transaction with aspect in Spring boot application

I am new to the concept of Aspect-Oriented Programming. I am writing this following aspect using AspectJ in my spring boot application:
#Aspect
public class MyAspect {
private final AspectUtil aspectUtil;
public MyAspect(AspectUtil aspectUtil)) {
this.aspectUtil = aspectUtil;
}
#Pointcut("#within(org.springframework.stereotype.Service)")
public void applicationServicePointcut() {
}
#AfterReturning(value = ("applicationServicePointcut()"))
public void process(JoinPoint joinPoint) {
HttpServletRequest request = aspectUtil.getRequest();
aspectUtil.getHttpVerb(request);
processUtil(request, joinPoint);
}
public void processUtil(HttpServletRequest request) {
...
}
All of the services defined in my application are annotated with #Transactional, one example is given below:
#Service
#Transactional
public class MyService {
..
}
So, here my question is if any exception I throw from my aspect as defined above, then would the associated transaction in the service layer get rolled back?
Actually, my goal is to roll back the entire transaction in case, any exception occurs at this aspect.
An example for a similar requirement 1.4.8. Advising Transactional Operations is available in the documentation.
Exerpt :
The ordering of advice is controlled through the Ordered interface.
.........
The result of the preceding configuration is a fooService bean that
has profiling and transactional aspects applied to it in that order.
If you want the profiling advice to run after the transactional advice
on the way in and before the transactional advice on the way out, you
can swap the value of the profiling aspect bean’s order property so
that it is higher than the transactional advice’s order value...
Please do go through : Advice Ordering to understand the concept better and control the order of advice.
Also go through the conceptual view of calling a method on a transactional proxy:
Basically the idea would be to throw the exception from the custom aspect that should result in the rollback from the Transaction Advisor.
The short answer is it depends. Aspects are ordered in Spring, meaning that they run with a specific order. That means that if your aspect is set to run before the transactional aspect, then no transaction will be created and there would be nothing to actually rollback.
On the other hand, if you set this to run after the transaction aspect and it indeed throws an exception, this would automatically mean that the transaction would have ran and would be committed.
I believe you need to check on ordering of aspects to find the pattern that suits your needs the best.

Spring transaction not being created when invoking #Transactional method via reflection

I have a class that is invoking a public method on another class via reflection. The invoking class already has an active transaction, and the public method on the invoked class is marked with
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void handleProcess() { ..}
The invocation looks like
Runnable runnable = null;
Method handleMethod = config.handleProcessMethod;
Object handler = autowireHandler(process);
runnable = () -> {
LOGGER.info("executing method {} on {}",handleMethod,handler);
handleMethod.invoke(handler);
};
runnable.run();
The method is called correctly, but the log indicates that it is particpating in the current transaction instead of creating a new one.
08:54:33.452 [process-executor-2] DEBUG o.springframework.orm.jpa.JpaTransactionManager - Found thread-bound EntityManager ... for JPA transaction
08:54:33.453 [process-executor-2] DEBUG o.springframework.orm.jpa.JpaTransactionManager - Participating in existing transaction
Edit: Actually the logs from above are being created within the invoked method, caused by a JPA repository, not prior to it. So it looks like the #Transactional annotation isn't being processed at all.
Is the reflection causing spring to miss the #Transactional annotation? I need the invoked method to use it's own transaction so that it's committed prior to returning from the invocation.
The annotation tells Spring what to wrap with a proxy that implements the transactional behavior. Reflection is bypassing that proxy and calling the wrapped method directly. Looking at the logs should confirm that, you may need to dial up log level for spring stuff.
Don't use reflection for this, because that's going behind Spring's back and it can't help you. You can autowire a list of services that implement a common interface. Your code can go through the list and figure out which one is relevant to what you need to do, then call the method on the chosen service.
If you are submitting tasks to an executor, you are going to have trouble when there is no entitymanager found on the worker thread. If you make these services use Spring async methods instead, that will let Spring handle the transactions, entityManager, etc.

Postgres Hibernate set session variables for row level security

I am having trouble finding information about this issue I am running into. I am interested in implementing row level security on my Postgres db and I am looking for a way to be able to set postgres session variables automatically through some form of an interceptor. Now, I know that with hibernate you are able to do row-level-security using #Filter and #FilterDef, however I would like to additionally set policies on my DB.
A very simple way of doing this would be to execute the SQL statement SET variable=value prior to every query, though I have not been able to find any information on this.
This is being used on a spring-boot application and every request is expected to will have access to a request-specific value of the variable.
Since your application uses spring, you could try accomplishing this in one of a few ways:
Spring AOP
In this approach, you write an advice that you ask spring to apply to specific methods. If your methods use the #Transactional annotation, you could have the advice be applied to those immediately after the transaction has started.
Extended TransactionManager Implementation
Lets assume your transaction is using JpaTransactionManager.
public class SecurityPolicyInjectingJpaTransactionManager extends JpaTransactionManager {
#Autowired
private EntityManager entityManager;
// constructors
#Override
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
super.prepareSynchronization(status, definition);
if (status.isNewTransaction()) {
// Use entityManager to execute your database policy param/values
// I would suggest you also register an after-completion callback synchronization
// This after-completion would clear all the policy param/values
// regardless of whether the transaction succeeded or failed
// since this happens just before it gets returned to the connection pool
}
}
}
Now simply configure your JPA environment to use your custom JpaTransactionManager class.
There are likely others, but these are the two that come to mind that I've explored.

Should I pass a managed entity to a method that requires a new transaction?

My application loads a list of entities that should be processed. This happens in a class that uses a scheduler
#Component
class TaskScheduler {
#Autowired
private TaskRepository taskRepository;
#Autowired
private HandlingService handlingService;
#Scheduled(fixedRate = 15000)
#Transactional
public void triggerTransactionStatusChangeHandling() {
taskRepository.findByStatus(Status.OPEN).stream()
.forEach(handlingService::handle);
}
}
In my HandlingService processes each task in issolation using REQUIRES_NEW for propagation level.
#Component
class HandlingService {
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void handle(Task task) {
try {
processTask(task); // here the actual processing would take place
task.setStatus(Status.PROCCESED);
} catch (RuntimeException e) {
task.setStatus(Status.ERROR);
}
}
}
The code works only because i started the parent transaction on TaskScheduler class. If i remove the #Transactional annotation the entities are not managed anymore and the update to the task entity is not propagated to the db.I don't find it natural to make the scheduled method transactional.
From what i see i have two options:
1. Keep code as it is today.
Maybe it`s just me and this is a correct aproach.
This varianthas the least trips to the database.
2. Remove the #Transactional annotation from the Scheduler, pass the id of the task and reload the task entity in the HandlingService.
#Component
class HandlingService {
#Autowired
private TaskRepository taskRepository;
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void handle(Long taskId) {
Task task = taskRepository.findOne(taskId);
try {
processTask(task); // here the actual processing would take place
task.setStatus(Status.PROCCESED);
} catch (RuntimeException e) {
task.setStatus(Status.ERROR);
}
}
}
Has more trips to the database (one extra query/element)
Can be executed using #Async
Can you please offer your opinion on which is the correct way of tackling this kind of problems, maybe with another method that i didn't know about?
If your intention is to process each task in a separate transaction, then your first approach actually does not work because everything is committed at the end of the scheduler transaction.
The reason for that is that in the nested transactions Task instances are basically detached entities (Sessions started in the nested transactions are not aware of those instances). At the end of the scheduler transaction Hibernate performs dirty check on the managed instances and synchronizes changes with the database.
This approach is also very risky, because there may be troubles if you try to access an uninitialized proxy on a Task instance in the nested transaction. And there may be troubles if you change the Task object graph in the nested transaction by adding to it some other entity instance loaded in the nested transaction (because that instance will now be detached when the control returns to the scheduler transaction).
On the other hand, your second approach is correct and straightforward and helps avoid all of the above pitfalls. Only, I would read the ids and commit the transaction (there is no need to keep it suspended while the tasks are being processed). The easiest way to achieve it is to remove the Transactional annotation from the scheduler and make the repository method transactional (if it isn't transactional already).
If (and only if) the performance of the second approach is an issue, as you already mentioned you could go with asynchronous processing or even parallelize the processing to some degree. Also, you may want to take a look at extended sessions (conversations), maybe you could find it suitable for your use case.
The current code processes the task in the nested transaction, but updates the status of the task in the outer transaction (because the Task object is managed by the outer transaction). Because these are different transactions, it is possible that one succeeds while the other fails, leaving the database in an inconsistent state. In particular, with this code, completed tasks remain in status open if processing another task throws an exception, or the server is restarted before all tasks have been processed.
As your example shows, passing managed entities to another transaction makes it ambiguous which transaction should update these entities, and is therefore best avoided. Instead, you should be passing ids (or detached entities), and avoid unnecessary nesting of transactions.
Assuming that processTask(task); is a method in the HandlingService class (same as handle(task) method), then removing #Transactional in HandlingService won't work because of the natural behavior of Spring's dynamic proxy.
Quoting from spring.io forum:
When Spring loads your TestService it wrap it with a proxy. If you call a method of the TestService outside of the TestService, the proxy will be invoke instead and your transaction will be managed correctly. However if you call your transactional method in a method in the same object, you will not invoke the proxy but directly the target of the proxy and you won't execute the code wrapped around your service to manage transaction.
This is one of SO thread about this topic, and Here are some articles about this:
http://tutorials.jenkov.com/java-reflection/dynamic-proxies.html
http://tutorials.jenkov.com/java-persistence/advanced-connection-and-transaction-demarcation-and-propagation.html
http://blog.jhades.org/how-does-spring-transactional-really-work/
If you really don't like adding #Transaction annotation in your #Scheduled method, you could get transaction from EntityManager and manage transaction programmatically, for example:
UserTransaction tx = entityManager.getTransaction();
try {
processTask(task);
task.setStatus(Status.PROCCESED);
tx.commit();
} catch (Exception e) {
tx.rollback();
}
But I doubt that you will take this way (well, I wont). In the end,
Can you please offer your opinion on which is the correct way of tackling this kind of problems
There's no correct way in this case. My personal opinion is, annotation (for example, #Transactional) is just a marker, and you need a annotation processor (spring, in this case) to make #Transactional work. Annotation will have no impact at all without its processor.
I Will more worry about, for example, why I have processTask(task) and task.setStatus(Status.PROCESSED); live outside of processTask(task) if it looks like does the same thing, etc.
HTH.

Bulk import and #Transactional

I'm developing an import module to load in a bulk way from an excel file. I'm using spring to manage the transactional context and spring-data-jpa like DAO, to read a excel file I'm using POI.
#Service
public class ImportService{
#Autowired
UserRepository userRepository;
#Autowired
UserDetailRepository userDetailRepository;
#Autowired
AddressRepository addressRepository;
#Transactional(propagation = Propagation.REQUIRES_NEW)
public Map<String,StringBuffer> import(byte[] bytesUploaded) {
wb = new XSSFWorkbook(new ByteArrayInputStream(bytesUploaded));
XSSFSheet mySheet = (XSSFSheet) wb.getSheetAt((short)0);
for(Row row: mySheet){
if(row.getRowNum() >= 2 ){
readRowFromExcel(row);
}
}
}
#Transactional(propagation = Propagation.NESTED,rollbackFor=Exception.class)
private void readRowFromExcel(Row row) {
try{
User user = readUserFromFile(row);
UserDetail = readUserDetailFromFile(row,user);
Address address = readAddressFromFile(row,user);
//A few check to verify the corretly load of the entities
userDetailRepository.save(userDetail);
addressRepository.save(address);
userRepository.save(user);
}
catch (Exception e){
//Do something
}
}
}
I would want that if an exception occurred during read or the save of the row nothing become persistent, however an internal exception mustn't cause rollback the outer transaction.
How can I manage this kind of situation?
Am I facing the problem correctly?
Can you give me some suggestion about this (I'm new of spring)?
Thanks
A good start is the Transaction management section of the Spring framework reference documentation
There are a few things in your sample code that needs improvements:
Declarative transaction management
See section 11.5.1
The most important concepts to grasp with regard to the Spring Framework’s
declarative transaction support are that this support is enabled via AOP
proxies, and that the transactional advice is driven by metadata (currently
XML- or annotation-based). The combination of AOP with transactional metadata
yields an AOP proxy that uses a TransactionInterceptor in conjunction with
an appropriate PlatformTransactionManager implementation to drive transactions
around method invocations.
What that means typically is that calling your private method will not honour the transaction demarcation as you expect. Calling other methods of your service internally won't work either by default as it does not go through the proxy. In your case, Using #Transactional provides additional useful references.
The #Transactional on readRowFromExcel seems useless as you have the guarantee that a fresh transaction will be created before calling import.
Exception handling
In order for the AOP proxy to manage an exception thrown during a transactional method, you have to actually make sure the exception is thrown outside the method boundary so that the proxy can see it. In the case of your example, the do something part should make sure to throw the appropriate exception. If you want fine-grained management of the exception, you should probably define another exception type than Exception.
Wrapping up
If you want to read your excel file and save the content to your store using Spring data, you should probably have a single public annotated method whose purpose is to read an item and store it using Spring data. This would need some refactoring of your API. If the excel sheet is fairly large, you should also consider Spring Batch to process it.
What would be the purpose of the outer transaction here? You should also consider failure scenario such as when one particular row cannot be read (or written to the store). Are you leaving the file half-processed or are you flagging this error somewhere?

Categories