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

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.

Related

Spring #transactional not auto rolling back during unchecked exception

Class A{
#transactional
public Void methodA(){
methodB();
int i=10/0;
}
#transactional
public void methodB(){
session.save(student)
}
Here there is an exception in methodA but it is not rolling back and inserting student data.why?
}
By default, #Transactional rolls back on runtime exceptions.
You need to use rollbackFor().
#Transactional(rollbackFor = {MyException.class})
Method marked with #Transactional has to be public.
This is documented in Spring Manual chapter 10.5.6:
Method visibility and #Transactional
When using proxies, 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. Consider the
use of AspectJ (see below) if you need
to annotate non-public methods.
Beacuse A and B separate transactions !
When you call a method without #Transactional within a transaction block, the parent transaction will continue to the new method. It will use the same connection from the parent method(with #Transactional) and any exception caused in the called method(without #Transactional will cause the transaction to rollback as configured in the transaction definition.
If you call a method with a #Transactional annotation from a method with #Transactional within the same instance, then the called methods transactional behavior will not have any impact on the transaction. But if you call a method with a transaction definition from another method with a transaction definition, and they are in different instances, then the code in the called method will follow the transaction definitions given in the called method.
You can find more details in the section Declarative transaction management of spring transaction documentation.
Spring declarative transaction model uses AOP proxy. so the AOP proxy is responsible for creation of the transactions. The AOP proxy will be active only if the methods with in the instance are called from out side the instance.
The answer depends on what you already know.
Do you know how Spring works when you add a #Transactional annotation?
Ans: It does so by creating a proxy class for the class which has annotated methods.
Do you know how Spring Proxy object works when one method in the proxied class calls another method in the same proxied class?
Ans: Sprig is not able to handle this scenario implicitly. Any annotation on the called method would be ignored (since the call happens on 'this' rather than on the Proxy)
You need to switch to AspectJ to handle such scenario's
If you really like to understand this behavior I recommend reading this section of the Spring documentation.

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.

issue with spring transaction management?

I am using spring and hibernate. i am using spring for transaction management. i have below class.
#Service
#Transactional(readOnly = true)
public class Sample implements SampleInterface{
#Override
public List<Some> getData(){
//gets data after that it updates something
setStatus(someId);
}
#Override
#Transactional
public void setStatus(Long someId){
//sets status
}
}
If i dont keep #Transactional for getData() then i get below exception.
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode
if i keep #Transactional for getData() then it will save properly. what is an issue here? any how i have #Transactional for setStatus(). Still do i need to keep #Transactional for getData() as it is calling a public method which will set the status?
Thanks!
The problem is a bit complex and is caused by calling setStatus() inside getData(). When you are calling getData() from outside, you are actually calling a Java proxy created for you by Spring framework. This proxy applies transaction behaviour (starts read-only transaction) and delegates to your actual service class. This works fine.
However, when you call setStatus(), you are bypassing the transactional proxy and calling your service directly. In other words the request to setStatus() is not intercepted, and #Transactional is ignored.
There is no easy way to deal with this problem and ejb has the same issue. You just have to be extra careful when calling public methods inside the same class.
See also
8.6.1 Understanding AOP proxies - official documentation
Spring pitfalls: proxying - my blog
When you call getData (without #Transactional on the method) Spring will start a read only transaction as that is the default for your class and when getData calls setStatus Spring will use the existing rad only transaction instead of creating a new one. That's the reason you are getting the exception.
The default transaction propagation is PROPAGATION REQUIRED. Read more on the topic at http://static.springsource.org/spring/docs/3.0.x/reference/transaction.html#tx-propagation

Seam #Transactional annotation not working?

I'm using the #Transactional annotation on a seam component similar to:
#Name( "myComponent" )
#AutoCreate
public class MyComponent
{
public void something() {
...
doWork();
}
...
#Transactional
protected void doWork() {
try {
log.debug( "transaction active: " + Transaction.instance().isActive() );
} catch (Exception ignore) {}
// some more stuff here that doesn't appear to be inside a transaction
}
}
In the "some more stuff" section, I'm modifying some Hibernate entities and then had a bug where an Exception was thrown. I noticed that the Exception wasn't causing the transaction to be rolled back (the modified entities were still modified in the db) so I added the "transaction active" logging. When this code executes, isActive() returns false.
Is there something I'm missing? Why isn't the transaction active?
In case it matters, I'm using the Seam component from inside another component that is using RESTEasy annotations to trigger my method calls.
I'm not familiar with how Seam works so my apologies in advance if this answer does not apply.
I noticed that the method that is #Transactional is protected. This implies to me that it is being called by another internal method.
With Spring's AOP, you mark the public methods with #Transactional which are wrapped and replaced with a transaction proxy. When an external class calls the public method, it is calling the proxy which forms the transaction. If the external class calls another public method that is not marked with #Transactional which then calls an internal method which is, there will be no transaction created because the proxy is not being called at all.
In Spring, even if you change your doWork() method to be public, the same problem would happen. No transaction because the proxy object is not being called. Method calls made inside of the class are not making calls to the proxy object.
A quick read of some documentation seems to indicate that, like Spring AOP, Seam is using CGLib proxying. The question is if it is able to proxy all methods -- even if they are called from within the proxied object. Sorry for wasting your time if this answer does not apply.

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

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)

Categories