您应该将@Transactional放在DAO类和/或它们的方法中,还是最好对使用DAO对象调用的Service类进行注释?还是注释两个“图层”是否有意义?

#1 楼

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

评论


我同意这一点。有时并不重要,但有时您可以从中受益,例如Hibernate会话是针对while事务而进行的,因此所有已加载的对象都位于第一级缓存中,您无需将这些对象重新附加到会话中,而且懒惰地加载了属性功能而没有模糊性。

– dma_k
2010-09-26 18:47

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

– lisak
2010年11月4日23:56

是的,我认为最外面的@Transactional是启用Propagation.REQUIRED的事务的边界。

–duffymo
2010年11月5日,0:10

stackoverflow.com/questions/18498115/…

– Shahid Ghafoor
13年8月28日在21:06

当@Transactional是单个工作单元时,我们无需明确提及。

–巴拉吉(Balaji Sudharsanam)
12月21日17:46

#2 楼

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

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

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

评论


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

–约翰
13年3月13日在9:05

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

– maxivis
13年11月29日在0:07

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

– Mathias G.
2013年12月6日上午10:07

我真的很喜欢这个想法,也可以在我的项目中尝试

–托马斯·艾恩沃勒
2014年2月7日在11:27

让我看看我是否理解...您是说我应该将@Transactional放在Service实现类上,而应该将@Transactional(propagation = MANDATORY)放在DAO(存储库)类实现上?

–约翰·亨克尔(John Henckel)
17年7月7日在21:52

#3 楼

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

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


更改密码。
审核更改。
向客户端发送密码已更改的电子邮件。

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

这些是您需要的问题在决定将@Transactional放在哪里时会问。

#4 楼

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

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

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

#5 楼

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

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

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

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

#6 楼

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

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

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


请参阅已检查的异常与未检查的异常。

#7 楼

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

评论


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

– HDave
2012年6月12日在20:02

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

–fool4耶稣
13年1月24日在21:08

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

–丹·卡特
13-10-30在20:13



#8 楼

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

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

#9 楼

对于数据库级别的事务

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


DAO的获取数据的方法(选择..)-不需要
@Transactional这会导致一些开销,因为
事务拦截器和AOP代理需要执行。
DAO进行插入/更新的方法将得到@Transactional

关于横向翻译的很好的博客

对于应用程序级别-
我正在使用事务性事务逻辑我希望能够在意外错误的情况下回滚

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

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

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


评论


+1关于Java中事务性的非常好的文章

–橡木
2014年5月5日在9:17

#10 楼

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

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

#11 楼

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

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

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


这里将国家/地区映射到多个城市并获取Lazily。因此,当我们从数据库中检索Country对象时,这里出现@Transactinal的角色,那么我们将获取Country对象的所有数据,但由于获取城市LAZILY而不会获取City Set。

//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可以在单个事务中进行多次调用而无需关闭与端点的连接。

评论


非常翔实,谢谢!正是我在寻找什么,@ Transactional的真正含义是什么

–CybeX
18年6月18日在21:04

#12 楼

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

#13 楼

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

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

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

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

#14 楼



@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);
}


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

#15 楼

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

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

因此,我的建议是明智地使用此注释,并仅在Service层使用。 >
Github项目-java-algos

评论


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

– Nabin Kumar Khatiwada
18年8月2日在8:55



#16 楼

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

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

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

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

#17 楼

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

#18 楼

我更喜欢在方法级别的服务层上使用@Transactional

#19 楼

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