0
点赞
收藏
分享

微信扫一扫

Spring事务机制---XML配置事务和注解方式事务

文章目录

1. Spring事务管理机制

1.1 Spring事务管理机制

Spring事务管理高层抽象主要包括3个接口,Spring事务主要是由他们共同完成的:

  1. PlatformTransactionManager:事务管理器–主要用于平台相关事务的管理
  2. TransactionDefinition:事务定义信息(隔离级别,传播,超时,只读)–通过配置如何进行事务管理
  3. TransactionStatus:事务具体运行状态–事务管理过程中,每个时间点事务的状态信息。

1.2 PlatformTransactionManager事务管理器

  1. 该接口定义了3个方法
public interface PlatformTransactionManager extends TransactionManager {
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;

    void commit(TransactionStatus var1) throws TransactionException;

    void rollback(TransactionStatus var1) throws TransactionException;
}
  1. Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现:
接口实现类说明
org.springframework.jdbc.datasource.DataSourceTransactionManager使用Spring JDBC或者Mybatis进行持久化数据时使用
org.springframework.orm.hibernate5.HibernateTransactionManager使用Hibernate5.0版本进行持久化数据时使用
org.springframework.orm.jpa.JpaTransactionManager使用JPA进行持久化时使用
org.springframework.jdo.JdoTransactionManager当持久化机制是Jdo时使用
org.springframework.transaction.jta.JtaTransactionManager使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用
  1. DataSourceTransactionManager针对JdbcTemplate、Mybatis事务控制,使用Connection(连接)进行事务控制:

1.3 TransactionDefinition事务定义信息

  1. 用来定义事务相关属性的,给事务管理器用

该接口主要提供的方法:

  1. getIsolationLevel:获取事务隔离级别
  2. getPropagationBehavior:获取事务的传播行为
  3. getTimeout:获取超时时间(事务的有效期)
  4. isReadOnly:是否只读(保存、更新、删除–对数据进行操作,变成可读写的,查询-设置这个属性为true,只能读不能写)。这些事务的定义信息,都可以在配置文件中配置和定制

1.3.1 常用事务隔离级别

  1. READ_UNCOMMITED:读未提交
  2. READ_COMMITTED:读已提交
  3. REPEATABLE_READ:可重复读
  4. SERIALIZABLE:序列化

MySQL 默认隔离级别为可重复读
Oracle默认隔离级别为读已提交

1.3.2 事务的传播行为PropagationBehavior

  1. 什么是事务的传播行为,有什么用?

什么是事务

  1. 业务层两个方法面临的事务问题

1.3.1 事务的传播行为

  1. 事务的传播行为
事务的传播行为类型说明
PROPAGATION_REQUIRED支持当前事务,如果不存在就新建一个
PROPAGATION_SUPPORTS支持当前事务,如果不存在,就不使用事务
PROPAGATION_MANDATORY支持当前事务,如果不存在,就抛出异常
PROPAGATION_REQUIRES_NEW如果有事务存在,挂起当前事务,创建一个新的事务
PROPAGATION_NOT_SUPPORTED以非事务方式运行,如果有事务存在,挂起当前事务
PROPAGATION_NEVER以非事务方式运行,如果有事务存在,抛出异常
PROPAGATION_NESTED如果当前事务存在,则嵌套事务执行,只对DataSourceTransactionManager起效
  1. 传播行为分类

第一类:PROPAGATION_REQUIRED(默认值)、PROPAGATION_SUPPORTS、PROPAGATION_MANDATORY
支持当前事务, A调用B,如果A事务存在,B和A处于同一个事务 。事务默认传播行为 REQUIRED。最常用的。

第二类:PROPAGATION_REQUIRES_NEW、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER
不会支持原来的事务,A调用B, 如果A事务存在, B肯定不会和A处于同一个事务。常用的事务传播行为:PROPAGATION_REQUIRES_NEW

第三类:PROPAGATION_NESTED
嵌套事务,只对DataSourceTransactionManager有效,底层使用JDBC的SavePoint机制,允许在同一个事务设置保存点,回滚保存点

