Spring事务(Transactions)的原理与实现

1、事务

1.1 什么是事务?

事务是用户定义的数据库操作的集合,这些操作作为一个完整的有机工作单元,要么全部正确执行,要么全部不执行。
四大特性:
(1)原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
(2)一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
(3)隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
(4)持久性(durability)。持续性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

1.2 事务的作用?

以银行转账为例,若业务方法没有事务时,假如在业务方法执行时发生异常,有可能会造成数据库数据不一致。
解决方法是在业务方法中加入事务。若发生异常,事务回滚(回滚方法由Spring事务自动完成,不需要自定义)。

2、Spring对事务的支持

2.1 Spring的事务管理主要包括3个接口

TransactionDefinition:封装事务的隔离级别,超时时间,是否为只读事务和事务的传播规则等事务属性,可通过XML配置具体信息。
PlatformTransactionManager:根据TransactionDefinition提供的事务属性配置信息,创建事务。
TransactionStatus:封装了事务的具体运行状态。比如,是否是新开启事务,是否已经提交事务,设置当前事务为rollback-only等。

2.2 Spring的事务管理

1,PlatformTransactionManager:接口统一,抽取处理事务操作相关的方法;
(1):TransactionStatus getTransaction(TransactionDefinition definition):
根据事务定义信息从事务环境中返回一个已存在的事务,或者创建一个新的事务,并用TransactionStatus描述该事务的状态。
(2):void commit(TransactionStatus status):
根据事务的状态提交事务,如果事务状态已经标识为rollback-only,该方法执行回滚事务的操作。
(3):void rollback(TransactionStatus status):
将事务回滚,当commit方法抛出异常时,rollback会被隐式调用
2,在使用spring管理事务的时候,首先得告诉spring使用哪一个事务管理器; 看图
3,常用的事务管理器:
DataSourceTransactionManager:使用JDBC,MyBatis的事务管理器;
HibernateTransactionManager:使用Hibernate的事务管理器;
在这里插入图片描述

3、Spring事务的配置—>xml方式

模拟在数据库中两个用户转账的案例。

3.1 准备Service层及Dao层相关类

在这里插入图片描述
(1) 业务层完成转账方法:

public class AccountServiceImpl implements IAccountService {

    @Setter
    private IAccountDao dao;

    public void trans(Integer inId, Integer outId, Integer money) {

        //转出
        this.transOut(outId,money);

        //转入
        this.transIn(inId,money);
        
    public void transIn(Integer inId, Integer money) {
        dao.transIn(inId,money);
    }

    public void transOut(Integer outId, Integer money) {
        dao.transOut(outId,money);
    }
   }

(2)数据库层完成转入和转出方法

public class AccountDaoImpl implements IAccountDao {

    /*通过Spring JDBC获取数据库连接对象*/
    private JdbcTemplate jdbcTemplate;
    
    public void setDataSource(DataSource dataSource){
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    /*
        转入
     */
    public void transIn(Integer inId, Integer money) {
        jdbcTemplate.update(
              "update bank_count set money = money + ? where count_id = ?",
                money,inId
        );
    }

    /*
        转出
     */
    public void transOut(Integer outId, Integer money) {
        jdbcTemplate.update(
                "update bank_count set money = money - ? where count_id = ?",
                money,outId
        );
    }
}

3.2 配置事务管理器

1.首先要配置事务管理器,JDBC和Mybatis使用的事务管理器是org.springframework.jdbc.datasource.DataSourceTransactionManager
2.事务管理器需要配置dataSource属性
3.配置Advice需要关联事务管理器.假如不配置transaction-manager属性.会自动关联容器中名字为"transactionManager"的事务管理器.
4.配置aop,将pointcut和advice关联

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:property-placeholder location="classpath:db.properties" system-properties-mode="NEVER"/>

    <bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
    </bean>

    <bean id="service" class="com.project.TransactionManager.service.impl.AccountServiceImpl">
        <property name="dao" ref="dao"/>
    </bean>

    <bean id="dao" class="com.project.TransactionManager.dao.impl.AccountDaoImpl">
        <property name="DataSource" ref="datasource"/>
    </bean>

    <!--配置Spring的事务控制,在AOP的基础上实现,出现错误自动回滚-->
    <!--增加事务管理器-->
    <bean id="manager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--配置jdbc连接池对象-->
        <property name="dataSource" ref="datasource"/>
    </bean>

    <!-- xml配置事务-->
    <aop:config>
        <aop:pointcut id="point" expression="execution( * com.project.TransactionManager.service.IAccountService.*(..))"/>
        <aop:advisor advice-ref="advice" pointcut-ref="point"/>
    </aop:config>
    <!-- 事务增强器-->
    <tx:advice id="advice" transaction-manager="manager">
        <tx:attributes>
            <!--查询方法时设置为只读事务,非查询的方法时设置非只读事务
            通用的一个事务管理器:-->
            <tx:method name="select*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="*"/>
            <!--或:只配置业务方法名(不推荐)
            <tx:method name="trans"/>-->
        </tx:attributes>
    </tx:advice>

3.3 测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class App {


    @Autowired
    private IAccountService service;

    @Test
    public void testTrans() throws Exception {

        //1号给2号转钱
        service.trans(2,1,1000);

    }
}

3.4 小结

1.事务方法的属性细节:
name属性为必须属性,可以使用通配符*
查询的方法read-only设置为"true",可以提升查询的执行效率.
在这里插入图片描述
2 通用的事务配置
在这里插入图片描述

4、Spring事务的配置—>注解方式

4.1 添加事务的注解解析器

打开spring的xml配置文件,添加事务的注解解析器,注解解析器需要关联事务处理器
<tx:annotation-driven transaction-manager=“transactionManager”/>

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:property-placeholder location="classpath:db.properties" system-properties-mode="NEVER"/>

    <bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
    </bean>

    <bean id="service" class="com.project.TransactionManager.service.impl.AccountServiceImpl">
        <property name="dao" ref="dao"/>
    </bean>

    <bean id="dao" class="com.project.TransactionManager.dao.impl.AccountDaoImpl">
        <property name="DataSource" ref="datasource"/>
    </bean>

    <!--配置Spring的事务控制,在AOP的基础上实现,出现错误自动回滚-->
    <!--增加事务管理器-->
    <bean id="manager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--配置jdbc连接池对象-->
        <property name="dataSource" ref="datasource"/>
    </bean>

    <!--注解配置事务管理器
    (1)只需要在xml中配置事务注解驱动
    (2)在业务类的实现类上加注解@Transactional
    -->
    <tx:annotation-driven transaction-manager="manager"/>

</beans>

4.2 在业务层的实现类上加注解@Transactional

(1)注解中相应的属性可以配置事务控制的相关细节(隔离级别/传播规则/是否只读等)
(2)类中的方法也可以添加@Transactional注解,同样可以对方法进行细节配置.方法中的配置信息会覆盖类中的同名配置.

@Transactional(readOnly = false)
public class AccountServiceImpl implements IAccountService {

    @Setter
    private IAccountDao dao;
    //@Transactional(readOnly = true)
    public void trans(Integer inId, Integer outId, Integer money) {

        //转出
        this.transOut(outId,money);

        System.out.println(1/0);
        //转入
        this.transIn(inId,money);
    }


    public void transIn(Integer inId, Integer money) {
        dao.transIn(inId,money);
    }

    public void transOut(Integer outId, Integer money) {
        dao.transOut(outId,money);
    }

}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值