grails 事务 Transactional

grails 2.3之前
 
For Example
 
AccountController.groovy

 
 
class AccountController {

	def transfer = {

		def fromAccount = params?.fromAccount
		def toAccount = params?.toAccount
		def amountToTransfer = params?.amount

		// some validity checks, could be performed through a Command Object class
		...

		// withdraw from the source account
		fromAccount.balance -= amountToTransfer
		fromAccount.save(flush:true)

		// some logic that raises an error or throws an exception
		...

		// transfer to the target account
		toAccount.balance += amountToTransfer
		toAccount.save(flush:true)
	}
}

这是一个关于账户的controller,它定义了一个transfer的闭包
然后定义了转出账户,转入账户和转账金额
然后对转出账户的余额进行了减少并立即保存数据库
然后对转入账户的余额进行了增加并立即保存数据库
 
但是,当程序运行到到‘some logic’模块时发生了错误或者抛出了异常,转出账户的钱转出了但转入账户却没有转入
 
导致出现这个情况的主要原因是:没有使用事务,数据库没有回滚
 
Grails Model – Business Logic in Services
 
Grails paradigm is to avoid putting business code in the Controller layer but rather in the model or service layer.
(Grails范例是避免把业务代码写入controller层而宁可说写在model层或者service层)

TransferService.groovy

class TransferService {

	def transfer(fromAccount, toAccount, amount) {

		// withdraw from the source account
		fromAccount.balance -= amountToTransfer
		fromAccount.save(flush:true)

		// some logic that raises an error or throws an exception
		...

		// transfer to the target account
		toAccount.balance += amountToTransfer
		toAccount.save(flush:true)
	}
}

AccountController.groovy

class AccountController {

	def transferService

	def transfer = {

		...

		// call service to make the transfer
		transferService.transfer(fromAccount, toAccount, amountToTransfer)
	}
}
代码基本没变,只是原先写在controller中,现在写在了service中,然后在controller中进行了调用

这时,当你先做了转出操作,然后发生了错误或者异常,第一个账户的钱则不会转出,达到了想要的目的。

这是为什么呢?原因就是:by default every call to a Service’s method is made transactional. 
		   (缺省状况下每个service方法的调用都是事务的。)

但这是怎么办到的呢?
Using a combination of Spring IoC for injecting an instance of a service in the controller and using the Proxy pattern to make sure the call is enrolled in a transaction. 

当然,缺省状况下service都是事务的,那么,要想修改为非事务的话,如下:

 
 
class TransferService {
	static transactional = false

	...
}
注意:use runtime exceptions if you want to roll back a transaction
    (要想回滚必须是runtime异常)
     如果不是runtime异常,则可以自己catch然后抛出一个runtime异常。


Other Example

 
 
class AccountController {

	def transferService
	def auditService

	def transfer = {

		def fromAccount = params?.fromAccount
		def toAccount = params?.toAccount
		def amountToTransfer = params?.amount

		// some validity checks, could be performed through a Command Object class
		// ...

		// call service to make the transfer
		transferService.transfer(fromAccount, toAccount, amountToTransfer)

		// notify the auditing plateform of the transfer
		auditService.notifyTransfer(fromAccount, toAccount, amountToTransfer)
	}
}
这里调用了两个service中的两个方法,当notifyTransfer发生错误或异常时,转账却被保存了,为什么?

这里开启了两个事务,transfer方法运行时,开启事务A,transfer方法结束时,关闭事务A,notifyTransfer方法运行时开启事务B,发生异常时,数据回滚,结束事务B。因为两个当开启事务B时,事务A已经结束了,所以即使B的数据回滚了,也不会使A的数据回滚,controller是非事务的。
解决办法:尽量在controller中引入一个service,把对多个service的调用写在另一个service中,因为service是事务的。


grails 2.3之后

grails2.3之后,使用了全局事务


可以通过注解的方式来控制是否为事务,缺省为readOnly = false,或者直接写成@Transactional

如果想把action定义为非事务的话,可以用@NotTransactional


 
 
事务回滚的原理


未使用事务:
 
当save的时候,数据保存到hibernate session的一级缓存中,只有当flush:true时,才把整个session提交到数据库进行保存,如果在flush:true之前错误或者异常,所有数据都不会被保存;如果在flush:true之后异常,数据已经提交到数据库,无法回滚。

使用事务之后:
当save的时候,数据保存到hibernate session的一级缓存中,用了事务,当flush:true时,把整个session提交到数据库的session的二级缓存,无论在flush:true之前还是之后发生错误或者异常,所有数据都不会被保存,数据都会回滚。
  

 
 
 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值