区分:REQUIRED、REQUIRES_NEW、NESTED
REQUIRED:只有一个事务(默认,推荐)
REQUIRES_NEW:存在两个事务,如果事务存在,挂起事务,重新又开启了一个新的事务
NESTED 嵌套事务,事务可以设置保存点,回滚到保存点,选择提交或者回滚(例如,游戏失败后,有3次复活机会)

1.4 事务状态:TransactionStatus

  1. TransactionStatus接口提供了一些方法
  1. 事务的结束:必须通过commit确认事务提交,rollback作用标记为回滚。也就是说,如果数据库操作过程中,如果只回滚,后面不操作,数据库关闭连接的时候,自动发出了commit。rollback只是进行了一个回滚的标记,最后还是有commit操作。commit是所有事务最终的操作。

1.5 Spring中要达到事务操作的三个步骤

  1. 首先需要配置事务管理器,TransactionManager,进行事务管理。
  2. 然后根据TransactionDefinition(事务定义信息),通过TransactionManager事务管理器进行事务管理。
  3. 最后事务运行过程中,每个时刻都可以通过获取TransactionStatus(事务状态)来了解事务的运行状态。

1.6 Spring事务管理两种方式

1.6.1 编程式事务管理

  1. 通过TransactionTemplate手动管理事务。在实际应用中很少使用,原因是需要修改原来代码,加入事务管理代码(侵入性)

1.6.2 通过XML或者注解配置声明式事务

  1. Spring声明式事务是通过AOP实现的(环绕通知),代码侵入性小。只需要告诉Spring,哪些方法需要事务就行

2. 声明式事务管理案例-转账(xml-tx,aop,@Transactional)

2.1 测试转账:

  1. Tom账户取出1000元,存放到Jack账户上。
  2. 数据表和测试数据准备:
 CREATE TABLE t_account  (

 id INT(11) NOT NULL AUTO_INCREMENT,

 name VARCHAR(20) NOT NULL,

 money DOUBLE DEFAULT NULL,

 PRIMARY KEY (id)

) 
INSERT INTO `t_account` (`id`, `name`, `money`) VALUES ('1', 'tom', '1000');
INSERT INTO `t_account` (`id`, `name`, `money`) VALUES ('2', 'jack', '1100');
INSERT INTO `t_account` (`id`, `name`, `money`) VALUES ('3', 'rose', '2000');
  1. 引入依赖配置
  <dependencies>
      <!-- junit测试 -->
      <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.10</version>
          <scope>test</scope>
      </dependency>

      <!-- spring核心包 -->
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>4.2.8.RELEASE</version>
      </dependency>
      <!-- spring集成测试 -->
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-test</artifactId>
          <version>4.2.8.RELEASE</version>
      </dependency>

      <!-- spring事物管理 -->
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jdbc</artifactId>
          <version>4.2.8.RELEASE</version>
      </dependency>



      <!-- c3p0数据源 -->
      <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
      <dependency>
          <groupId>com.mchange</groupId>
          <artifactId>c3p0</artifactId>
          <version>0.9.5.5</version>
      </dependency>

      <!-- 数据库驱动 -->
      <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.49</version>
      </dependency>


      <!-- 注解开发切面包 -->
      <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>1.9.1</version>
      </dependency>

  </dependencies>
  1. 引入spring配置applicationContext.xml:添加xmln的XML Namespaces的命名空间的主要目的是当编辑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:p="http://www.springframework.org/schema/p"
       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">


</beans>
  1. 引入log4j.properties和db.properties
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.rootLogger=info, stdout
  1. 引入db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.password=123456
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring?useUnicode=true&characterEncoding=utf8
jdbc.username=root

2.2 实现转账

  1. IAccountDao.java
/**
 * @Author: zkfgyzy@163.com
 * @Date: 2022/5/1 10:50
 */
public interface IAccountDao {

    /**
     * 转出
     */
    void out(String outName,Double money);


    /**
     * 转入
     * @param outName
     * @param money
     */
    void in(String outName, Double money);


}
  1. AccountDaoImpl.java
