这一节内容纯粹是为了写而写的,权当温习数据库知识和熟悉下怎么写博客了,谁让自己菜呢。:)
看了许多别的博客和资料,事务两个字都快不认识了,那么其实事务的概念很简单,可以理解为一件事情,在计算机里,它就是一个操作序列。
它相比于普通的事情不同的是,它必须服从ISO/IEC指定的ACID原则。
A(Aotomicity)原子性,简单的说就是要么全部一起做完,要么都不做,即事务的不可分割性,若非使用特殊手段,如catch某些子事务的exception,否则当其中一个子事务失败,其他事务都将一起回滚。
C(Consistency)一致性,即其保证是从一个正确的状态,转换成另一种正确的状态,即操作后数据不会被破坏,如转账时不会出现一方扣了钱而另一方却收不到的状况。
I(Isolation)隔离性,在事务提交之前,它可能的结果不应该显示给其他事务。举个例子,如果一个事务为一个银行需要存入100元后存入200元,那当然不可以在存入100元后,就实例化到数据库被别的事务读取了存入100元后的结果,如果在存入200元时发生回滚不就出大事了嘛。所谓的隔离,就是需要保证各个事务之间是没有相互干扰的。
D(Durability)持久性,其结果将正确的永久保存在数据库中,其他事务的失败不会对其造成影响。
以上四个特性,经常受到讨论和争辩的莫过于一致性和隔离性了。所谓鱼与熊掌不可兼得,当我们需要兼顾所有的特性,可想而知,当安全性越高,其操作的顾忌和检查越多,性能也就越差。所以对于一致性和隔离性,又有着不同的规范等级。(垃圾文字功底)
权衡之下可能出现的问题
1、脏读,即读到不干净的数据。
例子:(例子原址)
1)Mary的原工资为1000, 财务人员将Mary的工资改为了8000(但未提交事务)
2)Mary读取自己的工资 ,发现自己的工资变为了8000,欢天喜地!
3)而财务发现操作有误,回滚了事务,Mary的工资又变为了1000, 像这样,Mary记取的工资数8000是一个脏数据。
2、不可重复读,即在同一个事务中,重复读取却有不同的结果。
例子:(例子原址)
1)在事务A中,Mary 读取了自己的工资为1000,操作并没有完成
2)在事务B中,这时财务人员修改了Mary的工资为2000,并提交了事务.
3)在事务A中,Mary 再次读取自己的工资时,工资变为了2000
3、幻读,即在同一事务中,可读取到其他事务提交后的结果,造成疑惑。
例子:(例子原址)
1)A把所有的“黑色”改为“白色”
2)B把所有的“红色”改为“黑色”
3)A再查询黑色,却发现还有一批。
隔离性的四个级别
1、READ_UNCOMMITED(未授权读取),即可以读到未提交的数据。
附上一个别人写的生动的例子
公司发工资了,领导把5000元打到singo的账号上,但是该事务并未提交,而singo正好去查看账户,发现工资已经到账,是5000元整,非常高 兴。可是不幸的是,领导发现发给singo的工资金额不对,是2000元,于是迅速回滚了事务,修改金额后,将事务提交,最后singo实际的工资只有 2000元,singo空欢喜一场。(原址)
2、READ_COMMITED(授权读取),即其保证不会读到未提交的事务,但会造成不可重复读。
附上一个别人写的生动的例子
singo拿着工资卡去消费,系统读取到卡里确实有2000元,而此时她的老婆也正好在网上转账,把singo工资卡的2000元转到另一账户,并在 singo之前提交了事务,当singo扣款时,系统检查到singo的工资卡已经没有钱,扣款失败,singo十分纳闷,明明卡里有钱,为 何......
出现上述情况,即我们所说的不可重复读 ,两个并发的事务,“事务A:singo消费”、“事务B:singo的老婆网上转账”,事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。(原址)
3、REPEATABLE_READ(可重复读),即当读取时,不能进行更新。
附上一个别人写的生动的例子
当singo拿着工资卡去消费时,一旦系统开始读取工资卡信息(即事务开始),singo的老婆就不可能对该记录进行修改,也就是singo的老婆不能在此时转账。
虽然Repeatable read避免了不可重复读,但还有可能出现幻读。
singo的老婆工作在银行部门,她时常通过银行内部系统查看singo的信用卡消费记录。有一天,她正在查询到singo当月信用卡的总消费金额(select sum(amount) from transaction where month = 本月)为80元,而singo此时正好在外面胡吃海塞后在收银台买单,消费1000元,即新增了一条1000元的消费记录(insert transaction ... ),并提交了事务,随后singo的老婆将singo当月信用卡消费的明细打印到A4纸上,却发现消费总额为1080元,singo的老婆很诧异,以为出现了幻觉,幻读就这样产生了。
注:Mysql的默认隔离级别就是Repeatable read。(原址)
4、SERIALIZABLE(序列化),最高级别,在该级别下,事务顺序执行,那也就不会出现上面的问题了。
这四个规范,从上至下安全性越高,性能越差。
附一张哪都能看到的表
事务隔离级别 脏读 不可重复读 幻读
READ_UNCOMMITED Y Y Y
READ_COMMITED N Y Y
REPEATABLE_READ N N Y
SERIALIZABLE N N N
二、Spring事务用法
1、xml配置(搬运总结自开涛大神)
<!-- 事务管理的核心是事务管理器抽象,对于不同的数据访问框架(如Hibernate)通过实现策略接口PlatformTransactionManager,从而能支持各种数据访问框架的事务管理-->
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:advice transaction-manager="txManager" id="txAdvice">
<tx:attributes>
<!--表示将拦截以add开头的方法,被拦截的方法将应用配置的事务属性:propagation表示传播行为级别,默认是Required,isolation表示隔离级别
name:定义与事务属性相关联的方法名,将对匹配的方法应用定义的事务属性,可以使用“*”通配符来匹配一组或所有方法,如“save*”将匹配以save开头的方法,而“*”将匹配所有方法;
propagation:事务传播行为定义,默认为“REQUIRED”,表示Required,其值可以通过TransactionDefinition的静态传播行为变量的“PROPAGATION_”后边部分指定,如“TransactionDefinition.PROPAGATION_REQUIRED”可以使用“REQUIRED”指定;
isolation:事务隔离级别定义;默认为“DEFAULT”,其值可以通过TransactionDefinition的静态隔离级别变量的“ISOLATION_”后边部分指定,如“TransactionDefinition. ISOLATION_DEFAULT”可以使用“DEFAULT”指定:
timeout:事务超时时间设置,单位为秒,默认-1,表示事务超时将依赖于底层事务系统;
read-only:事务只读设置,默认为false,表示不是只读;
rollback-for:需要触发回滚的异常定义,以“,”分割,默认任何RuntimeException 将导致事务回滚,而任何Checked Exception 将不导致事务回滚;异常名字定义和TransactionProxyFactoryBean中含义一样
no-rollback-for:不被触发进行回滚的 Exception(s);以“,”分割;异常名字定义和TransactionProxyFactoryBean中含义一样; -->
<tx:method name="add*" />
< :attributes>
< :advice>
<aop:config>
<!--切入点定义,定义名为"myPointcut"的aspectj切入点,切入点表达式为"execution(* com..zhw.service..*.*(..))"表示拦截com包及子包下的zhw. service包及子包下的任何类的任何方法-->
<aop:pointcut id="myPointcut" expression="execution(* com..zhw.service..*.*(..))" />
<!--切入点为myPointcut,通知为txAdvice。-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
</aop:config>
个人觉得xml配置的方式有点过于粗暴,在有事务嵌套的情况下有点难以处理(对我这种比较懒的人)。
2、Annotation声明
<!--在xml配置文件中开启注解事务管理支持即可-->
<tx:annotation-driven/>
其具有三个属性
transaction-manager:指定事务管理器名字,默认为transactionManager
proxy-target-class:默认为false,false使用jdk动态代理,若为true则使用cglibs代理。设置为JDK代理和CGLIBS代理的主要区别是,使用JDK代理,@Transactional注释可以作用在接口和类及其方法上,而CGLIBS只能只用在类上,为了避免疏忽,建议就写在类上而不是接口上,而且,写在接口上事务是不支持继承的。
order:定义事务通知顺序,默认将顺序决定权交给AOP处理。
在配置文件中开启注解事务管理后我们就可以使用@Transcational注释用于事务管理了。
@Transcational
public class SuperServiceImpl implements SuperService{
public void add();
}
@Transactional
public class SubServiceImpl extends SuperServiceImpl implements SubService{
public void update();
}
以上是最常用的用法:
1、将@Transactional注释作用在类上,默认该类所有方法都加入事务
2、将@Transactional注释作用在父类上,其可以被子类集成。
@Transactional注释有许多自定义的属性使他的功能更加丰富。
value:指定事务管理器,用于支持多事务管理器环境
isolation:指定事务隔离级别,默认『DEFAULT』
readOnly:事务是否只读,默认false。(即该事务开始后,其他事务所提交的数据,对该事务来说不可见,常用于多条查询语句在同一事务的情况)
timeout:事务超时时间,单位:秒。默认-1,表示超时将依赖于底层事务系统。
rollbackFor:指定一组异常类,遇到该异常将进行回滚,如若没有指定,那么将只会对unchecked异常进行回滚,而checked异常将不会回滚。
rollbackForClassname:作用同上。
noRollbackFor:指定某异常类,遇到该异常不回滚事务。
noRollbackForClassname:作用同上。
注意事项:
1、@Transactional注释只能作用于public方法上,其他修饰将被忽略。
2、如果在接口,类,和方法上都指定了@Transactional注释,则优先级为方法->类->接口
3、建议只在类上加@Transactional注释,而不是接口上。
4、默认只会对unchecked异常进行回滚,最常见的为RuntimeException及其各种子类。
那么Spring事务基本的使用方法就酱了 : ),目测已经可以满足百分之八九十需求了。
继承类的@Transactional
@Transactional这个事务注解对父类继承过来的方法无效
只对当前类和子类有效,如果想要使其对父类方法有效,则可以将@Transactional写在父类上。
继承类的@Transactional:org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
假设有以下类:
@Transactional
public class SubClass extends SuperClass {
public void loadDb(){
//数据库操作
}
}
public class SuperClass {
public void savedb() {
//数据库操作
}
}
savedb是父类的方法,loadDb是子类的方法。如果有以下调用:
@Test
public void test(){
SubClass o = new SubClass();
o.savedb();//将会报没有Session的错误
o.loadDb();//正常
}
可以看到在调父类方法时Hibernate报没有Session的错误(org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here),子类没有问题。
解决办法一:在子类中重载父类方法:
@Transactional
public class SubClass extends SuperClass {
public void loadDb(){
}
@Override
public void savedb() {
super.savedb();
}
}
这样显然繁琐了,子类savedb没有任何新操作。
解决方法二:在父类中标注@Transactional(父类是抽象类也可以):
@Transactional
public class SuperClass {
public void savedb() {
}
}
这需要父类的修改权限。
--- http://www.cnblogs.com/xiefeifeihu/archive/2010/07/27/1785958.html