I have seen many comments in stack-overflow articles I found certain things about either @Transactional use with @Service or with @Controller
"Usually, one should put a transaction at the service layer."
"The normal case would be to annotate on a service layer level"
"Think transactions belong on the Service layer. It's the one that knows about units of work and use cases. It's the right answer if you have several DAOs injected into a Service that need to work together in a single transaction." [Source]
Drawback to use @transactional with @service layer
If I had 2 methods for example saveUser() and saveEmail() (because I store the emails in a database to send them later - like a queue) I would create in my service a method saveUserAndSendEmail(User user) which would be transactional. [Source]
It means I create many methods in service layer instead of one Save Generic Method as follow
public <T> long save(T entity) throws DataAccessException {
Session session = sessionFactory.getCurrentSession();
long getGenVal=(Long) session.save(entity);
return getGenVal;
}
According to the above solution , it means we have many methods like following LOL..
public <T> long saveAccount(T entity)....
public <T> long saveWithAuditLog(T entity, K entity1)....
public <T> long saveWithAuditLogAndEntries(T entity, K entity, M entity)....
OVERCOME this situation
I USE THE @Transactional in @Controller and Just make a Generic Save Method and save all the entities/ model using this simple save method. and if any method fail to save then all the transactions in controller rollback successfully.
Other situation that ensure that @Transactional should be use with @Controller
In @Controller:
pt.save(entity1);
pt.save(entity2);
int a = 2/0;
pt.save(entity3);
In case , @Transactional on Service, first 2 entity successfully saved but third not it not rollback all the transaction
In case , @Transactional on @Controller all the transaction rollback as exception occur
why stack-overflow asked , "Don't do transactions in your controller. Put them in your service layer classes."?
[source]
You are asking about best practice, and best practice is to mark @Transactional
in the service layer because a @Controller
should not be aware of data persistence in a MVC logic.
@Service
is constructed on use-case generated from analysis and knows about unit of works and is also realized thinking in terms of reuse: if you switch from a web context to a desktop one (for example, or some other visual frontend) where @Controller
layer doesn't exist you don't have problems because all is encapsulated in service layer.
A @Service
is a contract and a modification in presentation layer should not require a rewrite of @Service
code.
But Spring don't care about where you put your your transaction boundaries, you can put on @Controller
but your application may will be harder to be maintained.
I hope this is clear enough. Sorry if not; English is not my native language.
just so other know, interface annotations are discouraged
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.
Controller is firmly in the view layer, which can be changed at any time. The service still owns units of work and should operate correctly regardless of which view is accessing. My answer here still stands.
I created an upper-layer service which uses other (non-transactional) services. And the upper-layer service is transactional.
@Service
public class Service1Impl implements Servcie1 {
public Object method11(){...}
public Object method12(){...}
}
@Service
public class Service2Impl implements Service2 {
public method21(){...}
}
public interface Service1 {
public Object method11();
public Object method12();
}
public interface Service2 {
public Object method21();
}
@Transactional
public interface UpperLayerService {}
@Service
public class UpperLayerServiceImpl implements UpperLayerService {
@Autowired
private Service2 service2;
@Autowired
private Service3 service3;
public Object doWork() {
Object o1 = service1.method11();
Object o2 = service1.method12();
Object o3 = service2.method21();
....
Object res = null;//...Any code
return res;
}
}