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

    • 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

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

  • 相关阅读:
    扩展运算符(Spread operator)
    增强的对象字面量,解构赋值
    ES6 模板字符串(template string)
    let和const
    svg实现放大效果
    svg制作风车旋转
    jquery实现某宝放大点击切换
    jQuery之文档处理
    jQuery之属性操作
    jQuery css操作
  • 原文地址:https://www.cnblogs.com/zhouyeqin/p/7214429.html
Copyright © 2020-2023  润新知