在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()时没有进行任何事务,则将启动只读事务.如果正在进行读写事务,它将参与此事务.
但是要记住Spring事务行为的一个重要事项是Spring通过将类包装在代理中来执行事务管理,该代理执行适当的事务设置,调用您的方法,然后在控制返回时执行适当的事务拆除.并且(至关重要),只有在代理上调用方法时才会调用此事务管理代码,如果writeSomething()直接调用第一个项目符号中的dontReadOrWrite(),则不会发生这种情况.
如果你需要在另一个方法调用的方法上有不同的事务行为,那么如果你想继续使用Spring的@Transactional注释进行事务管理,你有两个我知道的选择:
>将另一个调用的方法移动到另一个服务类中,该服务类将通过Spring代理从原始服务类访问.
>将方法保留原样.声明服务类中的成员变量与服务类的接口类型相同,并使其成为@Autowired,这将为您提供对服务类的Spring代理对象的引用.然后,当您想要使用不同的事务行为调用您的方法时,请在该成员变量上而不是直接执行它,并且Spring事务代码将根据您的需要触发.
方法#1非常好,如果这两种方法实际上并不相关,因为它可以解决您的问题而不会混淆最终维护代码的人,并且无法忘记调用启用事务的方法.
方法#2通常是更好的选择,假设您的方法由于某种原因而全部在同一服务中,并且您不想真正将它们拆分出来.但是对于一个不理解Spring事务的皱纹的维护者而言,这是令人困惑的,你必须记住在你调用它的每个地方以这种方式调用它,所以这是有代价的.我通常愿意支付这个价格,以免不自然地分裂我的服务类,但一如既往,这取决于你的情况.