@Transactional注释在哪里?

时间:2009-07-03 12:20:12

标签: java spring annotations transactions dao

您是否应将@Transactional放在DAO类和/或其方法中,还是更好地注释使用DAO对象调用的Service类?或者注释两个“层​​”是否有意义?

19 个答案:

答案 0 :(得分:492)

我认为交易属于服务层。这是了解工作单位和用例的人。如果你有几个DAO注入服务需要在一个事务中一起工作,那么这是正确的答案。

答案 1 :(得分:287)

总的来说,我同意其他人说明交易通常是在服务级别上启动的(取决于您当然需要的粒度)。

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

我还有一个集成测试,在这里我检查所有bean(bean post processor)以获取此注释,如果在不属于服务层的bean中存在除了Mandatory之外的传播的@Transactional注释,则会失败。这样我就确保我们不会在错误的层上启动事务。

答案 2 :(得分:85)

交易注释应放在所有不可分割的操作周围。

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

  1. 更改密码。
  2. 审核变更。
  3. 通过电子邮件向客户端发送密码已更改。
  4. 所以在上面,如果审核失败,那么密码更改是否也会失败?如果是这样,那么交易应该在1和2附近(所以在服务层)。如果电子邮件失败(可能应该具有某种故障安全性,因此它不会失败)那么它应该回滚更改密码和审核吗?

    在决定放置@Transactional的位置时,您需要提出以下问题。

答案 3 :(得分:39)

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

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

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

答案 4 :(得分:38)

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

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

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

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

答案 5 :(得分:29)

我将@Transactional放在@Service图层上,并设置rollbackFor任何例外情况和readOnly以进一步优化交易。

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

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

请参阅Checked vs. Unchecked Exceptions

答案 6 :(得分:17)

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

答案 7 :(得分:16)

另外,Spring建议只在具体类而不是接口上使用注释。

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

答案 8 :(得分:13)

对于数据库级别的事务

我在DAO中仅使用@Transactional方法级别,因此配置可以专门用于方法/使用默认值(必需)

  1. DAO获取数据的方法(选择..) - 不需要 @Transactional因为这会导致一些开销 需要执行的事务拦截器/和AOP代理 好。

  2. DAO的插入/更新方法将获得@Transactional

  3. 非常好的博客transctional

    申请级别 -
    我正在使用事务性的业务逻辑我希望能够在出现意外错误时回滚

    @Transactional(rollbackFor={MyApplicationException.class})
    public void myMethod(){
    
        try {    
            //service logic here     
        } catch(Throwable e) {
    
            log.error(e)
            throw new MyApplicationException(..);
        }
    }
    

答案 9 :(得分:12)

通常,应该在服务层放置一个事务。

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

答案 10 :(得分:9)

@Transactional应该围绕所有不可分割的操作进行注释。 使用@Transactional事务传播是自动处理的。在这种情况下,如果当前方法调用了另一个方法,那么该方法可以选择加入正在进行的事务。

让我们举个例子:

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

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

此处国家/地区映射到多个城市,并提取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是Service可以在单个事务中进行多次调用而不关闭与端点的连接。

答案 11 :(得分:7)

最好将它放在服务层中!昨天我遇到的一篇文章清楚地解释了这一点!您可以查看以下the link

答案 12 :(得分:4)

首先让我们定义我们必须使用交易的位置?

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

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

甚至更多 - 不要忘记考虑到 @Transactional 显然会降低方法性能。 为了查看整个图片,您必须知道事务隔离级别。知道这可能会帮助您避免使用 @Transactional 而不一定需要它。

答案 13 :(得分:4)

服务层是添加@Transactional注释的最佳位置,因为它包含详细级别的用例行为。

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

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

Github project- java-algos

答案 14 :(得分:2)

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

答案 15 :(得分:1)

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

答案 16 :(得分:1)

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

答案 17 :(得分:1)

enter image description here

@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

答案 18 :(得分:0)

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

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

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

因此,在中间的@Transaction层将有助于使您的代码更好且易于处理。除此以外,    如果在DAO层中使用,则可能无法回滚所有操作。    如果在服务层中使用,则在某些情况下可能必须使用 AOP (面向方面​​的编程)。