@Transactional
放在DAO
类和/或它们的方法中,还是最好对使用DAO对象调用的Service类进行注释?还是注释两个“图层”是否有意义?#1 楼
我认为交易属于服务层。这是了解工作单元和用例的人。如果您将多个DAO注入到一个服务中,而这些DAO需要在单个事务中一起工作,那么这是正确的答案。#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个模型,即
Country
和City
。 Country
和City
模型的关系映射就像一个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
)的调用即与数据库相关的操作。
评论
我同意这一点。有时并不重要,但有时您可以从中受益,例如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