ChatGPT解决这个技术问题 Extra ChatGPT

Where does the @Transactional annotation belong?

Should you place the @Transactional in the DAO classes and/or their methods or is it better to annotate the Service classes which are calling using the DAO objects? Or does it make sense to annotate both "layers"?


L
Lii

I 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.


I agree with that. Sometimes it does not matter, but sometimes you can benefit from that e.g. Hibernate session is spanned for the while transaction, so all loaded objects are in 1st-level cache and you don't need to reattach the objects to session again, plus lazily loaded properties function without fuzz.
Can a global transaction consist of more than one of these @Transactional(propagation = Propagation.REQUIRED) ? Or @Transactional is always a boundary for a transaction ? I'm not sure I got it from the documentation, but it seems a bit that you can create even transactions that consist of @Transactional methods and everything that runs inside
Yes, I think the outermost @Transactional is the one the becomes the boundary for the transaction if Propagation.REQUIRED is turned on.
We do not need to mention @Transactional explicitly when it is a single unit of work.
m
mnp

In general I agree with the others stating that transactions are usually started on the service level (depending on the granularity that you require of course).

However, in the mean time I also started adding @Transactional(propagation = Propagation.MANDATORY) to my DAO layer (and other layers that are not allowed to start transactions but require existing ones) because it is much easier to detect errors where you have forgotten to start a transaction in the caller (e.g. the service). If your DAO is annotated with mandatory propagation you will get an exception stating that there is no active transaction when the method is invoked.

I also have an integration test where I check all beans (bean post processor) for this annotation and fail if there is a @Transactional annotation with propagation other than Mandatory in a bean that does not belong to the services layer. This way I make sure we do not start transactions on the wrong layer.


Should this be done in the dao (interfaces) layer, in the dao impl. layer or both?
I don't know if it fits into this discussion, but another tip could be add @Transactional(readOnly = true) in the dao impl in the non-writing operations.
@Johan Spring advises to put the Transaction annotations on the implementation classes instead of the interfaces.
I really like this idea, trying that for my projects too
Lemme see if I understand... are you saying I should put @Transactional on the Service implementation class, and I should put @Transactional(propagation = MANDATORY) on the DAO (repository) class implementation?
A
Abdul Rahman

Transactional Annotations should be placed around all operations that are inseparable.

For example, your call is "change password". That consists of two operations

Change the password. Audit the change. Email the client that the password has changed.

So in the above, if the audit fails, then should the password change also fail? If so, then the transaction should be around 1 and 2 (so at the service layer). If the email fails (probably should have some kind of fail safe on this so it won't fail) then should it roll back the change password and the audit?

These are the kind of questions you need to be asking when deciding where to put the @Transactional.


n
naXa stands with Ukraine

The correct answer for traditional Spring architectures is to place transactional semantics on the service classes, for the reasons that others have already described.

An emerging trend in Spring is toward domain-driven design (DDD). Spring Roo exemplifies the trend nicely. The idea is to make the domain object POJOs a lot richer than they are on typical Spring architectures (usually they are anemic), and in particular to put transaction and persistence semantics on the domain objects themselves. In cases where all that's needed is simple CRUD operations, the web controllers operate directly on the domain object POJOs (they're functioning as entities in this context), and there's no service tier. In cases where there's some kind of coordination needed between domain objects, you can have a service bean handle that, with @Transaction as per tradition. You can set the transaction propagation on the domain objects to something like REQUIRED so that the domain objects use any existing transactions, such as transactions that were started at the service bean.

Technically this technique makes use of AspectJ and <context:spring-configured />. Roo uses AspectJ inter-type definitions to separate the entity semantics (transactions and persistence) from the domain object stuff (basically fields and business methods).


h
hammarback

The normal case would be to annotate on a service layer level, but this really depends on your requirements.

Annotating on a service layer will result in longer transactions than annotating on DAO level. Depending on the transaction isolation level that can youse problems, as concurrent transactions wont see each other's changes in eg. REPEATABLE READ.

Annotating on the DAOs will keep the transactions as short as possible, with the drawback that the functionality your service layer is exposing wont be done in a single (rollbackable) transaction.

It does not make sense to annotate both layers if the propagation mode is set to default.


n
naXa stands with Ukraine

I place the @Transactional on the @Service layer and set rollbackFor any exception and readOnly to optimize the transaction further.

By default @Transactional will only look for RuntimeException (Unchecked Exceptions), by setting rollback to Exception.class (Checked Exceptions) it will rollback for any exception.

@Transactional(readOnly = false, rollbackFor = Exception.class)

See Checked vs. Unchecked Exceptions.


m
martoncsukas

For Transaction in database level

mostly I used @Transactional in DAO's just on method level, so configuration can be specifically for a method / using default (required)

DAO's method that get data fetch (select .. ) - don't need @Transactional this can lead to some overhead because of transaction interceptor / and AOP proxy that need to be executed as well. DAO's methods that do insert / update will get @Transactional

very good blog on transctional

For application level - I am using transactional for business logic I would like to be able rolback in case of unexpected error

@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){

    try {    
        //service logic here     
    } catch(Throwable e) {

        log.error(e)
        throw new MyApplicationException(..);
    }
}

+1 very nice article about Transactional in Java
n
naXa stands with Ukraine

