@Transactional注释属于哪里?

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

270106 次浏览

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

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

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

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

如果传播模式被设置为默认,那么对两个层都进行注释就没有意义了。

事务注释应该放在所有不可分割的操作周围。

例如,你的呼叫是“change password”。它由两个操作组成

  1. 修改密码。
  2. 审计变更。
  3. 通过电子邮件通知客户端密码已更改。

那么在上面,如果审计失败,那么修改密码也应该失败吗?如果是这样,那么事务应该在1和2左右(因此在服务层)。如果电子邮件失败了(可能应该有某种故障安全措施,这样它就不会失败),那么它是否应该回滚更改密码和审计?

当你决定把@Transactional放在哪里时,这些都是你需要问的问题。

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

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

最好把它放在服务层!这在我昨天看到的一篇文章中有清楚的解释!这里是的链接,你可以查看!

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

春季的新趋势是领域驱动设计 (DDD)。Spring Roo很好地体现了这一趋势。其思想是使域对象pojo比典型Spring体系结构上的pojo更更丰富的(通常是贫血),特别是将事务和持久性语义放在域对象本身上。在只需要简单的CRUD操作的情况下,web控制器直接在域对象pojo上操作(它们在此上下文中作为实体运行),并且没有服务层。在域对象之间需要某种协调的情况下,您可以使用服务bean处理它,按照传统使用@Transaction。您可以将域对象上的事务传播设置为REQUIRED之类的内容,以便域对象使用任何现有的事务,例如在服务bean上启动的事务。

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

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

但是正如前面所述,操作的原子性告诉我们哪里需要注释。因此,如果你使用像Hibernate这样的框架,其中一个“save/update/delete/…”一个对象上的“modify”操作有可能修改几个表中的几行(因为通过对象图的级联),当然在这个特定的DAO方法上也应该有事务管理。

或者对两个“层”都加注释有意义吗?——同时注释服务层和dao层不是很有意义吗——如果你想确保dao方法总是从服务层调用(传播),并且在dao中传播是“强制的”。这将为DAO方法从UI层(或控制器)调用提供一些限制。此外——特别是在对DAO层进行单元测试时——对DAO进行注释还将确保对其事务功能进行测试。

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

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

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

用于数据库级别的事务

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

  1. DAO获取数据的方法(select ..)-不需要 @Transactional这可能会导致一些开销,因为 事务拦截器/和需要执行的AOP代理 李。< / p > < / >

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

非常好的博客transctional

应用级别-
我正在使用事务性业务逻辑,我希望能够在意外错误的情况下回滚

@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){


try {
//service logic here
} catch(Throwable e) {


log.error(e)
throw new MyApplicationException(..);
}
}

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

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

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

看到# EYZ0。

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

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

让我们举个例子:

我们有2个模型,即CountryCityCountryCity模型的关系映射就像一个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 of Cities时,我们将在这个Set中获得空值,因为只有这个Set创建的对象没有初始化该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是服务可以在单个事务中进行多个调用,而不需要与端点关闭连接。

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

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

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

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

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

更重要的是,不要忘记考虑@ transactional显然会降低方法性能。 为了了解全局,您必须了解事务隔离级别。知道这一点可能会帮助你避免在不必要的地方使用@ transactional

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

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

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

Github project- java-algos

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);
}

春季医生:https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html

最好将@ transactional放在DAO和服务层之间的单独中间层中。 由于回滚非常重要,您可以将所有的DB操作放在中间层,并在服务层中编写业务逻辑。中间层将与DAO层交互。< / p >

这将帮助您在许多情况下,如ObjectOptimisticLockingFailureException -此异常只发生在您的事务结束后。所以,你不能在中间层捕获它,但你现在可以在服务层捕获它。如果在服务层中有@Transactional,这是不可能的。虽然你可以在控制器中捕获,但控制器应该尽可能干净。

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

所以有一个中间的@Transaction层将有助于使你的代码更好,更容易处理。否则, 如果在DAO层使用,可能无法回滚所有操作。 如果你在服务层使用,在某些情况下你可能不得不使用AOP(面向方面编程)。< / p >