Answers:
我认为交易属于服务层。这是了解工作单元和用例的人。如果您将多个DAO注入到一个服务中,而这些DAO需要在单个事务中一起工作,那么这是正确的答案。
总的来说,我同意其他人的说法,即事务通常是在服务级别上启动的(当然,这取决于您所需的粒度)。
但是,与此同时,我也开始添加@Transactional(propagation = Propagation.MANDATORY)
到我的DAO层(以及其他不允许启动事务但需要现有事务的层),因为在您忘记在调用方中开始事务的地方,错误检测要容易得多(例如服务)。如果您的DAO带有强制传播注释,您将获得一个异常,指出在调用该方法时没有活动的事务。
我还有一个集成测试,在该测试中,我检查所有bean(bean后处理器)是否有此注释,如果@Transactional
在不属于服务层的bean中存在非强制传播的注释,则失败。这样,我确保我们不会在错误的层上启动事务。
@Transactional
使用Service实现类,还是应该@Transactional(propagation = MANDATORY)
使用DAO(存储库)类实现?
对于传统的Spring架构,正确的答案是将事务性语义放在服务类上,这是其他人已经描述过的原因。
Spring的新兴趋势是向域驱动设计(DDD)迈进。Spring Roo很好地体现了这一趋势。这样做的目的是使域对象POJO 比典型的Spring架构丰富得多(通常按照传统,它们是贫乏的),尤其是将事务和持久性语义放在域对象本身上。如果仅需要简单的CRUD操作,则Web控制器将直接在域对象POJO上操作(在这种情况下,它们充当实体),并且没有服务层。如果域对象之间需要某种协调,则可以使用@Transaction
。您可以将域对象上的事务传播设置为类似的值,REQUIRED
以便域对象使用任何现有事务,例如在服务bean处启动的事务。
从技术上讲,此技术利用AspectJ和<context:spring-configured />
。Roo使用AspectJ类型间定义将实体语义(事务和持久性)与域对象(基本是字段和业务方法)分开。
通常情况是在服务层级别上进行注释,但这实际上取决于您的要求。
与在DAO级别上进行注释相比,在服务层上进行注释将导致更长的事务。根据可能导致问题的事务隔离级别,因为并发事务不会看到彼此的变化,例如。可重复读取。
在DAO上进行注释将使事务尽可能短,其缺点是,服务层公开的功能不会在单个(可回滚)事务中完成。
如果传播模式设置为默认,则对两层都进行注释是没有意义的。
还是对两个“层”都进行注释是否有意义?-注释服务层和dao层都没有意义-如果要确保始终从在DAO中具有传播“强制性”的服务层调用(传播)DAO方法。这将为从UI层(或控制器)调用DAO方法提供一些限制。另外-特别是在对DAO层进行单元测试时-带有DAO注释的内容也将确保对它进行事务性功能测试。
propagation=Propagation.REQUIRES_NEW
。否则,对于大多数情况,包括“传播=强制性”,DAO将仅参与由服务层启动的现有事务。
此外,Spring建议仅在具体类上使用批注,而不在接口上使用批注。
http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
对于数据库级别的事务
我主要是@Transactional
在DAO的方法级别上使用的,因此配置可以专门用于方法/使用默认值(必填)
DAO的获取数据(选择..)的方法-不需要这样做
@Transactional
会导致一些开销,因为还需要执行事务拦截器/和AOP代理。
DAO的插入/更新方法将得到 @Transactional
关于transalal非常好的博客
对于应用程序级别-
我正在将事务性用于业务逻辑,希望在发生意外错误时能够回退
@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){
try {
//service logic here
} catch(Throwable e) {
log.error(e)
throw new MyApplicationException(..);
}
}
Transactional
中Java
通常,应该在服务层进行事务处理。
但是如前所述,操作的原子性告诉我们在哪里需要注释。因此,如果使用Hibernate之类的框架,则对对象的单个“保存/更新/删除/ ...修改”操作有可能修改多个表中的几行(由于通过对象图的级联),当然,也应该对此特定的DAO方法进行事务管理。
@Transactional
注释应放在所有不可分割的操作周围。使用@Transactional
事务传播是自动处理的。在这种情况下,如果当前方法调用了另一个方法,则该方法可以选择加入正在进行的事务。
让我们举个例子:
我们有2个模型的ie Country
和City
。Country
与City
模型的关系映射就像一个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可以在单个事务中进行多个调用,而无需关闭与端点的连接。
@Transactional
真正是什么的解释
本@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文档:https : //docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html
服务层是添加最好的地方@Transactional
注释,因为大多数在座的业务逻辑,它包含详细级别用例的行为。
假设我们将其添加到DAO中,并从服务中调用2个DAO类,一个失败,另一个成功,在这种情况下,如果@Transactional
不在服务中,则一个DB将提交而另一个将回滚。
因此,我的建议是明智地使用此批注,并且仅在服务层使用。
首先,让我们定义我们必须在哪里使用事务?
我认为正确的答案是-当我们需要确保一系列动作作为一个原子操作一起完成时,或者即使其中一个动作失败,也不会进行任何更改。
将业务逻辑放入服务中是众所周知的做法。因此,服务方法可能包含不同的操作,这些操作必须作为单个逻辑工作单元执行。如果是这样,则此方法必须标记为“ 事务性”。当然,并非每种方法都需要这种限制,因此您无需将整个服务都标记为transactional。
甚至更多-不要忘记考虑@Transactional显然会降低方法性能。为了了解整体情况,您必须了解事务隔离级别。知道这可能会帮助您避免在不必要的地方避免使用@Transactional。
最好将@Transactional保留在DAO和服务层之间的单独中间层中。由于回滚非常重要,因此您可以将所有数据库操作置于中间层,并在服务层中编写业务逻辑。中间层将与您的DAO层进行交互。
这将在许多情况下为您提供帮助,例如ObjectOptimisticLockingFailureException-仅在您的交易结束后才会发生此异常。因此,您不能在中间层捕获它,但现在可以在服务层捕获它。如果在Service层中有@Transactional,则不可能。尽管您可以抓住Controller,但是Controller应该尽可能干净。
如果您在完成所有保存,删除和更新选项后在单独的线程中发送邮件或短信,则可以在中间层的事务完成后在服务中执行此操作。同样,如果在服务层中提到@Transactional,即使事务失败,您的邮件也会通过。
因此,在中间的@Transaction层将有助于使您的代码更好且易于处理。否则,如果在DAO层中使用,则可能无法回滚所有操作。如果在服务层中使用,则在某些情况下可能必须使用AOP(面向方面的编程)。