I don't get how it works the transactional annotations of Spring. So I made the next test with no practical sense but I It shows my problem:
public class TransactionalTest {
public void noTransaction(){
required();
}
#Transactional(propagation = Propagation.SUPPORTS)
public void supports(){
required();
}
#Transactional(propagation = Propagation.REQUIRED)
public void transaction(){
required();
}
#Transactional(propagation = Propagation.REQUIRED)
public void required(){
mandatory();
}
#Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.MANDATORY)
public void mandatory(){
doSomething();
}
private void doSomething(){
//I don't feel like to do something.
}
}
Methods noTransaction, supports and transaction call to the same method: required but only the last one (transaction) works properly. The two others give me back the message No existing transaction found for transaction marked with propagation 'mandatory'.
In the real case, I have some non transactional methods which calls to transactional methods annotated with REQUIRED (It works fine). but if a non transactional method calls to a transactional method (annotated with REQUIRED) which it calls to another transactional method annoted with MANDATORY, it fails.
Why this behaviour and how can avoid it? I annotated annotate all the method which calls to a transaccional method just in case?
If you're using the default AOP method, Spring Proxy AOP, the advice is implemented by wrapping the annotated class with a proxy object that intercepts and advises the calls. When you use self-calls like this, you're bypassing the proxy, and the advice doesn't get applied.
If you really do need to have advice applied on self-calls, you need to use AspectJ AOP, which actually modifies the class in question instead of decorating it.
Related
I have a few questions about #Transactional annotation. If I have an implementation similar as below:
#Transactional
public Interface ExampleRepo {
SampleClass createRecord();
}
public class RepoImpl implements ExampleRepo {
public SampleClass createRecord() {
return saveRecord();
}
private saveRecord() {
//Saving record to Database.
}
}
Will the transaction rollback as Spring will ignore if the #Transactional was called on a private method? But what if a public method annotated with #Transactional calls a private method that actually does the database operation.
Will this transaction be marked as rollback?
As per Spring Documentation
Spring recommends that you only annotate concrete classes (and methods
of concrete classes) with the #Transactional annotation, as opposed to
annotating interfaces. You certainly can place the #Transactional
annotation on an interface (or an interface method), but this works
only as you would expect it to if you are using interface-based
proxies. The fact that Java annotations are not inherited from
interfaces means that if you are using class-based proxies (
proxy-target-class="true") or the weaving-based aspect (
mode="aspectj"), then the transaction settings are not recognized by
the proxying and weaving infrastructure, and the object will not be
wrapped in a transactional proxy, which would be decidedly bad.
So, I would recommend to use #Transaction at method createRecord() level or RepoImpl class level.
All code within a transaction scope runs in that transaction. However, you can specify the behavior if a transactional method is run when a transaction context already exists.
When a method without #Transactional is called within a transaction block, the parent transaction will continue to exist for the new method. It will use the same connection from the parent method (method with #Transactional) and any exception caused in the called method (method without #Transactional) will cause the transaction to rollback as configured in the transaction definition.
Since this mechanism is based on proxies, only 'external' method calls coming in through the proxy will be intercepted. This means that 'self-invocation', i.e. a method within the target object calling some other method of the target object, won't lead to an actual transaction at runtime even if the invoked method is marked with #Transactional.
I need to use JdbcTemplate in Spring.
For example, I have:
void someFunction() {
// Some logic
sql();
}
#Transactional
void sql() {
jdbcTemplate.batchUpdate(...);
}
As I understand, this is not a valid usage of transactions.
So, can I use #Transactional annotation for JdbcTemplate as follows:
#Transactional
void someFunction() {
// Some logic
jdbcTemplate.batchUpdate(...);
}
or it's better to use TransactionTemplate?
Yes you can use the annotation like that, however review this part of the Spring documentation that states
'Due to the proxy-based nature of Spring's AOP framework, protected methods are by definition not intercepted, neither for JDK proxies (where this isn't applicable) nor for CGLIB proxies (where this is technically possible but not recommendable for AOP purposes). As a consequence, any given pointcut will be matched against public methods only!'.
Therefore, your method should be a public method, which it currently is not. Update it, and your method should work.
I have a Service class like below:
#Service("MyService")
public class MyService {
#Autowired
MyDao dao;
public void process() {
getFromDao();
// getMoreFromDao();
// process();
// if all good, then
doStuff();
}
public void getFromDao() {
// do some stuff
dao.getData();
}
#Transactional(transactionManager="simpleDatasourceTxMgr", propagation=Propagation.REQUIRED)
public void doStuff() {
dao.saveData(1);
dao.saveData(2);
dao.saveData(3);
}
}
The DAO called is:
#Repository
public class MyDao {
#Autowired
#Qualifier("myjdbcTemplate")
NamedParameterJdbcTemplate jdbcTemplate;
public void saveData(obj a) {
jdbcTemplate.execute("Query", ...);
}
}
I want my doStuff() method in the service class to run within a transaction and rollback everything if there is an exception in the saveData() method. But this is not running in transaction.
If I add #Transaction to a DAO method looks like it runs in separate transaction. Is this correct?
Update: I have added a process() method to my Service and I call getFromDao() and doStuff() from process(). process() is called from the controller. So looks like if I make the service class #Transactional, then everything executes within a transaction. But I don't want getFromDao() to execute in transaction.
We use just JDBC and no Hibernate.
You can place the #Transactional annotation before an interface
definition, a method on an interface, a class definition, or a public
method on a class. However, the mere presence of the #Transactional
annotation is not enough to activate the transactional behavior. The
#Transactional annotation is simply metadata that can be consumed by
some runtime infrastructure that is #Transactional-aware and that can
use the metadata to configure the appropriate beans with transactional
behavior. In the preceding example, the
element switches on the transactional behavior.
Or if you want annotations you can enable it with
It is not sufficient to tell you simply to annotate your classes with
the #Transactional annotation, add #EnableTransactionManagement to
your configuration, and then expect you to understand how it all
works. This section explains the inner workings of the Spring
Frameworkâs declarative transaction infrastructure in the event of
transaction-related issues.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html
I have a data access class which runs as part of a stand-alone java application. It is currently working which means that a transaction manager is defined but I want to refactor the class to reduce the scope of the transaction but if I do I get org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here which implies that moving the #Transactional has somehow stopped it from being recognised.
My original version had the refactored methods being private but I found a recommendation to change that to public as in some cases the annotation would not be picked up.
public class DoStuff {
#Transactional
public void originalMethod() {
// do database stuff
...
// do non-database stuff that is time consuming
...
}
}
What I want to do is refactor to the following
public class DoStuff {
public void originalMethod() {
doDatabaseStuff()
doNonDatabaseStuff()
}
#Transactional
public void doDatabaseStuff() {
...
}
public void doNonDatabaseStuff() {
...
}
}
Edit:
You need to understand how Spring proxying works to understand why your refactoring does not work.
Method calls on the object reference will be calls on the proxy, and as such the proxy will be able to delegate to all of the interceptors (advice) that are relevant to that particular method call. However, once the call has finally reached the target object, any method calls that it may make on itself, are going to be invoked against the this reference, and not the proxy. This has important implications. It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.
#Transactional uses Spring AOP, Spring uses proxies. This means that when you call an #Transactional method from another class, Spring will use a proxy, so the transactional advice will be applied. However, if you call the method from the same class, spring will use the "this" reference instead of the proxy, so that transactional advice will not be applied.
Original Answer:
Here is what worked for me in similar scenario.
public class DoStuff implement ApplicationContextAware {
private ApplicationContext CONTEXT;
public void setApplicationContext(ApplicationContext context) throws BeansException {
CONTEXT = context;
}
public void originalMethod() {
getSpringProxy().doDatabaseStuff()
doNonDatabaseStuff()
}
private DoStuff getSpringProxy() {
return context.getBean(this.getClass());
}
#Transactional
public void doDatabaseStuff() {
...
}
public void doNonDatabaseStuff() {
...
}
}
Explanation:
Make the class ApplicationContextAware, so it has a reference to the context
When you need to call a transactional method, fetch the actual spring proxy from the context
Use this proxy to call your method, so that #Transactional is actually applied.
Your approach looks like it should work just fine, I expect the issue is related to Spring proxies.
The reason that I asked about interfaces is related to the default method by which Spring applies transactional behaviour - JDK dynamic proxies.
If the actual definition of your class is:
public class DoStuff implements Doable {
public void originalMethod() {
}
}
public interface Doable {
public void originalMethod();
}
If this is indeed the structure, when you moved to the new structure Spring is not able to proxy the new doDatabaseStuff method.
Your options to fix this:
Add the new methods to your interface to ensure that Spring can proxy them
Move to using CGLIB based proxies (these do not rely on interfaces)
in one dao I have 2 #Transactional methods.
if i do not provide any explicit properties,
then what will happen, if
I run one method in the body of another?
Both methods will run within THE SAME ONE TRANSACTION?
Proxies in Spring AOP
When using Transactional, you're dealing with proxies of classes, so in this scenario:
#Transactional
public void doSomeThing(){ // calling this method targets a proxy
doSomeThingElse(); // this method targets the actual class, not the PROXY,
// so the transactional annotation has no effect
}
#Transactional
public void doSomeThingElse(){
}
you are calling the proxy from outside, but the second method call is made from inside the proxied object and therefor has no transactional support. So naturally, they run in the same transaction, no matter what the values of the #Transactional annotation in the second method are
so if you need separate transactions, you have to call
yourservice.doSomething();
yourservice.doSomethingElse();
from outside.
The whole scenario is explained pretty well in the chapter Spring AOP > Understanding AOP proxies, including this "solution":
Accessing the Current AOP Proxy object from the inside
public class SimplePojo implements Pojo {
public void foo() {
// this works, but... gah!
((Pojo) AopContext.currentProxy()).bar();
}
public void bar() {
// some logic...
}
}
The default value of the propagation attribute of #Transactional is REQUIRED, which means:
Support a current transaction, create a new one if none exists.
So yes - both methods will run in the same transaction.
But one important advice: don't make your DAO transactional. The services should be transactional, not the DAO.
Spring doc
one note:
In proxy mode (which is the default),
only external method calls coming in
through the proxy are intercepted.
This means that self-invocation, in
effect, a method within the target
object calling another method of the
target object, will not lead to an
actual transaction at runtime even if
the invoked method is marked with
#Transactional.