【发布时间】:2010-11-07 22:11:29
【问题描述】:
您应该将@Transactional 放在DAO 类和/或其方法中,还是最好注释使用DAO 对象调用的服务类?或者注释两个“层”有意义吗?
【问题讨论】:
标签: java spring annotations transactions dao
您应该将@Transactional 放在DAO 类和/或其方法中,还是最好注释使用DAO 对象调用的服务类?或者注释两个“层”有意义吗?
【问题讨论】:
标签: java spring annotations transactions dao
我认为事务属于服务层。它了解工作单元和用例。如果您将多个 DAO 注入到需要在单个事务中协同工作的服务中,这是正确的答案。
【讨论】:
总的来说,我同意其他人的观点,即事务通常在服务级别启动(当然取决于您需要的粒度)。
然而,与此同时,我也开始将@Transactional(propagation = Propagation.MANDATORY) 添加到我的 DAO 层(以及其他不允许启动事务但需要现有事务的层),因为在您忘记启动的地方更容易检测到错误调用者(例如服务)中的事务。如果您的 DAO 使用强制传播进行注释,您将收到一个异常,指出调用该方法时没有活动事务。
我还有一个集成测试,我检查所有 bean(bean 后处理器)是否有这个注解,如果在不属于服务层的 bean 中有一个 @Transactional 具有传播以外的传播的注解,则失败。这样我可以确保我们不会在错误的层上启动事务。
【讨论】:
@Transactional放在Service实现类上,我应该把@Transactional(propagation = MANDATORY)放在DAO(存储库)类实现上?
事务性注解应该放在所有不可分割的操作周围。
例如,您的呼叫是“更改密码”。这包括两个操作
那么在上面,如果审核失败,那么密码更改是否也应该失败?如果是这样,那么事务应该在 1 和 2 左右(在服务层也是如此)。如果电子邮件失败(可能应该对此进行某种故障保护,以免失败),那么它是否应该回滚更改密码和审核?
这些是您在决定将@Transactional 放在哪里时需要提出的问题。
【讨论】:
传统 Spring 架构的正确答案是将事务语义放在服务类上,原因其他人已经描述过。
春季的一个新兴趋势是domain-driven design (DDD)。 Spring Roo 很好地体现了这一趋势。这个想法是使域对象 POJO 比典型的 Spring 架构(通常是 anemic)更多 richer,特别是在域对象本身上放置事务和持久性语义。在只需要简单的 CRUD 操作的情况下,Web 控制器直接在域对象 POJO 上操作(它们在此上下文中作为实体运行),并且没有服务层。在域对象之间需要某种协调的情况下,您可以使用服务 bean 处理它,按照传统使用@Transaction。您可以将域对象上的事务传播设置为 REQUIRED 之类的内容,以便域对象使用任何现有事务,例如在服务 bean 上启动的事务。
从技术上讲,这种技术利用了 AspectJ 和 <context:spring-configured />。 Roo 使用 AspectJ 类型间定义将实体语义(事务和持久性)与域对象内容(基本上是字段和业务方法)分开。
【讨论】:
通常情况下是在服务层级别进行注释,但这实际上取决于您的要求。
在服务层进行注释将导致比在 DAO 级别进行注释更长的事务。取决于可以解决问题的事务隔离级别,因为并发事务不会看到彼此的更改,例如。可重复阅读。
在 DAO 上注释将使事务尽可能短,缺点是您的服务层公开的功能不会在单个(可回滚)事务中完成。
如果传播模式设置为默认,则对两个层都进行注释是没有意义的。
【讨论】:
我将@Transactional 放在@Service 层上并设置rollbackFor 任何异常和readOnly 以进一步优化事务。
默认情况下,@Transactional 只会查找 RuntimeException(未检查异常),通过将回滚设置为 Exception.class(已检查异常),它将回滚任何异常。
@Transactional(readOnly = false, rollbackFor = Exception.class)
【讨论】:
对于数据库级别的事务
我主要在 DAO 中使用 @Transactional 只是在方法级别,因此配置可以专门针对方法/使用默认值(必需)
DAO 获取数据的方法(选择 ..) - 不需要
@Transactional 这可能会导致一些开销,因为
需要执行的事务拦截器/和 AOP 代理
好吧。
DAO 的插入/更新方法将得到@Transactional
transctional 上非常好的博客
对于应用程序级别 -
我正在使用事务性的业务逻辑我希望能够在出现意外错误的情况下回滚
@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){
try {
//service logic here
} catch(Throwable e) {
log.error(e)
throw new MyApplicationException(..);
}
}
【讨论】:
Transactional 的非常好的文章Java
或者注释两个“层”是否有意义? - 注释服务层和 dao 层是否有意义 - 如果要确保 DAO 方法是总是从服务层调用(传播),在 DAO 中传播“强制”。这将为从 UI 层(或控制器)调用 DAO 方法提供一些限制。此外 - 特别是在对 DAO 层进行单元测试时 - 对 DAO 进行注释也将确保对其进行事务功能测试。
【讨论】:
propagation=Propagation.REQUIRES_NEW,可能会发生嵌套事务。否则对于大多数情况,包括 propogation=mandatory,DAO 将只参与服务层启动的现有事务。
另外,Spring 建议只在具体类上使用注解,而不是在接口上使用。
http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
【讨论】:
通常应该在服务层放置一个事务。
但如前所述,操作的原子性告诉我们在哪里需要注释。因此,如果您使用像 Hibernate 这样的框架,其中对对象的单个“保存/更新/删除/...修改”操作有可能修改多个表中的几行(因为通过对象图的级联),当然这个特定的 DAO 方法也应该有事务管理。
【讨论】:
@Transactional注解应该放在所有不可分割的操作周围。
使用@Transactional 事务传播是自动处理的。在这种情况下,如果当前方法调用了另一个方法,那么该方法将可以选择加入正在进行的事务。
让我们举个例子:
我们有 2 个模型,即 Country 和 City。 Country 和 City 模型的关系映射就像一个 Country 可以有多个城市,所以映射就像,
@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;
Here Country 映射到多个城市并获取它们Lazily。因此,当我们从数据库中检索 Country 对象时,@Transactinal 的角色出现了,然后我们将获取 Country 对象的所有数据,但不会获取城市集,因为我们正在获取城市 LAZILY。
//Without @Transactional
public Country getCountry(){
Country country = countryRepository.getCountry();
//After getting Country Object connection between countryRepository and database is Closed
}
当我们想从国家对象访问城市集合时,我们将在该集合中获得空值,因为集合的对象仅创建了这个集合并没有用那里的数据初始化以获取我们使用的集合的值@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 是服务可以在单个事务中进行多次调用而无需关闭与端点的连接。
【讨论】:
@Transactional 的真正含义的解释
最好放在服务层!这在我昨天看到的一篇文章中有清楚的解释!这是the link,您可以查看!
【讨论】:
首先让我们定义我们必须在哪里使用事务?
我认为正确的答案是 - 当我们需要确保一系列动作将作为一个原子操作一起完成,或者即使其中一个动作失败也不会进行任何更改。
将业务逻辑放入服务中是众所周知的做法。所以服务方法可能包含不同的动作,这些动作必须作为一个单一的逻辑工作单元来执行。如果是这样 - 那么这种方法必须标记为 Transactional。当然,并不是每个方法都需要这样的限制,所以你不需要将整个服务标记为事务性。
还有更多 - 不要忘记考虑到 @Transactional 显然可能会降低方法性能。 为了了解全局,您必须了解事务隔离级别。了解这一点可能会帮助您避免在不必要的地方使用 @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);
}
春季文档:https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html
【讨论】:
服务层是添加 @Transactional 注释的最佳位置,因为这里存在大多数业务逻辑,它包含详细级别的用例行为。
假设我们将它添加到 DAO 并从服务中调用 2 个 DAO 类,一个失败,另一个成功,在这种情况下,如果 @Transactional 不在服务上,一个 DB 将提交,另一个将回滚。
因此我的建议是明智地使用此注释并仅在服务层使用。
【讨论】:
最好将 @Transactional 保留在 DAO 和服务层之间的单独中间层中。 由于回滚非常重要,您可以将所有数据库操作放在中间层,并将业务逻辑写入服务层。中间层将与您的 DAO 层交互。
这将在许多情况下为您提供帮助,例如 ObjectOptimisticLockingFailureException - 此异常仅在您的事务结束后发生。所以,你不能在中间层捕捉它,但你现在可以在你的服务层捕捉它。如果您在服务层中有@Transactional,这将是不可能的。虽然你可以在 Controller 中捕获,但 Controller 应该尽可能干净。
如果您在完成所有保存、删除和更新选项后在单独的线程中发送邮件或短信,您可以在中间层中的事务完成后在服务中执行此操作。同样,如果您在服务层中提到@Transactional,即使您的交易失败,您的邮件也会继续发送。
因此,拥有一个中间 @Transaction 层将有助于使您的代码更好且更易于处理。否则, 如果在 DAO 层使用,可能无法回滚所有操作。 如果你在服务层使用,在某些情况下你可能不得不使用AOP(Aspect Oriented Programming)。
【讨论】:
理想情况下,服务层(管理器)代表您的业务逻辑,因此应该使用@Transactional 进行注释。服务层可能会调用不同的 DAO 来执行数据库操作。让我们假设一个服务方法中有 N 个 DAO 操作的情况。如果您的第一个 DAO 操作失败,其他操作可能仍然通过,您最终会出现不一致的数据库状态。注释服务层可以帮助您避免这种情况。
【讨论】:
我更喜欢在方法级别的服务层上使用@Transactional。
【讨论】:
@Transactional用于服务层,通过控制器层(@Controller)和服务层调用DAO层(@Repository)调用,即数据库相关操作。
【讨论】: