事务
事务用来保证数据的完整性和一致性。
事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。
1、原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
2、一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
3、隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作对并发执行的各个事务之间不能互相干扰。
4、持久性(durability)。持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
Spring事务管理
Spring事务管理即支持编程式事务管理,也支持声明式事务。
1、编程式事务:将事务管理代码嵌入到业务方法中来控制事务的提交和回滚。
2、声明式事务:将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。Spring通过AOP框架支持声明式事务。
Spring事务示例(XML)
先定义一个applicationContext.xml
1 <context:component-scan base-package="com.hzg.spring.jdbc"/> 2 3 <!-- 配置数据源 --> 4 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> 5 <!-- Connection Info --> 6 <property name="driverClass" value="${db.driverClass}" /> 7 <property name="jdbcUrl" value="${db.url}" /> 8 <property name="user" value="${db.username}" /> 9 <property name="password" value="${db.password}" /> 10 11 <!-- Connection Pooling Info --> 12 <property name="initialPoolSize" value="1" /> 13 <property name="minPoolSize" value="1" /> 14 <property name="maxPoolSize" value="15" /> 15 <property name="maxIdleTime" value="1800" /> 16 <property name="maxStatements" value="0" /> 17 </bean> 18 19 <bean id="jdbcTemplateDao" class="com.hzg.spring.jdbc.dao.JdbcTemplateDao" > 20 <property name="dataSource" ref="dataSource" /> 21 </bean> 22 23 <!-- JDBC事务管理器 --> 24 <bean id="jdbctTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 25 <property name="dataSource" ref="dataSource" /> 26 </bean> 27 28 <tx:advice id="txAdvice" transaction-manager="jdbctTxManager"> 29 <tx:attributes> 30 <tx:method name="domain*"/>
31 </tx:attributes> 32 </tx:advice> 33 34 <aop:config proxy-target-class="true"> 35 <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.chou.spring.jdbc.service.JdbcTemplateService.*(..))"/> 36 </aop:config>
DAO文件:
1 public class JdbcTemplateDao extends JdbcDaoSupport{ 2 3 public void save() { 4 String insertSql = "insert into tab_item values(?,?,?)"; 5 Assert.isTrue(getJdbcTemplate().update(insertSql, new Object[]{6, "HP", "PT540"}) == 1, "插入失败"); 6 } 7 8 public void delete() { 9 String deleteSql = "delete tab_item where id = ?"; 10 Assert.isTrue(getJdbcTemplate().update(deleteSql, new Object[]{6}) == 1, "删除失败"); 11 } 12 13 public void update() { 14 String updateSql = "update tab_item set itemno = ?, itemname = ? where id = ?"; 15 Assert.isTrue(getJdbcTemplate().update(updateSql, new Object[]{"HP", "PT555", 6}) == 1, "修改失败"); 16 } 17 }
service文件:
1 @Service 2 public class JdbcTemplateService { 3 4 @Autowired 5 private JdbcTemplateDao jdbcTemplateDao; 6 7 public void domain(){ 8 jdbcTemplateDao.save(); 9 int i = 2/0;//这里出错了,事务就会回滚,之前的save就无效了 10 jdbcTemplateDao.update(); 11 jdbcTemplateDao.delete(); 12 } 13 }
Main方法
1 //main方法 2 String[] configLocations = new String[] {"applicationContext.xml"}; 3 ApplicationContext ctx = new ClassPathXmlApplicationContext(configLocations); 4 JdbcTemplateService j = ctx.getBean(JdbcTemplateService.class); 5 j.domain();
<tx:advice/>配置详解
1 <tx:advice id="……" transaction-manager="……"> 2 <tx:attributes> 3 <tx:method name="*" 4 propagation="REQUIRED" 5 isolation="DEFAULT" 6 timeout="-1" 7 read-only="true" 8 no-rollback-for="" 9 rollback-for="java.lang.Exception"/> 10 </tx:attributes> 11 </tx:advice> 12 13 <!-- 最常用的配置 --> 14 <tx:advice id="txAdvice" transaction-manager="txManager"> 15 <tx:attributes> 16 <tx:method name="save*" propagation="REQUIRED" /> 17 <tx:method name="add*" propagation="REQUIRED" /> 18 <tx:method name="create*" propagation="REQUIRED" /> 19 <tx:method name="insert*" propagation="REQUIRED" /> 20 <tx:method name="update*" propagation="REQUIRED" /> 21 <tx:method name="merge*" propagation="REQUIRED" /> 22 <tx:method name="del*" propagation="REQUIRED" /> 23 <tx:method name="remove*" propagation="REQUIRED" /> 24 <tx:method name="put*" propagation="REQUIRED" /> 25 <tx:method name="get*" propagation="SUPPORTS" read-only="true" /> 26 <tx:method name="count*" propagation="SUPPORTS" read-only="true" /> 27 <tx:method name="find*" propagation="SUPPORTS" read-only="true" /> 28 <tx:method name="list*" propagation="SUPPORTS" read-only="true" /> 29 <tx:method name="*" propagation="SUPPORTS" read-only="true" /> 30 <tx:method name="batchSaveOrUpdate" propagation="REQUIRES_NEW" /> 31 </tx:attributes> 32 </tx:advice> 33 <aop:config> 34 <aop:pointcut id="txPointcut" expression="execution(* cn.javass..service.*.*(..))" /> 35 <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" /> 36 </aop:config>
XML形式的事务配置<tx:method >的属性详解
属性
|
类型
|
默认值
|
说明
|
propagation | Propagation枚举 | REQUIRED | 事务传播属性 |
isolation | isolation枚举 | DEFAULT(所用数据库默认级别) | 事务隔离级别 |
readOnly | boolean | false | 是否才用优化的只读事务 |
timeout | int | -1 | 超时(秒) |
rollbackFor | Class[] | {} | 需要回滚的异常类 |
rollbackForClassName | String[] | {} | 需要回滚的异常类名 |
noRollbackFor | Class[] | {} | 不需要回滚的异常类 |
noRollbackForClassName | String[] | {} | 不需要回滚的异常类名 |
readOnly
事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务。如果值为true就会告诉Spring我这个方法里面没有insert或者update,你只需要提供只读的数据库Connection就行了,这种执行效率会比read-write的Connection高,所以这是一个最优化提示。在一些情况下,一些事务策略能够起到显著的最优化效果,例如在使用Object/Relational映射工具(如:Hibernate或TopLink)时避免dirty checking(试图“刷新”)。
timeout
在属性中还有定义“timeout”值的选项,指定事务超时为几秒。一般不会使用这个属性。在JTA中,这将被简单地传递到J2EE服务器的事务协调程序,并据此得到相应的解释。
Isolation Level(事务隔离等级)的5个枚举值
1、DEFAULT:采用数据库默认隔离级别
2、SERIALIZABLE:最严格的级别,事务串行执行,资源消耗最大;
3、REPEATABLE_READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。
4、READ_COMMITTED:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。
5、READ_UNCOMMITTED:保证了读取过程中不会读取到非法数据。隔离级别在于处理多事务的并发问题。
propagation属性的7个传播行为
1、REQUIRED:指定当前方法必需在事务环境中运行,如果当前有事务环境就加入此事务环境,如果没有就新建一个事务。(默认值)
2、SUPPORTS:指定当前方法加入当前事务环境,如果当前没有事务,就以非事务方式执行。
3、MANDATORY:指定当前方法必须加入当前事务环境,如果当前没有事务,就抛出异常。
4、REQUIRES_NEW:指定当前方法总是发起一个新事务,存在别的事务就被挂起,直到我的事务方法commit结束,原先事务才恢复。
5、NOT_SUPPORTED:指定当前方法以非事务方式执行,如果当前存在事务就挂起,等我以非事务的状态运行完,再继续原来事务。
6、NEVER:指定当前方法绝对不能在事务范围内执行,如果方法在事务范围内执行,容器就抛异常,只有没关联到事务,才正常执行。
7、NESTED:指定当前方法执行时,如果已经有一个事务存在,则运行在这个嵌套的事务中.如果当前环境没有运行的事务,就新建一个事务,并与父事务相互独立,这个事务拥有多个可以回滚的保证点。就是指我自己内部事务回滚不会对外部事务造成影响,只对DataSourceTransactionManager事务管理器起效。
Spring事务示例(注解形式@Transactional)
注意@Transactional只能被应用到public方法上,对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能。
默认情况下,一个有事务的方法,遇到RuntiomeException时会回滚。遇到受检查的异常是不会回滚的,要想所有异常都回滚,要加上属性rollbackFor={Exception.class}
1 <!-- 事务管理器配置 --> 2 <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 3 <property name="sessionFactory" ref="sessionFactory" /> 4 </bean> 5 6 <!-- 使用annotation定义事务 --> 7 <tx:annotation-driven transaction-manager="txManager" proxy-target-class="true" />
transaction-manager:指定事务管理器名字,默认为transactionManager,当使用其他名字时需要明确指定;
proxy-target-class:默认false表示使用JDK代理,如果为true将使用CGLIB代理
order:定义事务通知顺序,默认Ordered.LOWEST_PRECEDENCE,表示将顺序决定权交给AOP来处理。
1 @Transactional//放在这里表示所有方法都加入事务管理 2 public class AnnotationUserServiceImpl implements IUserService { 3 private IUserDao userDao; 4 private IAddressService addressService; 5 6 @Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED) 7 public void save(UserModel user) { 8 userDao.save(user); 9 user.getAddress().setUserId(user.getId()); 10 addressService.save(user.getAddress()); 11 } 12 13 @Transactional(propagation=Propagation.REQUIRED, readOnly=true, 14 isolation=Isolation.READ_COMMITTED) 15 public int countAll() { 16 return userDao.countAll(); 17 } 18 //setter... 19 }
-----------------------------------------------------------------------------------------------------------------------