@Transactional注释在哪里?


Answers:


537

我认为交易属于服务层。这是了解工作单元和用例的人。如果您将多个DAO注入到一个服务中,而这些DAO需要在单个事务中一起工作,那么这是正确的答案。


我同意这一点。有时并不重要,但有时您可以从中受益,例如,在进行while事务时会跨越Hibernate会话,因此所有已加载的对象都位于第一级缓存中,您无需将对象重新附加到会话中,也无需延迟加载属性功能不模糊。
dma_k 2010年

5
全局事务可以包含多个@Transactional(propagation = Propagation.REQUIRED)吗?还是@Transactional始终是事务的边界?我不确定我是从文档中得到的,但是似乎您甚至可以创建包含@Transactional方法和其中运行的所有内容的事务
lisak 2010年

1
是的,我认为最外层的@Transactional是启用Propagation.REQUIRED的事务的边界。
duffymo's


309

总的来说,我同意其他人的说法,即事务通常是在服务级别上启动的(当然,这取决于您所需的粒度)。

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

我还有一个集成测试,在该测试中,我检查所有bean(bean后处理器)是否有此注释,如果@Transactional在不属于服务层的bean中存在非强制传播的注释,则失败。这样,我确保我们不会在错误的层上启动事务。


3
是否应在dao impl中的dao(接口)层中完成此操作。层还是两者兼有?
2013年

3
我不知道它是否适合本次讨论,但是另一个提示可以在非写操作的dao impl中添加@Transactional(readOnly = true)。
maxivis

10
@Johan Spring建议将Transaction注释放在实现类上,而不是在接口上。
Mathias G.

我真的很喜欢这个主意,想的是为我的项目太
托马斯·埃沃勒

1
Lemme看看我是否了解...您是说我应该@Transactional使用Service实现类,还是应该@Transactional(propagation = MANDATORY)使用DAO(存储库)类实现?
John Henckel

93

事务注释应放在所有不可分割的操作周围。

例如,您的呼叫是“更改密码”。这包括两个操作

  1. 修改密码。
  2. 审核更改。
  3. 向客户端发送密码已更改的电子邮件。

因此,在上面的内容中,如果审核失败,那么密码更改是否也会失败?如果是这样,则事务应该在1和2左右(因此在服务层)。如果电子邮件失败(可能应该对此进行某种故障保护,这样就不会失败),那么它应该回滚更改密码和审核吗?

这些是在确定放置位置时需要问的问题@Transactional


41

对于传统的Spring架构,正确的答案是将事务性语义放在服务类上,这是其他人已经描述过的原因。

Spring的新兴趋势是向域驱动设计(DDD)迈进。Spring Roo很好地体现了这一趋势。这样做的目的是使域对象POJO 比典型的Spring架构丰富得多(通常按照传统,它们是贫乏的),尤其是将事务和持久性语义放在域对象本身上。如果仅需要简单的CRUD操作,则Web控制器将直接在域对象POJO上操作(在这种情况下,它们充当实体),并且没有服务层。如果域对象之间需要某种协调,则可以使用@Transaction。您可以将域对象上的事务传播设置为类似的值,REQUIRED以便域对象使用任何现有事务,例如在服务bean处启动的事务。

从技术上讲,此技术利用AspectJ和<context:spring-configured />。Roo使用AspectJ类型间定义将实体语义(事务和持久性)与域对象(基本是字段和业务方法)分开。


38

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

与在DAO级别上进行注释相比,在服务层上进行注释将导致更长的事务。根据可能导致问题的事务隔离级别,因为并发事务不会看到彼此的变化,例如。可重复读取。

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

如果传播模式设置为默认,则对两层都进行注释是没有意义的。


31

我将其放置@Transactional在该@Service层上并设置rollbackFor任何异常,然后readOnly进一步优化事务。

默认情况下@Transactional将仅查找RuntimeException(Unchecked Exceptions),通过将rollback设置为Exception.class(Checked Exceptions),它将回滚任何异常。

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

