关于java:@Transactional注释属于哪里?

Where does the @Transactional annotation belong?

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


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


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

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

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


Transactional Annotations应围绕所有不可分割的操作。

例如,您的电话是"更改密码"。这包括两个操作

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

    这些是在决定放置@Transactional的位置时需要询问的问题。


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

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

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


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

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

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

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


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

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

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

    请参阅已检查与未检查的例外。


    或者注释两个"层??"是否有意义? - 注意服务层和dao层都没有意义 - 如果想要确保始终从DAO中具有传播"强制"的服务层调用(传播)DAO方法。这将为从UI层(或控制器)调用DAO方法提供一些限制。此外 - 当特别是对DAO层进行单元测试时 - 对DAO进行注释也将确保对其进行交易功能测试。


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

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


    对于数据库级别的事务

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

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

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

  • 关于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方法进行事务管理。


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

    让我们举个例子:

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

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

    此处国家/地区映射到多个城市并提取它们Lazily。因此,当我们从数据库中检索Country对象时,会出现@Transactinal的角色,然后我们将获取Country对象的所有数据但不会获得城市集,因为我们正在获取城市Lazily

    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我们使用@Transactional的值,即,

    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显然,可能会降低方法性能。
    为了查看整个图片,您必须知道事务隔离级别。知道这可能会帮助您避免在不一定需要的地方使用@Transactional。


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

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

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

    Github项目 - java-algos


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


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


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


    enter image description here

    @Transactional应该在服务层上使用,因为它包含业务逻辑。 DAO层通常只有数据库CRUD操作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 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


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

    这将在许多情况下帮助您,如ObjectOptimisticLockingFailureException - 只有在您的Transaction结束后才会发生此异常。所以,你不能在中间层抓住它,但你现在可以抓住你的服务层。如果您在服务层中有@Transactional,则无法进行此操作。虽然你可以捕获控制器,但控制器应尽可能干净。

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

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