Grails 服务不是事务性的?

Grails services are not transactional?

根据官方documentation,和我看过的书,服务是默认跨国的。但是,即使我们立即抛出 RuntimeException,我们也会提交记录。

例如:

class MyService {
    def someMethod() {
        new someDomainObject().save(failOnError:true)
        throw new RuntimeException("rollback!")
    }
}

并这样称呼它:

class myController{
   MyService myService
    def someMethod() {
         myService.someMethod()
    }
}

在上述情况下,在调用调用服务的控制器后,检查该行是否是通过使用 mysql workbench 附加到数据库创建的,该行确实已提交但未提交回滚。

所以我们接下来尝试了这个:

class MyService {
    static transactional = true
    def someMethod() {
        new someDomainObject().save(failOnError:true)
        throw new RuntimeException("rollback!")
    }
}

同样的问题。

接下来我们尝试这个:

@Transactional
class MyService {
    static transactional = true
    def someMethod() {
        new SomeDomainObject().save(failOnError:true)
        throw new RuntimeException("rollback!")
    }
}

终于成功了。但是,我们不明白为什么。

注意: 使用 MYSQL:

的 Grails 2.4.4
development {
    dataSource {
        dbCreate = "create-drop"
        url = "jdbc:mysql://127.0.0.1:3306/db"
        username = "user"
        password = "***"
    }
}

这是正常行为吗?
@Transactional 与 static transactional=true 不同吗?

服务 类 由 intellij 14 使用 Grails 视图中服务文件夹中的 "new groovy class" 选项生成。 "new Grails Service" 选项对我们不起作用,它什么都不做,所以我们必须在正确的位置创建所有 groovy 类 "by hand"。

这没有多大意义。该服务的所有不同变体都应该具有相同的功能。使用的一般逻辑是在 class 级别或至少一种方法上查找 @Transactional。如果您使用 org.springframework.transaction.annotation.Transactional 则将创建一个事务代理。如果你使用较新的 grails.transaction.Transactional 那么 AST 将重写方法以使用事务模板,但最终效果基本相同。如果没有注释,那么除非您有 static transactional = false,否则该服务是事务性的并且会创建一个 Spring 代理(就像您包含 Spring @Transactional class 级别的注释)。 static transactional = true 永远不需要,因为它是默认值;服务完全非事务性的唯一方法是包含 static transactional = false 并且没有 @Transactional 注释。

可能发生的一件事是底层 table 可能不是事务性的。 MySQL 的较新版本默认为 InnoDB 作为 table 类型,但在 5.5 之前默认为 MyISAM。 Grails 会自动检测数据库并为您注册一个 Hibernate 方言,这在大多数情况下都能正常工作,但 MySQL + MyISAM 除外。为确保您始终使用 InnoDB,请在 DataSource.groovy 中指定适当的方言,例如

dataSource {
   dialect = org.hibernate.dialect.MySQL5InnoDBDialect
}

这只会对以后由 Hibernate 创建的新 table 有所帮助。请务必将任何现有的 MyISAM table 转换为 InnoDB(尽管在这种情况下不需要,因为您使用的是 create-drop)。

好的,找到原因了,还是明白了:

"Annotating a service method with Transactional disables the default Grails transactional behavior for that service"

所以我碰巧将服务中的众多方法之一注释为@Transactional(propagation=Propagation.REQUIRES_NEW),认为其他方法将保留其默认的事务性,但是没有,如果您进行任何声明,它会删除事务性行为所有其他方法都默默地,即使你说“static transactional = true

这个好像比较危险,以后我会把每个服务class都注释成@Transactional,以免被抓到