• 《Java Spring框架》Spring事务管理


    1、  事务相关知识:

     什么是事务:把多条数据库操作捆绑到一起执行,要么都成功,要么都失败;

     事务的原则ACID:

           原子性:事务包含的所有操作,要么全部成功,要么全部失败回滚,成功全部应用到数据库,失败不能对数据库有任何影响;

           一致性:事务在执行前和执行后必须一致;例如A和B一共有100块钱,无论A、B之间如何转账,他们的钱始终相加都是100;

           隔离性:多用户并发访问同一张表时,数据库为每一个用户开启新的事务,该事务不能被其他事务所影响,相互有隔离;

           持久性:一个事务一旦提交,则对数据库中数据的改变是永久的,即便系统故障也不会丢失;

    并发可能引起的问题:

            脏读:一个事务读取到另一个事务未提交的数据;

            不可重复读:一个事务读取到另一个事务已提交(Update操作)的数据,导致前后读取不一致;

            幻读(虚读):一个事务中读取到别的事务插入(Insert操作)的数据,导致前后读取不一致;

    事务的隔离级别:根据实际情况选择;

             Serializable串行化:可避免脏读、不可重复读和幻读;

             Repeatable read可重复读:可避免脏读、不可重复读;(MySql默认值)

             Read committed读已提交:可避免脏读;

             Read uncommitted读未提交:任何情况都无法保证;

    2、  Spring-aop事务-搭建环境;

    事务基本操作:打开事务、提交事务、回滚事务;

    Spring中利用接口来管理不同框架的事务操作;

         通过实现PlatformTransactionManager接口支持不同的框架完成各自的事务处理;

         为不同平台提供对应的事务管理器的实现:

         JDBC&Mybatis:DataSourceTransactionManager;

    Spring-aop事务通过配置事务的隔离级别、事务传播行为、是否只读来操作;

          隔离级别:串行化、可重复读、读已提交、读未提交;

          是否只读:true:不可改变数据库中的数据,查询操作推荐;  false:可以改变数据库数据;

          事务传播行为:事务方法嵌套调用的规则: xService.x();  ->     yService.y();

                REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置;

                REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务;

                SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行;

                NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起(暂停);

                MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常;

                NEVER:以非事务方式执行,如果当前存在事务,则抛出异常;

                NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与REQUIRED类似的操作。

    3、  Spring-aop事务 – 从麻烦的事务代码中走出之xml配置版aop事务;

    使用经典的转账案例进行测试,准备数据:bean、service、dao;

    使用事务需要额外导入tx包和tx约束;

    配置事务核心管理器: DataSourceTransactionManager;

    配置事务通知 tx:Advice;

    配置aop;

    根据以上知识点,我们来实现spring的事务管理。

    数据库创建一张表:

     代码:

    /**
     * 账户
     * @author hubt11585
     */
    public class Account {
        private Integer id;
        private String name;
        private Double money;
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Double getMoney() {
            return money;
        }
        public void setMoney(Double money) {
            this.money = money;
        }
    }
    public interface AccountDao {
        //扣款
        void subMoney(Integer id, Double money);
        //加款
        void addMoney(Integer id, Double money);
    }
    import org.springframework.jdbc.core.support.JdbcDaoSupport;
    
    public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
    
        @Override
        public void subMoney(Integer id, Double money) {
            String sql = "update account set money = money - ? where id = ?";
            getJdbcTemplate().update(sql, money, id);
        }
    
        @Override
        public void addMoney(Integer id, Double money) {
            String sql = "update account set money = money + ? where id = ?";
            getJdbcTemplate().update(sql, money, id);
        }
    }
    /**
     * 账户service
     * @author Joey
     *
     */
    public interface AccountService {
        
        //转账接口
        void transferAccounts();
    }
    import org.springframework.transaction.annotation.Isolation;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.tao.dao.AccountDao;
    
    @Transactional(isolation=Isolation.DEFAULT, propagation=Propagation.REQUIRED, readOnly=true)
    public class AccountServiceImpl implements AccountService {
    
        //账户dao
        private AccountDao ad;
        public void setAd(AccountDao ad) {
            this.ad = ad;
        }
    
        @Override
        @Transactional(isolation=Isolation.DEFAULT, propagation=Propagation.REQUIRED, readOnly=false)
        public void transferAccounts() {
            //转账逻辑
            
            //先从A账户扣款
            ad.subMoney(1, 50d);
            int a = 1/0;      // 除以0会出现异常
            //再给B账户加款
            ad.addMoney(2, 50d);
        }
    }
    import javax.annotation.Resource;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import com.tao.service.AccountService;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class TxTest {
        
        @Resource(name="accountService")
        private AccountService as;
        @Test
        public void Test1() {
            as.transferAccounts();
        }
    }

    配置文件:applicationContext.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:context="http://www.springframework.org/schema/context"
        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-4.3.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
        
        <!-- 依赖关系 dao ->  -> dataSource -->
        <!-- 读取配置文件 -->
        <context:property-placeholder location="db.properties"/>
        
        <!-- 配置 dataSource -->
        <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${jdbc.driverClass}"/>
            <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
            <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    
        <!-- dao -->
        <bean name="accountDao" class="com.tao.dao.AccountDaoImpl">
            <property name="dataSource" ref="dataSource"/>
        </bean>
        
        <!-- service -->
        <bean name="accountService" class="com.tao.service.AccountServiceImpl">
            <property name="ad"  ref="accountDao"/>
        </bean>
        
        <!-- 配置事务核心管理器 不同平台不一样 -->
        <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
        
        <!-- 事务通知 -->
        <tx:advice id="txAdivce" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="transferAccounts" isolation="DEFAULT"  propagation="REQUIRED" read-only="false"/>
                <tx:method name="save*" isolation="DEFAULT"  propagation="REQUIRED" read-only="false"/>
                <tx:method name="delete*" isolation="DEFAULT"  propagation="REQUIRED" read-only="false"/>
                <tx:method name="update*" isolation="DEFAULT"  propagation="REQUIRED" read-only="false"/>
                <tx:method name="select*" isolation="DEFAULT"  propagation="REQUIRED" read-only="true"/>
            </tx:attributes>
        </tx:advice>
        
        <!-- 配置aop -->
        <aop:config>
            <aop:pointcut expression="execution(* com.tao.service.*ServiceImpl.*(..))" id="txPc"/>
            <aop:advisor advice-ref="txAdivce" pointcut-ref="txPc"/>
        </aop:config>
    </beans>

    运行结果:

    会出现异常

    表里面数据被回滚,并没有出现执行了异常前面部分,效果达到。

    以下是注解版代码:

    只需要调整一下部分代码。

    配置文件中:开启事务:

    <!-- 开启注解事务 -->   <tx:annotation-driven/>

    完整配置文件如下:

    <?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:context="http://www.springframework.org/schema/context"
        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-4.3.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
        
        <!-- 依赖关系 dao ->  -> dataSource -->
        <!-- 读取配置文件 -->
        <context:property-placeholder location="db.properties"/>
        
        <!-- 配置 dataSource -->
        <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="${jdbc.driverClass}"/>
            <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
            <property name="user" value="${jdbc.user}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <!-- dao -->
        <bean name="accountDao" class="com.tao.dao.AccountDaoImpl">
            <property name="dataSource" ref="dataSource"/>
        </bean>
        
        <!-- service -->
        <bean name="accountService" class="com.tao.service.AccountServiceImpl">
            <property name="ad"  ref="accountDao"/>
        </bean>
        
        <!-- 配置事务核心管理器 不同平台不一样 -->
        <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
        
        <!-- 开启注解事务 -->
        <tx:annotation-driven/>
        
    </beans>

    代码:AccountServiceImpl类上(整个类下所有方法有效)或者方法上加上注解即可实现统一事务。

    import org.springframework.transaction.annotation.Isolation;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.tao.dao.AccountDao;
    
    @Transactional(isolation=Isolation.DEFAULT, propagation=Propagation.REQUIRED, readOnly=true)
    public class AccountServiceImpl implements AccountService {
    
        //账户dao
        private AccountDao ad;
        public void setAd(AccountDao ad) {
            this.ad = ad;
        }
    
        @Override
        @Transactional(isolation=Isolation.DEFAULT, propagation=Propagation.REQUIRED, readOnly=false)
        public void transferAccounts() {
            //转账逻辑
            
            //先从A账户扣款
            ad.subMoney(1, 50d);
            int a = 1/0;      // 除以0会出现异常
            //再给B账户加款
            ad.addMoney(2, 50d);
        }
    }

    运行结果:

    会出现异常

    表里面数据被回滚,并没有出现执行了异常前面部分,效果达到。

     总结:通过spring来控制事务,方便整洁。

    This moment will nap, you will have a dream; But this moment study,you will interpret a dream.
  • 相关阅读:
    .net微信公众号开发——群发消息
    .net微信公众号开发——消息与事件
    .net微信公众号开发——基础接口
    .net微信公众号开发——快速入门
    如何实现ASP.NET中网站访问量的统计
    Asp.Net MVC3.0网站统计登录认证的在线人数
    SQL server 2008数据库的备份与还原(转)
    python创建多层目录的方式
    30款基本UX工具
    B树、B-树、B+树、B*树都是什么(转)
  • 原文地址:https://www.cnblogs.com/jssj/p/12148828.html
Copyright © 2020-2023  润新知