spring事务管理

本节要点:java

  •    了解事务的概念
  •   了解jdbc事务管理
  •   掌握spring事务管理的实现方式
    • 编程式事务管理
    • 声明式事务管理
  •  了解事务的隔离级别和传播方式

事务的定义:程序员

数据库系统为了保证数据操做的完整性和一致性,引入了事务这个重要的概念,所谓事务,就是将一系列的数据库操做做为一个总体来执行。如对数据库存取,就是一组SQL指令,这一组SQL指令必须所有执行成功;若是由于某个缘由(例如其中一行SQL有错误),则先前所执行过的SQL指令撤销。spring

 

Jdbc事务管理sql

在JDBC中,能够用Connection的setAutoCommit()方法,给定它false参数。在一连串的SQL语句后面,调用Connection的commit()来送出变动。若是中间发生错误,则调用rollback()来撤销全部的执行,例如:数据库

try{express

    connection.setAutoCommit(false);编程

    …//一连串SQL操做并发

    connection.commit();    //执行成功,提交全部变动oracle

}catch(SQLException e){app

    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框架自定义的事务。

  • PlatformTransactionManager:定义了事务管理器,全部与事务相关的操做都有它管理;
  • TransactionStatus:表明事务自己,它提供了简单的控制事务执行和查询事务状态的方法;
  • 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

    这是花费最高代价可是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读、不可重复读外,还避免了幻像读。可是并发性最差。

相关文章
相关标签/搜索