/**
 * @Author: zkfgyzy@163.com
 * @Date: 2022/5/1 11:00
 */
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
    @Override
    public void out(String outName, Double money) {

        String sql="update t_account set money=money-? where name = ?";
        super.getJdbcTemplate().update(sql,money,outName);

    }

    @Override
    public void in(String inName, Double money) {

        String sql="update t_account set money=money+? where name = ?";
        super.getJdbcTemplate().update(sql,money,inName);

    }
}
  1. IAccountService.java
/**
 * @Author: zkfgyzy@163.com
 * @Date: 2022/5/1 11:41
 */
public interface IAccountService {

    void transfer(String outName,String inName,Double money);
}
  1. AccountServiceImpl.java
/**
 * @Author: zkfgyzy@163.com
 * @Date: 2022/5/1 11:43
 */
public class AccountServiceImpl implements IAccountService {


    private IAccountDao iAccountDao;

    public IAccountDao getiAccountDao() {
        return iAccountDao;
    }

    public void setiAccountDao(IAccountDao iAccountDao) {
        this.iAccountDao = iAccountDao;
    }

    @Override
    public void transfer(String outName, String inName, Double money) {

        //先转出
        iAccountDao.out(outName,money);
        //转入
        iAccountDao.in(inName,money);

    }

}
  1. Junit测试类