请参阅检查与未检查异常


17

还是对两个“层”都进行注释是否有意义?-注释服务层和dao层都没有意义-如果要确保始终从在DAO中具有传播“强制性”的服务层调用(传播)DAO方法。这将为从UI层(或控制器)调用DAO方法提供一些限制。另外-特别是在对DAO层进行单元测试时-带有DAO注释的内容也将确保对它进行事务性功能测试。


这样做不会导致嵌套交易吗?以及随之而来的所有细微问题?
HDave 2012年

11
不,JPA中没有嵌套事务。将它们放在两者中都很好-如果您在点击DAO时已经在进行事务处理,那么该事务将继续进行。
fool4jesus

4
如果使用,则可能发生嵌套事务propagation=Propagation.REQUIRES_NEW。否则,对于大多数情况,包括“传播=强制性”,DAO将仅参与由服务层启动的现有事务。
丹·卡特2013年


15

对于数据库级别的事务

我主要是@Transactional在DAO的方法级别上使用的,因此配置可以专门用于方法/使用默认值(必填)

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

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

关于transalal非常好的博客

对于应用程序级别-
我正在将事务性用于业务逻辑,希望在发生意外错误时能够回退

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

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

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

6
+1约很不错的文章TransactionalJava
橡木

11

通常,应该在服务层进行事务处理。

但是如前所述,操作的原子性告诉我们在哪里需要注释。因此,如果使用Hibernate之类的框架,则对对象的单个“保存/更新/删除/ ...修改”操作有可能修改多个表中的几行(由于通过对象图的级联),当然,也应该对此特定的DAO方法进行事务管理。


10

@Transactional注释应放在所有不可分割的操作周围。使用@Transactional事务传播是自动处理的。在这种情况下,如果当前方法调用了另一个方法,则该方法可以选择加入正在进行的事务。

让我们举个例子:

我们有2个模型的ie CountryCityCountryCity模型的关系映射就像一个Country城市可以有多个城市,因此映射就像,

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

在这里,Country映射到多个城市并获取它们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 
}

当我们要从国家对象访问城市集时,我们将在该集合中获得空值,因为仅创建了该集合的Set的对象并未初始化那里的数据来获取我们使用的Set的值,@Transactional即,

//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();   
}

因此,基本上@Transactional,Service可以在单个事务中进行多个调用,而无需关闭与端点的连接。


2
非常翔实,谢谢!正是我所寻找的,是对@Transactional真正是什么的解释
CybeX

6

最好在服务层中使用它!我昨天遇到的一篇文章对此做了清楚的解释!这是您可以签出的链接



5

服务层是添加最好的地方@Transactional注释,因为大多数在座的业务逻辑,它包含详细级别用例的行为。

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

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

Github项目-Java算法


诸如ObjectOptimisticLockingFailureException之类的异常仅在事务完成之后发生。如果您有用于其他操作(如邮件服务)的单独线程,则此设计将完全失败。我们现在正在受苦。只有剩下的解决方案是AOP。
Nabin Kumar Khatiwada '18

4

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

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

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

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


3

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

这将在许多情况下为您提供帮助,例如ObjectOptimisticLockingFailureException-仅在您的交易结束后才会发生此异常。因此,您不能在中间层捕获它,但现在可以在服务层捕获它。如果在Service层中有@Transactional,则不可能。尽管您可以抓住Controller,但是Controller应该尽可能干净。

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

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


2

理想情况下,Service layer(Manager)表示您的业务逻辑,因此应在其上加上注释@Transactional。Service层可以调用不同的DAO来执行数据库操作。让我们假设一个服务方法中有N个DAO操作的情况。如果您的第一个DAO操作失败,则其他操作可能仍会通过,并且您最终将获得不一致的数据库状态。注释服务层可以使您免于此类情况。



1

@Transactional在服务层中使用,通过使用控制器层(@Controller)和服务层对DAO层(@Repository)的调用来调用,即与数据库相关的操作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.