跟着刚哥学习Spring框架--事务配置(七)

时间:2022-03-12 19:54:58

事务

事务用来保证数据的完整性和一致性。

事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。
1、原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
2、一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
3、隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作对并发执行的各个事务之间不能互相干扰。
4、持久性(durability)。持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。

Spring事务管理

Spring事务管理即支持编程式事务管理,也支持声明式事务。
1、编程式事务:将事务管理代码嵌入到业务方法中来控制事务的提交和回滚。
2、声明式事务:将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。Spring通过AOP框架支持声明式事务。

Spring事务示例(XML)

先定义一个applicationContext.xml

 <context:component-scan base-package="com.hzg.spring.jdbc"/>  

 <!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<!-- Connection Info -->
<property name="driverClass" value="${db.driverClass}" />
<property name="jdbcUrl" value="${db.url}" />
<property name="user" value="${db.username}" />
<property name="password" value="${db.password}" /> <!-- Connection Pooling Info -->
<property name="initialPoolSize" value="1" />
<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="15" />
<property name="maxIdleTime" value="1800" />
<property name="maxStatements" value="0" />
</bean> <bean id="jdbcTemplateDao" class="com.hzg.spring.jdbc.dao.JdbcTemplateDao" >
<property name="dataSource" ref="dataSource" />
</bean> <!-- JDBC事务管理器 -->
<bean id="jdbctTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean> <tx:advice id="txAdvice" transaction-manager="jdbctTxManager">
<tx:attributes>
<tx:method name="domain*"/>
      
</tx:attributes>
</tx:advice> <aop:config proxy-target-class="true">
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.chou.spring.jdbc.service.JdbcTemplateService.*(..))"/>
</aop:config>

DAO文件:

 public class JdbcTemplateDao extends JdbcDaoSupport{  

     public void save() {
String insertSql = "insert into tab_item values(?,?,?)";
Assert.isTrue(getJdbcTemplate().update(insertSql, new Object[]{6, "HP", "PT540"}) == 1, "插入失败");
} public void delete() {
String deleteSql = "delete tab_item where id = ?";
Assert.isTrue(getJdbcTemplate().update(deleteSql, new Object[]{6}) == 1, "删除失败");
} public void update() {
String updateSql = "update tab_item set itemno = ?, itemname = ? where id = ?";
Assert.isTrue(getJdbcTemplate().update(updateSql, new Object[]{"HP", "PT555", 6}) == 1, "修改失败");
}
}

service文件:

 @Service
public class JdbcTemplateService { @Autowired
private JdbcTemplateDao jdbcTemplateDao; public void domain(){
jdbcTemplateDao.save();
int i = 2/0;//这里出错了,事务就会回滚,之前的save就无效了
jdbcTemplateDao.update();
jdbcTemplateDao.delete();
}
}

Main方法

 //main方法
String[] configLocations = new String[] {"applicationContext.xml"};
ApplicationContext ctx = new ClassPathXmlApplicationContext(configLocations);
JdbcTemplateService j = ctx.getBean(JdbcTemplateService.class);
j.domain();

<tx:advice/>配置详解

 <tx:advice id="……" transaction-manager="……">
<tx:attributes>
<tx:method name="*"
propagation="REQUIRED"
isolation="DEFAULT"
timeout="-1"
read-only="true"
no-rollback-for=""
rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice> <!-- 最常用的配置 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="create*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="merge*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="put*" propagation="REQUIRED" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
<tx:method name="count*" propagation="SUPPORTS" read-only="true" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="list*" propagation="SUPPORTS" read-only="true" />
<tx:method name="*" propagation="SUPPORTS" read-only="true" />
<tx:method name="batchSaveOrUpdate" propagation="REQUIRES_NEW" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* cn.javass..service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
</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}

 <!-- 事务管理器配置 -->
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean> <!-- 使用annotation定义事务 -->
<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来处理。

 @Transactional//放在这里表示所有方法都加入事务管理
public class AnnotationUserServiceImpl implements IUserService {
private IUserDao userDao;
private IAddressService addressService; @Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED)
public void save(UserModel user) {
userDao.save(user);
user.getAddress().setUserId(user.getId());
addressService.save(user.getAddress());
} @Transactional(propagation=Propagation.REQUIRED, readOnly=true,
isolation=Isolation.READ_COMMITTED)
public int countAll() {
return userDao.countAll();
}
//setter...
}

-----------------------------------------------------------------------------------------------------------------------

跟着刚哥学习Spring框架--创建HelloWorld项目(一)

跟着刚哥学习Spring框架--Spring容器(二)

跟着刚哥学习Spring框架--通过XML方式配置Bean(三)

跟着刚哥学习Spring框架--通过注解方式配置Bean(四)

跟着刚哥学习Spring框架--AOP(五)

跟着刚哥学习Spring框架--JDBC(六)

跟着刚哥学习Spring框架--事务配置(七)