/**
 * @Author: zkfgyzy@163.com
 * @Date: 2022/5/1 11:58
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")  //主要是为了加载配置
public class AccountServiceImplTest extends TestCase {



    @Autowired
    private IAccountService iAccountService;

    @Test
    public void transfer() {

        iAccountService.transfer("tom","jack",1000d);
    }
}
  1. 配置bean的依赖注入
<?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:p="http://www.springframework.org/schema/p"
     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" />

  <!--配置dataSource-->
  <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
      <!--配置连接参数-->
      <property name="driverClass" value="${jdbc.driver}" />
      <property name="jdbcUrl" value="${jdbc.url}" />
      <property name="user" value="${jdbc.username}" />
      <property name="password" value="${jdbc.password}" />

  </bean>

  <!--配置dao和service-->
  <bean id="iAccountDao" class="net.toposphere.dao.impl.AccountDaoImpl">
      <!--注入数据源-->
      <property name="dataSource" ref="dataSource"> </property>

  </bean>

  <bean id="iAccountService" class="net.toposphere.service.impl.AccountServiceImpl">
      <property name="iAccountDao" ref="iAccountDao"></property>
  </bean>

</beans>

3. XML配置方式添加事务管理(tx、aop元素)

3.1 AOP三步走

  1. 确定目标:需要对AccountService的transfer方法上,配置切入点。将需要管理的内容交给Spring管理
  2. 需要Advice(环绕通知),方法前开启事务,方法后提交关闭事务
  3. 配置切面和切入点

3.1.1 确定目标

  1. 将需要操作的Service的方法交给Spring管理
<bean id="iAccountService" class="net.toposphere.service.impl.AccountServiceImpl">
    <property name="iAccountDao" ref="iAccountDao"></property>
</bean>

3.1.2 配置环绕通知

  1. 什么是环绕通知?环绕通知就是,当你调用这个方法前,我需要操作一些内容,当你调用方法后我需要操作一些内容。
  2. 配置环绕通知,Spring为了简化事务配置,提供了<tx:advice>来配置事务管理,也可以理解为该标签是Spring为你实现好了的事务的通知增强方案。
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!--配置事务的管理策略-->
    <tx:attributes>
        <!--需要被增强的方法-->
        <tx:method name="transfer" isolation="DEFAULT" no-rollback-for="" rollback-for="" read-only="" propagation="" timeout="-1"/>
    </tx:attributes>
</tx:advice>
  • name:配置事务的方法
  • isolation:数据库的隔离级别
  • no-rollback-for:遇到那些错误不回滚
  • rollback-for:遇到那些问题进行回滚
  • read-only:表示所有的事务是不是都可以对数据进行处理,如果是查询,都可以,可以设置为true
  • propagation:事务的传播机制

3.1.3 配置切入点和切入面

  1. 切入点:是指要处理的对象
  2. 切入面:指将要处理的对象,通知给要处理的Bean工厂,进行处理。把点交给面,由面通知Bean处理。
  <!--配置切入点和切入面-->
<aop:config>
    <!--切入点-->
    <aop:pointcut id="mycut" expression="bean(*Service)"/>
    <!--切面-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="mycut"></aop:advisor>
</aop:config>

3.1.4 整体配置

<?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:p="http://www.springframework.org/schema/p"
       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" />

    <!--配置dataSource-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--配置连接参数-->
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />

    </bean>

    <!--配置dao和service-->
    <bean id="iAccountDao" class="net.toposphere.dao.impl.AccountDaoImpl">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"> </property>

    </bean>

    <bean id="iAccountService" class="net.toposphere.service.impl.AccountServiceImpl">
        <property name="iAccountDao" ref="iAccountDao"></property>
    </bean>


    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource" />


    </bean>

    <!--配置事务通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--配置事务的管理策略-->
        <tx:attributes>
            <!--需要被增强的方法-->
            <!--<tx:method name="transfer" isolation="DEFAULT" no-rollback-for="" rollback-for="" read-only="" propagation="" timeout="-1"/>-->
            <tx:method name="transfer"></tx:method>
        </tx:attributes>
    </tx:advice>


      <!--配置切入点和切入面-->
    <aop:config>
        <!--切入点-->
        <aop:pointcut id="mycut" expression="bean(*Service)"/>
        <!--切面-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="mycut"></aop:advisor>
    </aop:config>


</beans>

4. Spring注解方式配置事务管理

4.1 Spring注解配置步骤

  1. 配置事务管理器
  2. 配置数据源
  3. 配置注解扫描器
  4. 配置事务启动开关
  5. 方法或者类上配置事务注解
  6. 启动测试

4.2 配置内容

  1. xml配置事务管理器
  2. xml配置数据源
  3. 配置注解扫描
  4. 配置事务开启开关
<?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:p="http://www.springframework.org/schema/p"
       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" />

    <!--配置dataSource-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--配置连接参数-->
        <property name="driverClass" value="${jdbc.driver}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />

    </bean>


    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource" />


    </bean>

    <!--配置注解扫描-->
    <context:component-scan base-package="net.toposphere"/>


    <!--配置事务注解驱动:注解生效开关-->
    <tx:annotation-driven transaction-manager="transactionManager" />


</beans>

4.3 使用注解将数据源配置到DataSource

  1. 注解配置JDBC数据源
/**
 * @Author: zkfgyzy@163.com
 * @Date: 2022/5/1 11:00
 */

@Service
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {

    
    
    //通过注解Autowired将dataSource的bean注入到JdbcDaoSupport的数据源中
    @Autowired
    public void setSuperDatasource(DataSource dataSource){
        super.setDataSource(dataSource);
    }
    
    @Override
    public void out(String outName, Double money) {

        String sql="update t_account set money=money-? where name = ?";
        super.getJdbcTemplate().update(sql,money,outName);

    }

    @Override
    public void in(String inName, Double money) {

        String sql="update t_account set money=money+? where name = ?";
        super.getJdbcTemplate().update(sql,money,inName);

    }
}
  1. 在方法上添加事务注解,并进行测试
/**
 * @Author: zkfgyzy@163.com
 * @Date: 2022/5/1 11:43
 */
@Service
public class AccountServiceImpl implements IAccountService {


    @Autowired
    private IAccountDao iAccountDao;

    @Override
    @Transactional
    public void transfer(String outName, String inName, Double money) {

        //先转出
        iAccountDao.out(outName,money);

        //异常
        int i=1/0;
        //转入
        iAccountDao.in(inName,money);

    }

}
  • @Transactional可以用在方法上,也可以用在类上,如果方法和类上都存在,则就近原则,使用方法上的。

小结

举报

相关推荐

0 条评论