事务经常遇到的问题:
1.脏读 Dirty reads :事务A未提交之前(还在缓存中)修改了记录1,事务B读取记录1,如果事务A回滚,则事务B所读是错误数据。
2.不可重复读 non-repeatable reads:事务A中两处读取记录1中的值。第一次读取记录1值为“123”,事务B把记录1值修改为“456”,事务A第二次读结果变为“456”,数据混乱
3.幻想读 phantom reads:事务A中两处搜索字段a=1的所有记录。第一次搜索记录数1条,事务B增加了一条a=1的记录,事务A第二次搜索记录数2条,插入造成数据混乱
解决方式
设置事务的隔离级别isolation
ISOLATION_READ_UNCOMMITTED :最低隔离级别,允许一个事务看到另一个事务未提交的数据
ISOLATION_READ_COMMITTED:一个事务不能看到另一个事务未提交的数据,只能解决脏读,不能解决 不可重复都和幻想读
ISOLATION_REPEATABLE_READ:可防止脏读和不可重复读,但是不能解决 幻想读
ISOLATION_SERIALIZABLE:事务处理为顺序执行。代价最大,最安全。可解决脏读,不可重复都,幻想读
x锁 排他锁 被加锁的对象只能被只有锁的事务读取和修改,其他事务无法在该对象上加其他锁,也不能读取和修改对象
s锁 共享锁 被加锁的对象可以被持有锁事务读取,但是不能被修改,其他事务也可以在上面加共享锁不能加排他锁
共享锁下其他用户可以并发读取,查询数据。但不能修改增加,删除数据。
并发控制下乐观锁和悲观锁
悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作
应用:实用数据库锁机制(排他锁)
乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违法数据完整性
应用:1.使用自增长的整数表示数据版本号。每次提交+1,匹配相等可更新
2.实用时间戳实现
spring配置事务实例:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<!-- 开启注解配置 -->
<context:annotation-config />
<!-- 开启事务注解驱动 -->
<tx:annotation-driven />
<bean id="mybatisDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${db_driver}" />
<property name="jdbcUrl" value="${db_url}" />
<property name="user" value="${db_username}" />
<property name="password" value="${db_password}" />
<!-- 最大空闲时间,超过空闲时间的连接将被丢弃。为0或负数则永不丢弃。默认为0 -->
<property name="maxIdleTime" value="60" />
<!-- 连接池中保留的最小连接数。 -->
<property name="minPoolSize" value="1" />
<!-- 连接池中保留的最大连接数。默认为15 -->
<property name="maxPoolSize" value="5" />
<!-- 隔多少秒检查所有连接池中的空闲连接,默认为0表示不检查 -->
<property name="idleConnectionTestPeriod" value="60" />
<!-- 初始化时创建的连接数,应在minPoolSize与maxPoolSize之间取值。默认为3 -->
<property name="initialPoolSize" value="10" />
<!-- 当连接池中的连接用完时,C3P0一次性创建新连接的数目 -->
<property name="acquireIncrement" value="5" />
<!-- 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
<property name="acquireRetryAttempts" value="30" />
<property name="acquireRetryDelay" value="100" />
<!-- 获取连接失败将会引起所有等待获取连接的线程抛出异常。 但是数据源仍有效保留,并在下次调 用getConnection()的时候继续尝试获取连接。
如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。默认为 false -->
<property name="breakAfterAcquireFailure" value="true" />
</bean>
<!-- myBatis文件 -->
<bean id="mybatisSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml" />
<property name="dataSource" ref="mybatisDataSource" />
<!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
<property name="mapperLocations" value="classpath*:com/trustsaving/**/mapper/impl/*.xml" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.trustsaving.*.mapper" />
<property name="sqlSessionFactoryBeanName" value="mybatisSessionFactory" />
</bean>
<!-- 配置事务管理器 -->
<bean id="mybatisTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="mybatisDataSource" />
<property name="globalRollbackOnParticipationFailure" value="false" />
</bean>
<!-- 配置事务的传播特性 -->
<tx:advice id="mybatisAdvice" transaction-manager="mybatisTransactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" isolation="REPEATABLE_READ" rollback-for="Exception,RuntimeException" />
<tx:method name="create*" propagation="REQUIRED" isolation="REPEATABLE_READ" rollback-for="Exception,RuntimeException" />
<tx:method name="save*" propagation="REQUIRED" isolation="REPEATABLE_READ" rollback-for="Exception,RuntimeException" />
<tx:method name="edit*" propagation="REQUIRED" isolation="REPEATABLE_READ" rollback-for="Exception,RuntimeException" />
<tx:method name="update*" propagation="REQUIRED" isolation="REPEATABLE_READ" rollback-for="Exception,RuntimeException" />
<tx:method name="modify*" propagation="REQUIRED" isolation="REPEATABLE_READ" rollback-for="Exception,RuntimeException" />
<tx:method name="get*" propagation="REQUIRED" isolation="REPEATABLE_READ" rollback-for="Exception,RuntimeException" />
<tx:method name="find*" propagation="SUPPORTS" isolation="REPEATABLE_READ" rollback-for="Exception,RuntimeException" />
<tx:method name="query*" propagation="SUPPORTS" isolation="REPEATABLE_READ" rollback-for="Exception,RuntimeException" />
<tx:method name="join*" propagation="SUPPORTS" isolation="REPEATABLE_READ" rollback-for="Exception,RuntimeException" />
<tx:method name="buy*" propagation="REQUIRED" isolation="REPEATABLE_READ" rollback-for="Exception,RuntimeException" />
<tx:method name="end*" propagation="REQUIRED" isolation="REPEATABLE_READ" rollback-for="Exception,RuntimeException" />
<tx:method name="run*" propagation="REQUIRED" isolation="REPEATABLE_READ" rollback-for="Exception,RuntimeException" />
<tx:method name="clearing*" propagation="REQUIRED" isolation="REPEATABLE_READ" rollback-for="Exception,RuntimeException" />
<tx:method name="liquidation*" propagation="REQUIRES_NEW" isolation="REPEATABLE_READ" rollback-for="Exception,RuntimeException" />
<tx:method name="*" read-only="true" />
</tx:attributes>
</tx:advice>
<!--哪些类的哪些方法参与事务 -->
<aop:config>
<aop:pointcut id="mybatisServiceOperation" expression="execution(* com.trustsaving.MatchMoudle.service..*ServiceImpl.*(..))" />
<aop:advisor advice-ref="mybatisAdvice" pointcut-ref="mybatisServiceOperation" />
</aop:config>
</beans>
事务传播性propagation
PROPAGATION_REQUIRED:支持当前事务,如果当前没有事务则新建一个事务。这是最常见选择
PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务则按照无事务方式执行
PROPAGATION_MANDATORY:支持当前事务,如果没有事务则抛出异常
PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,则当前事务挂起
PROPAGATION_NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,则把当前事务挂起
PROPAGATION_NEVER:已非事务方式执行,如果当前存在事务,则抛出异常
PROPAGATION_NESTED:
理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,
而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。
而Nested事务的好处是他有一个savepoint。
*****************************************
ServiceA {
/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
void methodA() {
try {
//savepoint
ServiceB.methodB(); //PROPAGATION_NESTED 级别
} catch (SomeException) {
// 执行其他业务, 如 ServiceC.methodC();
}
}
}
********************************************
也就是说ServiceB.methodB失败回滚,那么ServiceA.methodA也会回滚到savepoint点上,ServiceA.methodA可以选择另外一个分支,比如
ServiceC.methodC,继续执行,来尝试完成自己的事务。
但是这个事务并没有在EJB标准中定义
引用自:http://blog.csdn.net/it_man/article/details/5074371