6

在 Grails 应用程序中,服务方法的默认行为是它们是事务性的,如果抛出未经检查的异常,事务会自动回滚。但是,在 Groovy 中,不会强制处理(或重新抛出)受检异常,因此存在如果服务方法抛出受检异常,事务将不会回滚的风险。鉴于此,注释每个 Grails 服务类似乎是可取的

@Transactional(rollbackFor = Throwable.class) 
class MyService {

    void writeSomething() {
    }
}

假设我有其他方法MyService,其中一个只读取数据库,另一个不接触数据库,以下注释是否正确?

@Transactional(readOnly = true)
void readSomething() {}

// Maybe this should be propagation = Propagation.NOT_SUPPORTED instead?
@Transactional(propagation = Propagation.SUPPORTS)
void dontReadOrWrite() {}

为了回答这个问题,我想你需要知道我的意图是什么:

  • 如果任何方法抛出异常并且正在进行事务,它将回滚。例如,如果writeSomething()调用dontReadOrWrite(),并且后者抛出异常,则前者启动的事务将被回滚。我假设rollbackFor类级属性由各个方法继承,除非它们显式覆盖它。
  • 如果没有正在进行的事务,则不会启动诸如dontReadOrWrite
  • 如果调用时没有事务在进行readSomething(),将启动一个只读事务。如果一个读写事务正在进行,它将参与这个事务。
4

2 回答 2

4

就目前而言,您的代码是正确的:您确实想在服务类中的各个方法上使用 Spring @Transactional 注释来获得您正在寻找的粒度,您是对的,您希望支持 dontReadOrWrite(NOT_SUPPORTED 将暂停现有交易,根据您所描述的内容不会为您购买任何东西,并且会要求您的软件花费周期,因此没有收获就会有痛苦),并且您想要默认的传播行为是对的(需要) 用于 readSomething。

但是要记住 Spring 事务行为的重要一点是,Spring 通过将您的类包装在一个代理中来实现事务管理,该代理执行适当的事务设置,调用您的方法,然后在控制返回时执行适当的事务拆除。而且(至关重要),只有在您调用代理上的方法时才会调用此事务管理代码,如果 writeSomething() 直接调用 dontReadOrWrite() 就不会发生这种情况,就像您的第一个项目符号一样。

如果您需要对另一个方法调用的方法进行不同的事务行为,那么如果您想继续使用 Spring 的 @Transactional 注释进行事务管理,那么您有两个选择:

  1. 将另一个正在调用的方法移动到另一个服务类中,该服务类将通过 Spring 代理从您的原始服务类访问。
  2. 将方法留在原处。在您的服务类中声明一个成员变量,使其与您的服务类的接口类型相同,并将其设为@Autowired,这将为您提供对您的服务类的 Spring 代理对象的引用。然后,当您想以不同的事务行为调用您的方法时,请在该成员变量上而不是直接执行此操作,Spring 事务代码将按照您的意愿触发。

如果这两种方法确实不相关,则方法 #1 非常好,因为它可以解决您的问题而不会混淆最终维护您的代码的人,并且没有办法意外忘记调用启用事务的方法。

方法#2 通常是更好的选择,假设您的方法出于某种原因都在同一个服务中,并且您真的不想将它们分开。但是对于不了解 Spring 事务的这种皱纹的维护者来说,这会让人感到困惑,并且您必须记住在您调用它的每个地方都以这种方式调用它,因此它是有代价的。我通常愿意为此付出代价,以免不自然地分裂我的服务课程,但与往常一样,这取决于您的情况。

于 2013-01-24T13:58:03.033 回答
1

我认为您正在寻找的是更精细的事务管理,而使用 @Transactional 注释是正确的方向。也就是说,有一个Grails 事务处理插件可以为您提供您正在寻找的行为。需要注意的是,您需要将服务方法调用包装在 DomainClass.withTransaction 闭包中,并将您正在寻找的非标准行为作为参数映射提供给 withTransaction() 方法。

请注意,在后端,这正是您在上面所说的,通过使用 @Transactional 注释来更改运行时事务的行为。插件文档非常好,所以我认为如果没有足够的指导,你不会发现自己。

希望这就是你要找的。

于 2012-02-20T16:49:16.037 回答