I am very new to transactions in spring.Due to some code standards used by my organization , i asked to join parent transaction if exist while calling any method.
My application is a Spring MVC application having three layers
Web Layer(controller classes)
Service Layer(Service class containing business logic)
DAO Layer (DAO(Data access layer) classes for DB related query )
Now in a method on service layer uses three different methods of dao layer. I have annotated this service method as transactional in nature by using #Transactional. Now i want all three dao methods called from this service layer methods also be transactional in nature and must join the parent transaction started by service layer method insisted of starting other new translations for each dao methods.
You need to annotate your service method with a REQUIRES_NEW propagation. This would mark the start of transaction. By default, the dao methods if called by this method would inherit the transactional behavior and use the existing transaction.
However, if you want to represent the transactional boundaries in your code, you can annotate them with a REQUIRED (participate in transaction if existing or create a new if doesn't exist) or MANDATORY (participate in transaction if existing, otherwise throw an exception).
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void serviceMethod() {}
#Transactional(propagation = Propagation.REQUIRED)
public void daoMethod1() {}
#Transactional(propagation = Propagation.REQUIRED)
public void daoMethod2() {}
You can use the propagation element in the #Transactional annotation with the property Propagation.MANDATORY. With this, the method supports the current transaction or throws an exception if there is no active transaction. So, in your DAO layer, you can do something like this :
#Transactional(propagation=Propagation.MANDATORY)
public void daoMethod() { // some logic }
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.
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.
Any reason for this?
I've been using Spring data for years but I don't think I've ever unit tested one of their out-of-the-box CRUD methods before.
Why is it that the following interface definition has no effect on their transactional implementation for CRUD methods?
#Repository
#Transactional(propagation = Propagation.MANDATORY)
public interface MyRepository extends JpaRepository<MyEntity, Long> {
Stream<MyEntity> findMyEntityByStatusEquals(Status status);
}
If I call myRepository.save(new MyEntity()) from a test method, WITHOUT my test being wrapped in a transaction, it succeeds.
However, if I call myRepository.findMyEntityByStatusEquals("MY_STATUS") it fails stating that it needs to be wrapped in a transaction.
The latter case I expect, the former case terrifies me as I don't seem to be able to enforce it to be part of an existing transaction.
::Edit:: Turns out putting #Transactional at the top of the interface has no effect on Spring Data CRUD methods that have previously been marked as #Transactional. I always assumed it was also an override when specified on these interfaces.
As stated by documentation here
CRUD methods on repository instances are transactional by default. For
reading operations the transaction configuration readOnly flag is set
to true, all others are configured with a plain #Transactional so that
default transaction configuration applies.
#Transactional has Propagation.REQUIRED as its default propagation type, so when you call the save method a new transaction just begin.
If you want force Propagation.MANDATORY even on built-in CRUD methods you have to override such methods, i.e
#Repository
#Transactional(propagation = Propagation.MANDATORY)
public interface MyRepository extends JpaRepository<MyEntity, Long> {
Stream<MyEntity> findMyEntityByStatusEquals(Status status);
#Transactional(propagation = Propagation.MANDATORY)
public <MyEntity> MyEntity save(MyEntity entity) {
super.save(entity);
}
}
hope this helps
I am trying to understand the transactions management and try to use its power in my already existing application developed in Struts 2, EJB 3 and hibernate 5.2.
Now I have ejb in my business layer like below
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER)
public class MyEJb implements ejbxyz {
#Override
public void method(){
Dao dao=new Dao() //Dao class is simple java class
dao.fooMethod(); //this method updates some record
dao.barMethod(); // this method updates some other record
}
}
public class Dao{
fooMethid(){
Session session=sessFactory.openSession();
session.beginTransaction();
session.update(x);
}
barMethod(){
try{
Session session=sessFactory.getCurrentSession();
session.getNamedQuery("xyz").executeUpdate();
}catch(HibernateException ex){
session.getTransaction.rollback();
}
}
}
I understand that Transaction management should be done at service layer(at ejb in my case). But how can I achieve this over there. ?
Now the dependency is if barMethod() fails to update the record then I need to rollback the changes made in fooMethod. So basically I need both the methods to be done in one transaction.
When I execute the application it throws the below exception
Exception while barMethod getNamedQuery is not valid without active transaction
Its because I am not beginning any transaction in barMethod. But then I really dont want to start a new transaction and want to continue with the older transaction started in fooMethod.
Container managed transactions are indeed suported out of the box for EJB beans. However, your Dao class is not a managed bean - it is a regular pojo that you instantiate manualy - therefore it does not participate in any transaction started by your other ejb.
So move your Dao to separate file, annotate it with #Stateless and then inject it into your service using #EJB private Dao dao;
There is more to transactions in Ejb container though. You can control the transaction support on method level via #TransactionAttribute annotation, that specifies how should the container invoke your method with regard to transaction. That way you can control, whether your method requires its own transaction, or if it shall participate in a transaction initiated by the caller(e.g. when invoked from ejb bean). For more info have a look at official Java EE tutorial
I'm working on spring boot and I need to clarify something regarding to transaction management.
For example, I do have 2 classes that run two separate jobs (the first job is to create profile on database and the second job is to call restful application also profile creation but on different system).
This 2 jobs must be in transactional. Both success needed. It should not create any profile on any data store if one of the job is fails)
Since I'm really new in this Spring. I hope to get suggestion and need to know what is the best practice for this scenario.
There is a facade pattern. I suggest to make a facade service to join the logic of two services. Services must be separate, because working with profiles and communicating with other system are different parts of business logic.
For example, there are ProfileService and OuterService to work with profiles and to outer communication. You can write SomeFacadeService to join two methods and wrap it in one transaction. Default propagation of #Transactional is REQUIRED. So transaction will be created on method SomeFacadeService.doComplexJob and methods profileService.createProfile and outerService.doOuterJob will join the current transaction. If exception occurs in one of them whole SomeFacadeService.doComplexJob will be rolled back.
#Controller
public class SomeController {
#Autowired
SomeFacadeService someFacadeService ;
#RequestMapping("/someMapping")
public void doSomeJob() {
someFacadeService.doComplexJob();
}
}
#Service
public class SomeFacadeService {
#Autowired
ProfileService profileService;
#Autowired
OuterService outerService;
#Transactional
public void doComplexJob() {
profileService.createProfile();
outerService.doOuterJob();
}
}
#Service
public class ProfileService {
#Transactional
public void createProfile() {
// create profile logic
}
}