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
,以免被抓到
根据官方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.4development {
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
,以免被抓到