Where does the @Transactional annotation belong?
您应该将
我认为交易属于服务层。这是了解工作单位和用例的人。如果您将几个DAO注入到需要在单个事务中协同工作的服务中,那么这是正确的答案。
总的来说,我同意其他人的观点,即交易通常是在服务级别上启动的(取决于您当然需要的粒度)。
但是,与此同时,我也开始将
我还有一个集成测试,我检查所有bean(bean post processor)以获取此注释,如果在不属于服务层的bean中存在除了Mandatory之外的传播的
Transactional Annotations应围绕所有不可分割的操作。
例如,您的电话是"更改密码"。这包括两个操作
那么在上面,如果审核失败,那么密码更改是否也会失败?如果是这样,那么交易应该在1和2附近(所以在服务层)。如果电子邮件失败(可能应该具有某种故障安全性,因此它不会失败)那么它应该回滚更改密码和审核吗?
这些是在决定放置
传统Spring体系结构的正确答案是在服务类上放置事务语义,这是其他人已经描述过的原因。
Spring的一个新兴趋势是面向域驱动设计(DDD)。 Spring Roo很好地体现了这一趋势。我们的想法是使域对象POJO比它们在典型的Spring架构上更丰富(通常它们是贫血的),特别是将事务和持久性语义放在域对象本身上。如果需要的只是简单的CRUD操作,那么Web控制器直接在域对象POJO上运行(它们在这种情况下作为实体运行),并且没有服务层。如果域对象之间需要某种协调,则可以使用服务bean句柄,按照传统使用
从技术上讲,这种技术使用了AspectJ和
通常的情况是在服务层级注释,但这实际上取决于您的要求。
在服务层上添加注释将导致比在DAO级别上注释更长的事务。取决于可以解决问题的事务隔离级别,因为并发事务不会看到彼此的更改,例如。可重复阅读。
对DAO进行注释将使事务尽可能短,但缺点是服务层公开的功能不会在单个(可回滚)事务中完成。
如果传播模式设置为默认值,则注释两个图层没有意义。
我将
默认情况下,
1 |
请参阅已检查与未检查的例外。
或者注释两个"层??"是否有意义? - 注意服务层和dao层都没有意义 - 如果想要确保始终从DAO中具有传播"强制"的服务层调用(传播)DAO方法。这将为从UI层(或控制器)调用DAO方法提供一些限制。此外 - 当特别是对DAO层进行单元测试时 - 对DAO进行注释也将确保对其进行交易功能测试。
此外,Spring建议仅在具体类而不是接口上使用注释。
http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
对于数据库级别的事务
大多数情况下我在DAO中仅使用
DAO的获取数据的方法(选择..) - 不需要
需要执行的事务拦截器/和AOP代理
好。
DAO的插入/更新方法将获得
关于transctional的非常好的博客
对于应用程序级别
我正在使用事务性的业务逻辑我希望能够在意外错误的情况下回滚
1 2 3 4 5 6 7 8 9 10 11 | @Transactional(rollbackFor={MyApplicationException.class}) public void myMethod(){ try { //service logic here } catch(Throwable e) { log.error(e) throw new MyApplicationException(..); } } |
通常,应该在服务层放置一个事务。
但如前所述,操作的原子性告诉我们注释在哪里是必要的。因此,如果你使用像Hibernate这样的框架,对一个对象的单个"保存/更新/删除/修改"操作有可能修改几个表中的几行(因为通过对象图层级联),当然还应该对这种特定的DAO方法进行事务管理。
使用
让我们举个例子:
我们有2个模型,即
1 2 | @OneToMany(fetch = FetchType.LAZY, mappedBy="country") private Set<City> cities; |
此处国家/地区映射到多个城市并提取它们
1 2 3 4 5 | //Without @Transactional public Country getCountry(){ Country country = countryRepository.getCountry(); //After getting Country Object connection between countryRepository and database is Closed } |
当我们想要从country对象访问City of Cities时,我们将在该Set中获取null值,因为Set的对象只创建了这个Set没有初始化那里有数据来获取Set我们使用
1 2 3 4 5 6 7 | //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。当然,并非每种方法都需要这样的限制,因此您不需要将整个服务标记为事务性的。
甚至更多 - 不要忘记考虑到@Transactional显然,可能会降低方法性能。
为了查看整个图片,您必须知道事务隔离级别。知道这可能会帮助您避免在不一定需要的地方使用@Transactional。
服务层是添加
假设我们将它添加到DAO,并且从服务我们调用2个DAO类,一个失败并且其他成功,在这种情况下,如果
因此,我的建议是明智地使用此注释,并仅在服务层使用。
Github项目 - java-algos
理想情况下,服务层(Manager)代表您的业务逻辑,因此应使用
我更喜欢在方法级别的服务层上使用
1 2 3 4 5 6 7 8 9 10 11 12 |
Spring doc:https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html
最好将@Transactional保存在DAO和服务层之间的单独中间层中。
由于回滚非常重要,您可以将所有数据库操作放在中间层,并在服务层中编写业务逻辑。中间层将与您的DAO层交互。
这将在许多情况下帮助您,如ObjectOptimisticLockingFailureException - 只有在您的Transaction结束后才会发生此异常。所以,你不能在中间层抓住它,但你现在可以抓住你的服务层。如果您在服务层中有@Transactional,则无法进行此操作。虽然你可以捕获控制器,但控制器应尽可能干净。
如果您在完成所有保存,删除和更新选项后以单独的线程发送邮件或短信,则可以在中间层完成交易后在服务中执行此操作。同样,如果您在服务层中提及@Transactional,即使您的交易失败,您也会收到邮件。
因此,拥有一个中间的@Transaction层将有助于使您的代码更好,更容易处理。除此以外,
如果在DAO层中使用,则可能无法回滚所有操作。
如果在Service层中使用,则在某些情况下可能必须使用AOP(面向方面??编程)。