简体   繁体   English

Grails服务交易行为

[英]Grails service transactional behaviour

In a Grails app, the default behaviour of service methods is that they are transactional and the transaction is automatically rolled-back if an unchecked exception is thrown. 在Grails应用程序中,服务方法的默认行为是它们是事务性的,如果抛出未经检查的异常,事务将自动回滚。 However, in Groovy one is not forced to handle (or rethrow) checked exceptions, so there's a risk that if a service method throws a checked exception, the transaction will not be rolled back. 但是,在Groovy中,不会强制处理(或重新抛出)已检查的异常,因此存在一种风险,即如果服务方法抛出已检查的异常,则不会回滚该事务。 On account of this, it seems advisable to annotate every Grails service class 考虑到这一点,似乎建议注释每个Grails服务类

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

    void writeSomething() {
    }
}

Assume I have other methods in MyService , one of which only reads the DB, and the other doesn't touch the DB, are the following annotations correct? 假设我在MyService有其他方法,其中一个只读取数据库,另一个不接触数据库,以下注释是否正确?

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

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

In order to answer this question, I guess you'll need to know what my intention is: 为了回答这个问题,我想你需要知道我的意图是什么:

  • If an exception is thrown from any method and there's a transaction in progress, it will be rolled back. 如果从任何方法抛出异常并且正在进行事务,则将回滚它。 For example, if writeSomething() calls dontReadOrWrite() , and an exception is thrown from the latter, the transaction started by the former will be rolled back. 例如,如果writeSomething()调用dontReadOrWrite() ,并且从后者抛出异常,则前者启动的事务将被回滚。 I'm assuming that the rollbackFor class-level attribute is inherited by individual methods unless they explicitly override it. 我假设rollbackFor类级属性是由单个方法继承的,除非它们显式覆盖它。
  • If there's no transaction in progress, one will not be started for methods like dontReadOrWrite 如果没有正在进行的事务,则不会为dontReadOrWrite方法启动dontReadOrWrite
  • If no transaction is in progress when readSomething() is called, a read-only transaction will be started. 如果在调用readSomething()时没有进行任何事务,则将启动只读事务。 If a read-write transaction is in progress, it will participate in this transaction. 如果正在进行读写事务,它将参与此事务。

Your code is right as far as it goes: you do want to use the Spring @Transactional annotation on individual methods in your service class to get the granularity you're looking for, you're right that you want SUPPORTS for dontReadOrWrite (NOT_SUPPORTED will suspend an existing transaction, which won't buy you anything based on what you've described and will require your software to spend cycles, so there's pain for no gain), and you're right that you want the default propagation behavior (REQUIRED) for readSomething. 你的代码是正确的:你确实希望在服务类中的各个方法上使用Spring @Transactional注释来获得你正在寻找的粒度,你想要支持dontReadOrWrite(NOT_SUPPORTED将是正确的)暂停一个现有的交易,根据你所描述的内容不会给你买任何东西,并且需要你的软件花费周期,所以没有收获就很痛苦,而且你想要默认的传播行为是正确的(必需的) )for readSomething。

But an important thing to keep in mind with Spring transactional behavior is that Spring implements transaction management by wrapping your class in a proxy that does the appropriate transaction setup, invokes your method, and then does the appropriate transaction tear-down when control returns. 但是要记住Spring事务行为的一个重要事项是Spring通过将类包装在代理中来执行事务管理,该代理执行适当的事务设置,调用您的方法,然后在控制返回时执行适当的事务拆除。 And (crucially), this transaction-management code is only invoked when you call the method on the proxy, which doesn't happen if writeSomething() directly calls dontReadOrWrite() as in your first bullet. 并且(至关重要), 只有在代理上调用方法时才会调用此事务管理代码,如果writeSomething()在第一个项目符号中直接调用dontReadOrWrite(),则不会发生这种情况。

If you need different transactional behavior on a method that's called by another method, you've got two choices that I know of if you want to keep using Spring's @Transactional annotations for transaction management: 如果你需要在另一个方法调用的方法上有不同的事务行为,那么如果你想继续使用Spring的@Transactional注释进行事务管理,你有两个我知道的选择:

  1. Move the method being called by the other into a different service class, which will be accessed from your original service class via the Spring proxy. 将另一个调用的方法移动到另一个服务类中,该服务类将通过Spring代理从原始服务类访问。
  2. Leave the method where it is. 将方法保留在原处。 Declare a member variable in your service class to be of the same type as your service class's interface and make it @Autowired, which will give you a reference to your service class's Spring proxy object. 声明服务类中的成员变量与服务类的接口类型相同,并使其成为@Autowired,这将为您提供对服务类的Spring代理对象的引用。 Then when you want to invoke your method with the different transactional behavior, do it on that member variable rather than directly, and the Spring transaction code will fire as you want it to. 然后,当您想要使用不同的事务行为调用您的方法时,请在该成员变量上而不是直接执行它,并且Spring事务代码将根据您的需要触发。

Approach #1 is great if the two methods really aren't related anyway, because it solves your problem without confusing whoever ends up maintaining your code, and there's no way to accidentally forget to invoke the transaction-enabled method. 方法#1非常好,如果这两种方法实际上并不相关,因为它可以解决您的问题,而不会混淆最终维护代码的人,并且无法忘记调用启用事务的方法。

Approach #2 is usually the better option, assuming that your methods are all in the same service for a reason and that you wouldn't really want to split them out. 方法#2通常是更好的选择,假设您的方法由于某种原因而全部在同一服务中,并且您不希望将它们拆分出来。 But it's confusing to a maintainer who doesn't understand this wrinkle of Spring transactions, and you have to remember to invoke it that way in each place you call it, so there's a price to it. 但是对于一个不理解Spring事务的皱纹的维护者而言,这是令人困惑的,你必须记住在你调用它的每个地方以这种方式调用它,所以这是有代价的。 I'm usually willing to pay that price to not splinter my service classes unnaturally, but as always, it'll depend on your situation. 我通常愿意支付这个价格,以免不自然地分裂我的服务类,但一如既往,这取决于你的情况。

I think that what you're looking for is more granular transaction management, and using the @Transactional annotation is the right direction for that. 我认为您正在寻找的是更细粒度的事务管理,使用@Transactional注释是正确的方向。 That said, there is a Grails Transaction Handling Plugin that can give you the behavior that you're looking for. 也就是说,有一个Grails事务处理插件可以为您提供您正在寻找的行为。 The caveat is that you will need to wrap your service method calls in a DomainClass.withTransaction closure and supply the non-standard behavior that you're looking for as a parameter map to the withTransaction() method. 需要注意的是,您需要将您的服务方法调用包装在DomainClass.withTransaction闭包中,并提供您正在寻找的非标准行为作为withTransaction()方法的参数映射。

As a note, on the backend this is doing exactly what you're talking about above by using the @Transactional annotation to change the behavior of the transaction at runtime. 作为一个注释,在后端,通过使用@Transactional注释在运行时更改事务的行为,这正是您正在讨论的内容。 The plugin documentation is excellent, so I don't think you'll find yourself without sufficient guidance. 插件文档非常好,所以我认为如果没有足够的指导,你会发现自己。

Hope this is what you're looking for. 希望这是你正在寻找的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM