@Transactional注释在哪里?

Tho*_*ler 504 java spring annotations dao transactions

你应该把它@Transactional放在DAO类和/或它们的方法中,还是更好地注释使用DAO对象调用的Service类?或者注释两个"层​​"是否有意义?

duf*_*ymo 516

我认为交易属于服务层.这是了解工作单位和用例的人.如果您将几个DAO注入到需要在单个事务中协同工作的服务中,那么这是正确的答案.

  • 全局事务可以包含多个@Transactional(propagation = Propagation.REQUIRED)吗?或@Transactional始终是交易的边界?我不确定我是从文档中得到的,但是有点你甚至可以创建包含@Transactional方法和内部运行的所有事务的事务 (5认同)

小智 299

总的来说,我同意其他人的观点,即交易通常是在服务级别上启动的(取决于您当然需要的粒度).

但是,与此同时,我也开始添加@Transactional(propagation = Propagation.MANDATORY)到我的DAO层(以及其他不允许启动事务但需要现有事务的层),因为更容易检测到忘记在调用者中启动事务的错误(例如服务).如果您的DAO使用强制传播进行注释,您将收到一个异常,指出在调用该方法时没有活动事务.

我还有一个集成测试,我检查所有bean(bean post processor)以获取此注释,如果@Transactional在不属于服务层的bean中存在除Mandatory以外的传播的注释,则会失败.这样我确保我们不会在错误的层上启动事务.

  • 这是一个好主意.希望我可以投票10次. (46认同)
  • @Johan Spring建议将Transaction注释放在实现类而不是接口上. (8认同)
  • 我不知道它是否适合这个讨论,但另一个提示可能是在非写入操作中的dao impl中添加@Transactional(readOnly = true). (3认同)
  • 这应该在dao impl中的dao(接口)层中完成.一层或两层? (2认同)

Mic*_*les 89

Transactional Annotations应围绕所有不可分割的操作.

例如,您的电话是"更改密码".这包括两个操作

  1. 更改密码.
  2. 审核变更.
  3. 通过电子邮件向客户端发送密码已更改.

那么在上面,如果审核失败,那么密码更改是否也会失败?如果是这样,那么交易应该在1和2附近(所以在服务层).如果电子邮件失败(可能应该具有某种故障安全性,因此它不会失败)那么它应该回滚更改密码和审核吗?

这些是你在决定把它放在哪里时需要问的问题@Transactional.


Wil*_*ler 39

传统Spring体系结构的正确答案是在服务类上放置事务语义,这是其他人已经描述过的原因.

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按照传统.您可以将域对象上的事务传播设置为类似,REQUIRED以便域对象使用任何现有事务,例如在服务bean上启动的事务.

从技术上讲,这种技术利用了AspectJ和<context:spring-configured />.Roo使用AspectJ交互式定义将实体语义(事务和持久性)与域对象(基本上是字段和业务方法)分开.


ham*_*ack 37

通常的情况是在服务层级注释,但这实际上取决于您的要求.

在服务层上添加注释将导致比在DAO级别上注释更长的事务.取决于可以解决问题的事务隔离级别,因为并发事务不会看到彼此的更改,例如.可重复阅读.

对DAO进行注释将使事务尽可能短,但缺点是服务层公开的功能不会在单个(可回滚)事务中完成.

如果传播模式设置为默认值,则注释两个图层没有意义.


use*_*995 30

我将它@Transactional放在@Service图层上并设置rollbackFor任何异常并readOnly进一步优化事务.

默认情况下,@Transactional只查找RuntimeException(未经检查的异常),通过将回滚设置为Exception.class(已检查的异常),它将回滚任何异常.

@Transactional(readOnly = false, rollbackFor = Exception.class)
Run Code Online (Sandbox Code Playgroud)

请参阅已检查与未检查的例外.


小智 16

或者注释两个"层​​"是否有意义? - 注意服务层和dao层都没有意义 - 如果想要确保始终从DAO中具有传播"强制"的服务层调用(传播)DAO方法.这将为从UI层(或控制器)调用DAO方法提供一些限制.此外 - 当特别是对DAO层进行单元测试时 - 对DAO进行注释也将确保对其进行交易功能测试.

  • 不,JPA中没有嵌套事务.把它们放在两者中都是完全没问题的 - 如果你在DAO中已经处于交易中,那么交易就会继续. (11认同)
  • 如果使用`propagation = Propagation.REQUIRES_NEW`,则可能发生嵌套事务.否则,对于大多数情况,包括propogation = mandatory,DAO将只参与服务层启动的现有事务. (4认同)

dav*_*emm 15

此外,Spring建议仅在具体类而不是接口上使用注释.

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


luk*_*s77 13

对于数据库级别的事务

