spring事务管理
本节要点:
- 了解事务的概念
- 了解jdbc事务管理
- 掌握spring事务管理的实现方式
- 编程式事务管理
- 声明式事务管理
- 了解事务的隔离级别和传播方式
事务的定义:
数据库系统为了保证数据操作的完整性和一致性,引入了事务这个重要的概念,所谓事务,就是将一系列的数据库操作作为一个整体来执行。如对数据库存取,就是一组SQL指令,这一组SQL指令必须全部执行成功;如果因为某个原因(例如其中一行SQL有错误),则先前所执行过的SQL指令撤销。
Jdbc事务管理
在JDBC中,可以用Connection的setAutoCommit()方法,给定它false参数。在一连串的SQL语句后面,调用Connection的commit()来送出变更。如果中间发生错误,则调用rollback()来撤销所有的执行,例如:
try{
connection.setAutoCommit(false);
…//一连串SQL操作
connection.commit(); //执行成功,提交所有变更
}catch(SQLException e){
connection.rollback(); //发生错误,撤销所有变更
}
案例:操作用户信息
在数据库新建一个表t_user 并包含id和name两个属性。
User
public class User { private int id; private String name; get/set…… }
UserDao
public interface UserDao { public String getUser(int id); public void updUser(int id); public void addUser(User user); public void delUser(int id); }
UserDaoImpl
/** * JDBC Connection类的事务控制方法: setAutoCommit(boolean autoCommit) 设置是否自动提交事务,默认自动 commit() 提交事务 rollback() 撤销事务 * @author Administrator * */ public class UserDaoImpl implements UserDao { public DataSource dataSource;// 数据源 private Connection conn;// 数据库连接 /** * 设置数据源并根据数据源获取数据库的连接 */ public void setDataSource (DataSource dataSource){ this.dataSource = dataSource; try{ this.conn = dataSource.getConnection(); }catch(SQLException e){ e.printStackTrace(); } } @Override public String getUser(int id) { String name = null; try{ PreparedStatement ps = conn.prepareStatement("select name from " + "t_user where id=?"); ps.setInt(1, id); ResultSet rs = ps.executeQuery(); if(rs.next()){ name = rs.getString(1); } }catch(SQLException e){ e.printStackTrace(); } return name; } @Override public void updUser(int id) { try{ PreparedStatement ps = conn.prepareStatement("update t_user set name=? where id=? "); ps.setString(1, "25342"); ps.setInt(2, id); ps.executeUpdate(); }catch(SQLException e){ e.printStackTrace(); } } @Override public void addUser(User user) { try{ PreparedStatement ps = conn.prepareStatement("insert into t_user(id,name) values(" +user.getId()+","+user.getName()+")"); ps.executeUpdate(); }catch(SQLException e){ e.printStackTrace(); } } @Override public void delUser(int id) { try{ PreparedStatement ps = conn.prepareStatement("delete from t_user where id=? "); ps.setInt(1, id); ps.executeUpdate(); }catch(SQLException e){ e.printStackTrace(); } } }
UserService
public interface UserService { public String getUser(int id); public void updUser(int id); public void addUser(User user); public void delUser(int id); }
UserServiceImpl
public class UserServiceImpl implements UserService{ private UserDao userDao; public UserDao getUserDao() { return userDao; } public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public String getUser(int id) { return userDao.getUser(id); } @Override public void updUser(int id) { userDao.updUser(id); } @Override public void addUser(User user) { userDao.addUser(user); } @Override public void delUser(int id) { userDao.delUser(id); } }
Bean.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--数据源配置 DriverManagerDataSource是spring提供的一个简单的数据源实现类 这个类实现了javax.sql.DataSource接口,但是它没有提供池化连接的机制, 每次调用getConnection()获取新连接时,只是简单地创建一个新的连接。 因此,这个数据源简单应用或测试,因为它不需要额外的依赖类 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property> <property name="url" value="jdbc:oracle:thin:@localhost:1521:XE"></property> <property name="username" value="zhou"></property> <property name="password" value="123456"></property> </bean> <bean id="userService" class="com.silvan.service.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <bean id="userDao" class="com.silvan.dao.UserDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
Test
public class Test { public static void main(String[] args) throws Exception{ ApplicationContext applicationContext=new ClassPathXmlApplicationContext("beans.xml"); UserService userService=(UserService)applicationContext.getBean("userService"); User user = new User(); // user.setId(1); // user.setName("1231"); // userService.addUser(user); userService.delUser(1); } }
UserDaoImpl中的新增方法修改成如下,体验事务的功能:如果事务过程中出现异常,捕获异常后对数据进行回滚,可以保证数据的完整性。
public void addUser(User user) { try{ conn.setAutoCommit(false); PreparedStatement ps = conn.prepareStatement("insert into t_user(id,name) values(" +user.getId()+","+user.getName()+")"); ps.executeUpdate(); Integer.parseInt("asfdas"); conn.commit(); }catch(Exception e){ e.printStackTrace(); try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } }
Spring事务概述
Spring框架提供了极其强大而简便的事务处理功能,其核心便是PlatformTransactionManager抽象接口。Spring将所有的事务管理都抽象为PlatformTransactionManager、TransactionStatus和TransactionDefinition这3个接口,而无论其底层关联的具体的事务究竟是JDBC事务、JTA事务,还是ORM框架自定义的事务。
- l PlatformTransactionManager:定义了事务管理器,所有与事务相关的操作都有它管理;
- l TransactionStatus:代表事务本身,它提供了简单的控制事务执行和查询事务状态的方法;
- l TransactionDefinition :定义了事务的规则(事务的隔离级别、传播行为、事务超时、只读状态), 在启动事务时,事务管理器会根据TransactionDefinition来启动合适的事务;
在Spring中实现事务管理有两种方式,一种是传统的编程式事务管理,也就是程序员在编写程序代码实现事务的管理,具体包括定义事务的开始、在程序异常时进行事务的回滚及程序正常执行后的事务提交。
另一种则是基于AOP技术实现的声明式事务管理,事务管理本身是一项共有的系统级服务功能,完全可以将事务管理抽象成一个事务切面,程序员不再关心事务管理的问题,把主要精力放在核心业务逻辑代码的编写上,然后在需要进行事务管理的方法上切入事务切面,使之具有事务管理的功能,达到事务管理的目的。
Spring编程式事务
PlatformTransactionManager:
- 事务管理器接口, 只定义了3个方法:getTransaction()获取事务的状态; commit();rollback();
- 事务管理器的实现类有多种,根据具体的持久层框架的不同而不同;
- 实现类: DataSourceTransactionManager,HibernateTransactionManager,JdoTransactionManager等
- 可以使用PlatformTransactionManager直接管理事务。简单地通过一个bean引用给你的bean传递一个你使用的 PlatformTransaction对象。然后,使用TransactionDefinition和TransactionStatus对象就可以发起、回滚、提交事务。
- 流程一般如下:
- 1 .声明数据源
- 2 .声明一个事务管理类,例如 DataSourceTransactionManager, HibernateTransactionManger, JTATransactionManager等
- 3 .在我们的代码中加入事务处理代码
userDaoImpl
package com.silvan.dao; import java.util.List; import java.util.Map; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; import com.silvan.pojo.User; /** *spring编程式事务 * @author Administrator */ public class UserDaoImpl implements UserDao { public DataSourceTransactionManager transactionManager;// 事务管理器的实现类,作用如创建事务,管理事务等 public JdbcTemplate jdbcTemplate;//spring中要使用Jdbctemplate对象来完成jdbc 操作 public void setTransactionManager( DataSourceTransactionManager transactionManager) { this.transactionManager = transactionManager; } public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public String getUser(int id) { String name = null; try{ List<Map<String, Object>> result = jdbcTemplate.queryForList("select name from t_user where id=?", id); if(result.iterator() != null){ name = (String) result.get(0).get("name"); } }catch(Exception e){ e.printStackTrace(); } return name; } @Override public void updUser(int id) { try{ jdbcTemplate.update("update t_user set name=? where id=? ","2544",id); }catch(Exception e){ e.printStackTrace(); } } @Override public void addUser(User user) { TransactionStatus ts=null; try{ //定义事务规则 TransactionDefinition td=new DefaultTransactionDefinition(); //根据事务规则,创建一个新的事务或者获取之前已经创建的事务 ts=transactionManager.getTransaction(td); jdbcTemplate.update("insert into t_user(id,name) values(?,?)",user.getId(),user.getName()); // Integer.parseInt("asfdas"); transactionManager.commit(ts); }catch(Exception e){ e.printStackTrace(); try { transactionManager.rollback(ts); }catch (Exception e1) { e1.printStackTrace(); } } } @Override public void delUser(int id) { try{ jdbcTemplate.update("delete from t_user where id=?",id); }catch(Exception e){ e.printStackTrace(); } } }
Beans
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 创建数据源对象 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property> <property name="url" value="jdbc:oracle:thin:@localhost:1521:zhouyq"></property> <property name="username" value="zhou"></property> <property name="password" value="123456"></property> </bean> <!-- 创建事务管理对象,并注入数据源 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name="dataSource" ref="dataSource"></property> </bean> <!-- 创建jdbc操作对象 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="userService" class="com.silvan.service.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <!-- 注入jdbc事务操作对象和事务管理对象 --> <bean id="userDao" class="com.silvan.dao.UserDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> <property name="transactionManager" ref="transactionManager"></property> </bean> </beans>
Spring声明式事务
Spring为声明式事务提供了简单而强大的支持,所谓声明式事务,是指在Spring的配置文件中使用相应的标签对事务进行配置,这样做的好处是Spring可以帮助我们管理事务,例如:什么时候提交事务、什么时候回滚事务等。
从开发效率与易维护的角度来看,Spring声明式事务管理是实际开发中比较常用的。
基于 <tx> 命名空间的声明式事务管理:
① 在xml中启用tx和aop两个命名空间
xmlns:tx=http://www.springframework.org/schema/tx
xsi:schemaLocation="http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"
② 在xml中配置通知、切入点以及Advisor
修改一下文件:
UserDaoImpl
package com.silvan.dao; import java.util.List; import java.util.Map; import org.springframework.jdbc.core.JdbcTemplate; import com.silvan.pojo.User; /** *spring声明式事务 * @author Administrator * */ public class UserDaoImpl implements UserDao { public JdbcTemplate jdbcTemplate;//spring中要使用Jdbctemplate对象来完成jdbc 操作 public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public String getUser(int id) { String name = null; try{ List<Map<String, Object>> result = jdbcTemplate.queryForList("select name from t_user where id=?", id); if(result.iterator() != null){ name = (String) result.get(0).get("name"); } }catch(Exception e){ e.printStackTrace(); } return name; } @Override public void updUser(int id) { try{ jdbcTemplate.update("update t_user set name=? where id=? ","2544",id); }catch(Exception e){ e.printStackTrace(); } } @Override public void addUser(User user) { try{ jdbcTemplate.update("insert into t_user(id,name) values(?,?)",user.getId(),user.getName()); Integer.parseInt("asfdas"); }catch(Exception e){ e.printStackTrace(); throw new RuntimeException(); } } @Override public void delUser(int id) { try{ jdbcTemplate.update("delete from t_user where id=?",id); }catch(Exception e){ e.printStackTrace(); } } }
Bean.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 创建数据源对象 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property> <property name="url" value="jdbc:oracle:thin:@localhost:1521:zhouyq"></property> <property name="username" value="zhou"></property> <property name="password" value="123456"></property> </bean> <!-- 配置JdbcTemplate,如果不用spring的jdbc可以省略 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 创建事务管理对象 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name="dataSource" ref="dataSource"></property> </bean> <tx:advice id="testAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 配置切入点和advisor --> <aop:config> <aop:pointcut expression="execution(* com.silvan.service.*.*(..))" id="pointcut"/> <aop:advisor advice-ref="testAdvice" pointcut-ref="pointcut"/> </aop:config> <bean id="userService" class="com.silvan.service.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <bean id="userDao" class="com.silvan.dao.UserDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> </beans>
事务回滚:
默认spring事务只在发生未被捕获的 runtimeexcetpion时才回滚。
spring aop 异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeexception的异常,但可以通过配置来捕获特定的异常并回滚,换句话说在service的方法中不使用try catch 或者在catch中最后加上throw new runtimeexcetpion(),这样程序异常时才能被aop捕获进而回滚
事务的传播方式:
事务的传播行为用于指定在多个事务方法间调用时,事务是如何在这些方法间传播的,spring共支持7种传播行为。
- PROPAGATION_REQUIRED
表示业务逻辑方法需要在一个事务中运行,如果该方法在运行时,已经处在一个事务中,则直接加入到该事务中,否则自己创建一个新的事务。即:如果存在一个事务,则支持当前事务。如果没有事务则开启。(在实际开发中常用该传播方式。)
- PROPAGATION_SUPPORTS
表示业务逻辑方法如果在某个事务范围内被调用,则该方法直接成为当前事务的一部分。如果该方法在事务范围外被调用,则该方法在无事务的环境下执行。即:如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。
- PROPAGATION_MANDATORY
表示业务逻辑方法只能在一个已经存在的事务中执行,该方法不能创建自己的事务,如果该方法在没有事务的环境下被调用,容器就会抛出事务不存在的异常。 即:如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
- PROPAGATION_REQUIRES_NEW
表示不管当前是否有事务存在,该业务逻辑方法都会为自己创建一个全新的事务。如果该方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到该方法执行结束后新事务才算结束,原先的事务再恢复执行。即: 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起,新事务运行完毕后,再接着运行被挂起的事务。
- PROPAGATION_NOT_SUPPORTED
表示业务逻辑方法不需要事务。如果该方法目前没有关联到某个事务,容器不会为它创建事务。如果该方法在一个事务中被调用,则该事务会被挂起,在方法调用结束后原先的事务才会恢复执行。即:总是非事务地执行,并挂起任何存在的事务,当前方法运行完毕后,被挂起的事务才恢复执行。
- PROPAGATION_NEVER
表示业务逻辑方法绝对不能在事务范围内执行。如果该方法在某个事务中执行,容器会抛出异常,只有没有关联到任何事务时该方法才能正常执行。即:总是非事务地执行,如果存在一个活动事务,则抛出异常
- PROPAGATION_NESTED
表示如果一个活动的事务存在,业务逻辑方法则运行在一个嵌套的事务中,如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点,内部事务的回滚不会对外部事务造成影响,它只对DataSourceTransactionManager事务管理器生效。即:如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。
事务传播特性:
对基于方法级别的事务管理而言,方法开始执行时创建事务,方法运行过程中若出现异常则进行事务回滚,方法如果正常执行完成则进行事务的提交。因而事务管理的主要任务就是事务的创建、事务的回滚与事务的提交,其中是否需要创建事务及如何创建事务时由事务传播行为控制的,通常数据的读取时不需要事务管理的,或者也可为其指定只读事务,而对于插入、修改与删除数据的方法来说,就有必要进行事务管理了,在未指定事务传播行为时,Spring2.x将启用默认的REQUIRED。
Spring中事务隔离级别
- ISOLATION_DEFAULT
这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.
以下四个与JDBC的隔离级别相对应
- ISOLATION_READ_UNCOMMITTED
这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读、不可重复读和幻像读。
- ISOLATION_READ_COMMITTED
保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。
- ISOLATION_REPEATABLE_READ
这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生,即:不可重复读。
- ISOLATION_SERIALIZABLE
这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读、不可重复读外,还避免了幻像读。但是并发性最差。