Or does it make sense to annotate both "layers"? - doesn't it make sense to annotate both the service layer and the dao layer - if one wants to make sure that DAO method is always called (propagated) from a service layer with propagation "mandatory" in DAO. This would provide some restriction for DAO methods from being called from UI layer (or controllers). Also - when unit testing DAO layer in particular - having DAO annotated will also ensure it is tested for transactional functionality.


Wouldn't doing this result in a nested transactions? And all the subtle problems that go along with it?
No, there's no nested transactions in JPA. Putting them in both would be perfectly fine - if you're already in a transaction when you hit the DAO, that transaction would be continued.
Nested transaction can occur if you use propagation=Propagation.REQUIRES_NEW. Otherwise for most cases, including propogation=mandatory, the DAO will just participate in the existing transaction started by the service layer.
d
davidemm

Also, Spring recommends only using the annotation on concrete classes and not interfaces.

http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html


H
Harshal Patil

@Transactional Annotations should be placed around all operations that are inseparable. Using @Transactional transaction propagation are handled automatically.In this case if another method is called by current method,then that method will have the option of joining the ongoing transaction.

So lets take example:

We have 2 model's i.e. Country and City. Relational Mapping of Country and City model is like one Country can have multiple Cities so mapping is like,

@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;

Here Country mapped to multiple cities with fetching them Lazily. So here comes role of @Transactinal when we retrieve Country object from database then we will get all the data of Country object but will not get Set of cities because we are fetching cities LAZILY.

//Without @Transactional
public Country getCountry(){
   Country country = countryRepository.getCountry();
   //After getting Country Object connection between countryRepository and database is Closed 
}

When we want to access Set of Cities from country object then we will get null values in that Set because object of Set created only this Set is not initialize with there data to get values of Set we use @Transactional i.e.,

//with @Transactional
@Transactional
public Country getCountry(){
   Country country = countryRepository.getCountry();
   //below when we initialize cities using object country so that directly communicate with database and retrieve all cities from database this happens just because of @Transactinal
   Object object = country.getCities().size();   
}

So basically @Transactional is Service can make multiple call in single transaction without closing connection with end point.


very informative, thank you! Just what I was looking for, an explaination of what @Transactional really is
y
yannick555

Usually, one should put a transaction at the service layer.

But as stated before, the atomicity of an operation is what tells us where an annotation is necessary. Thus, if you use frameworks like Hibernate, where a single "save/update/delete/...modification" operation on an object has the potential to modify several rows in several tables (because of the cascade through the object graph), of course there should also be transaction management on this specific DAO method.


P
Prags

Service layer is best place to add @Transactional annotations as most of the business logic present here, it contain detail level use-case behaviour.

Suppose we add it to DAO and from service we are calling 2 DAO classes , one failed and other success , in this case if @Transactional not on service one DB will commit and other will rollback.

Hence my recommendation is use this annotation wisely and use at Service layer only.

Github project- java-algos


Exceptions like ObjectOptimisticLockingFailureException occurs only after transaction completes. If you have seperate threads for other operation like mail service, this design totally fails. We are suffering right now. Only left solution would be AOP.
s
sundary

It is better to have it in the service layer! This is clearly explained on one of the article that I came across yesterday! Here is the link that you can check out!


A
Amiko

First of all let's define where we have to use transaction?

I think correct answer is - when we need to make sure that sequence of actions will be finished together as one atomic operation or no changes will be made even if one of the actions fails.

It is well known practice to put business logic into services. So service methods may contain different actions which must be performed as a single logical unit of work. If so - then such method must be marked as Transactional. Of course, not every method requires such limitation, so you don't need to mark whole service as transactional.

And even more - don't forget to take into account that @Transactional obviously, may reduce method performance. In order to see whole picture you have to know transaction isolation levels. Knowing that might help you avoid using @Transactional where it's not necessarily needed.


A
Alin

https://i.stack.imgur.com/WlLfh.png

The @Transactional should be used on service layer as it contains the business logic. The DAO layer usually has only database CRUD operations.

// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);
}

Spring doc : https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html


N
Nabin Kumar Khatiwada

It's better to keep @Transactional in a separate middle layer between DAO and Service Layer. Since, rollback is very important, you can place all your DB manipulation in the middle layer and write business logic in Service Layer. Middle layer will interact with your DAO layers.

This will help you in many situations like ObjectOptimisticLockingFailureException - This exception occurs only after your Transaction is over. So, you cannot catch it in middle layer but you can catch in your service layer now. This would not be possible if you have @Transactional in Service layer. Although you can catch in Controller but Controller should be as clean as possible.

If you are sending mail or sms in seperate thread after completing all the save,delete and update options, you can do this in service after Transaction is completed in your middle layer. Again, if you mention @Transactional in service layer, you mail will go even if your transaction fails.

So having a middle @Transaction layer will help to make your code better and easy to handle. Otherwise, If you use in DAO layer, you may not be able to Rollback all operations. If you use in Service layer, you may have to use AOP(Aspect Oriented Programming) in certain cases.


H
Harshal Patil

Ideally, Service layer(Manager) represents your business logic and hence it should be annotated with @Transactional.Service layer may call different DAO to perform DB operations. Lets assume a situations where you have N number of DAO operations in a service method. If your 1st DAO operation failed, others may be still passed and you will end up inconsistent DB state. Annotating Service layer can save you from such situations.


H
Harshal Patil

I prefer to use @Transactional on services layer at method level.


H
Harshal Patil

@Transactional uses in service layer which is called by using controller layer (@Controller) and service layer call to the DAO layer (@Repository) i.e data base related operation.