@Transactional在DAO中使用的只是方法级别,所以配置可以专门用于方法/使用默认值(必需)

  1. 获取数据获取的DAO方法(选择..) - 不需要 @Transactional 这样可能会导致一些开销,因为事务拦截器/和AOP代理也需要执行.

  2. DAO的插入/更新方法将得到 @Transactional

关于transctional的非常好的博客

对于应用程序级别 -
我使用事务性的业务逻辑我希望能够在出现意外错误时进行回滚

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

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

        log.error(e)
        throw new MyApplicationException(..);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 关于`Java`中的`Transactional`的+1非常好的文章 (3认同)

小智 11

通常,应该在服务层放置一个事务.

但如前所述,操作的原子性告诉我们注释在哪里是必要的.因此,如果您使用像Hibernate这样的框架,其中对对象的单个"保存/更新/删除/修改"操作可能会修改多个表中的多个行(因为通过对象图层级联),当然还应该对这种特定的DAO方法进行事务管理.


Har*_*til 10

@Transactional应该围绕所有不可分割的操作放置注释.使用@Transactional事务传播是自动处理的.在这种情况下,如果当前方法调用另一个方法,那么该方法将具有加入正在进行的事务的选项.

让我们举个例子:

我们有2个模型即CountryCity.关系映射CountryCity模型就像一个Country可以有多个城市,所以映射就像,

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

此处国家/地区映射到多个城市并提取它们Lazily.因此,@Transactinal当我们从数据库中检索Country对象时,我们将获得Country对象的所有数据,但是由于我们正在获取城市,因此不会获得城市集LAZILY.

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

当我们想要从country对象访问City of Cities时,我们将在该Set中获取null值,因为Set的对象只创建了这个Set没有初始化那里有数据来获取我们使用的Set的值@Transactionalie,

//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();   
}
Run Code Online (Sandbox Code Playgroud)

所以基本上@Transactional服务可以在单个事务中进行多次调用而无需关闭与端点的连接.

  • 信息量很大,谢谢!正是我一直在寻找的,对“@Transactional”真正是什么的解释 (2认同)

小智 6

最好将它放在服务层中!昨天我遇到的一篇文章清楚地解释了这一点!这是您可以查看的链接!


sma*_*kov 5

首先让我们定义我们必须在哪里使用事务

我认为正确的答案是 - 当我们需要确保一系列动作将作为一个原子操作一起完成时,或者即使其中一个动作失败也不会进行任何更改。

将业务逻辑放入服务中是众所周知的做法。因此,服务方法可能包含不同的操作,这些操作必须作为单个逻辑工作单元来执行。如果是,则必须将此类方法标记为Transactional。当然,并非每种方法都需要这样的限制,因此您无需将整个服务标记为transactional

甚至更多 - 不要忘记考虑到@Transactional显然可能会降低方法性能。为了查看全貌,您必须了解事务隔离级别。知道这可能会帮助您避免在不一定需要的地方使用@Transactional


小智 5

服务层是添加@Transactional注释的最佳位置,因为这里存在的大多数业务逻辑,它包含详细级别的用例行为。

假设我们将它添加到 DAO 并从服务中调用 2 个 DAO 类,一个失败,另一个成功,在这种情况下,如果@Transactional不在服务上,一个数据库将提交,另一个将回滚。

因此我的建议是明智地使用此注释并仅在服务层使用。

Github 项目-java-algos


Ali*_*lin 5

在此处输入图片说明

@Transactional应在服务层,它包含业务逻辑来使用。DAO 层通常只有数据库 CRUD 操作。

// 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);
}
Run Code Online (Sandbox Code Playgroud)

弹簧文档:https : //docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html


Nab*_*ada 5

最好将@Transactional保留在 DAO 和服务层之间的单独中间层中。由于回滚非常重要,您可以将所有数据库操作放在中间层,并在服务层编写业务逻辑。中间层将与您的 DAO 层交互。

这将在许多情况下为您提供帮助,例如ObjectOptimisticLockingFailureException - 此异常仅在您的事务结束后发生。所以,你不能在中间层捕捉它,但你现在可以在你的服务层捕捉它。如果您在服务层有 @Transactional,这是不可能的。虽然你可以在 Controller 中捕获,但 Controller 应该尽可能干净。

如果您在完成所有保存、删除和更新选项后在单独的线程中发送邮件或短信,您可以在中间层完成事务后在服务中执行此操作。同样,如果您在服务层提及@Transactional,即使您的交易失败,您的邮件也会发送。

因此,拥有一个中间的 @Transaction 层将有助于使您的代码更好且易于处理。否则,如果在 DAO 层使用,可能无法回滚所有操作。如果您在服务层使用,在某些情况下您可能必须使用AOP(面向方面​​的编程)。