ChatGPT解决这个技术问题 Extra ChatGPT

@Transactional 注解属于哪里?

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


L
Lii

我认为事务属于服务层。它了解工作单元和用例。如果您将多个 DAO 注入需要在单个事务中协同工作的服务中,那么这是正确的答案。


我同意这一点。有时这无关紧要,但有时您可以从中受益,例如 Hibernate 会话跨越 while 事务,因此所有加载的对象都在 1 级缓存中,您不需要再次将对象重新附加到会话,加上延迟加载属性功能没有模糊。
全局事务是否可以包含多个 @Transactional(propagation = Propagation.REQUIRED) ?或者@Transactional 始终是事务的边界?我不确定我是从文档中得到的,但似乎你可以创建由 @Transactional 方法和在其中运行的所有内容组成的事务
是的,我认为如果 Propagation.REQUIRED 被打开,最外面的@Transactional 将成为事务的边界。
当 @Transactional 是单个工作单元时,我们不需要明确提及它。
m
mnp

一般来说,我同意其他人的观点,即事务通常在服务级别启动(当然取决于您需要的粒度)。

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

我还有一个集成测试,我检查所有 bean(bean 后处理器)是否有此注释,如果在不属于服务层的 bean 中存在具有传播以外的传播的 @Transactional 注释,则失败。这样我确保我们不会在错误的层上启动事务。


这是否应该在 dao(接口)层,在 dao impl 中完成。层或两者?
我不知道它是否适合这个讨论,但另一个技巧可以是在非写入操作的 dao impl 中添加 @Transactional(readOnly = true) 。
@Johan Spring 建议将 Transaction 注释放在实现类而不是接口上。
我真的很喜欢这个想法,也在我的项目中尝试过
让我看看我是否理解......你是说我应该把 @Transactional 放在服务实现类上,我应该把 @Transactional(propagation = MANDATORY) 放在 DAO(存储库)类实现上?
A
Abdul Rahman

Transactional Annotations 应该放在所有不可分割的操作周围。

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

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

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

在决定将 @Transactional 放在何处时,您需要提出这些问题。


n
naXa stands with Ukraine

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

Spring 的一个新兴趋势是向 domain-driven design (DDD) 发展。 Spring Roo 很好地体现了这一趋势。这个想法是使域对象 POJO 比典型的 Spring 架构(通常是 anemic)要多很多 richer,特别是将事务和持久性语义放在域对象本身上。在只需要简单的 CRUD 操作的情况下,Web 控制器直接在域对象 POJO 上操作(它们在此上下文中作为实体运行),并且没有服务层。如果域对象之间需要某种协调,您可以让服务 bean 处理它,按照传统使用 @Transaction。您可以将域对象上的事务传播设置为类似 REQUIRED 的内容,以便域对象使用任何现有事务,例如在服务 bean 上启动的事务。

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


h
hammarback

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

在服务层上进行注释将导致比在 DAO 级别上进行注释更长的事务。取决于可以解决问题的事务隔离级别,因为并发事务不会看到彼此的更改,例如。可重复阅读。

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

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


n
naXa stands with Ukraine

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

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

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

请参阅Checked vs. Unchecked Exceptions


m
martoncsukas

对于数据库级别的事务

大多数情况下,我在 DAO 中仅在方法级别使用 @Transactional,因此配置可以专门针对方法/使用默认值(必需)

DAO 获取数据的方法(select ..) - 不需要 @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(..);
    }
}

Java 中 +1 关于 Transactional 的非常好的文章
n
naXa stands with Ukraine

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


这样做不会导致嵌套事务吗?以及随之而来的所有微妙问题?
不,JPA 中没有嵌套事务。将它们放在两者中会非常好 - 如果您在点击 DAO 时已经在进行交易,那么该交易将继续进行。
如果您使用 propagation=Propagation.REQUIRES_NEW,可能会发生嵌套事务。否则对于大多数情况,包括propogation=mandatory,DAO 将只参与服务层启动的现有事务。
d
davidemm

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

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


H
Harshal Patil

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

所以让我们举个例子:

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

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

此处 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 真正含义的解释
y
yannick555

通常,应该将事务放在服务层。

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


P
Prags

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

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

因此,我的建议是明智地使用此注释并仅在服务层使用。

Github project- java-algos


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

最好放在服务层!这在我昨天看到的一篇文章中有清楚的解释!这是您可以查看的the link


A
Amiko

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

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

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

甚至更多 - 不要忘记考虑到 @Transactional 显然可能会降低方法性能。为了了解全局,您必须了解事务隔离级别。知道这可能会帮助您避免在不一定需要的地方使用@Transactional。


A
Alin

https://i.stack.imgur.com/WlLfh.png

@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


N
Nabin Kumar Khatiwada

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

这将在许多情况下为您提供帮助,例如 ObjectOptimisticLockingFailureException - 此异常仅在您的事务结束后发生。所以,你不能在中间层捕捉它,但你现在可以在你的服务层捕捉它。如果您在服务层中有@Transactional,这将是不可能的。尽管您可以在 Controller 中捕获,但 Controller 应该尽可能干净。

如果您在完成所有保存、删除和更新选项后在单独的线程中发送邮件或短信,您可以在中间层完成事务后在服务中执行此操作。同样,如果您在服务层中提及@Transactional,即使您的交易失败,您的邮件也会继续发送。

因此,拥有一个中间 @Transaction 层将有助于使您的代码更好且易于处理。否则,如果在 DAO 层使用,可能无法回滚所有操作。如果你在服务层使用,在某些情况下你可能不得不使用 AOP(Aspect Oriented Programming)。


H
Harshal Patil

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


H
Harshal Patil

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


H
Harshal Patil

@Transactional用于服务层,通过控制器层(@Controller)和服务层调用DAO层(@Repository)调用,即数据库相关操作。