Spring

【Spring】12 - Spring 中的事务控制

2020-10-26  本文已影响0人  itlu

1. Spring 事务控制我们要明确的

  1. 第一: JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案。

  2. 第二: spring 框架为我们提供了一组事务控制的接口。具体在后面的第二小节介绍。这组接口是在spring-tx-5.0.2.RELEASE.jar中。

  3. 第三: spring 的事务控制都是基于 AOP的,它既可以使用编程的方式实现,也可以使用配置的方式实现。我们学习的重点是使用配置的方式实现

2. Spring 中事务控制的 API 介绍

2.1 PlatformTransactionManager 接口

  1. 此接口是 spring 的事务管理器,它里面提供了我们常用的操作事务的方法,如下图:
spring事务管理器图解
  1. 我们在开发中都是使用它的实现类。

  2. 真正管理事务的对象

  • org.springframework.jdbc.datasource.DataSourceTransactionManager 使用Spring JDBCBatis 进行持久化数据时使用。
  • org.springframework.orm.hibernate5.HibernateTransactionManager 使用
    Hibernate版本进行持久化数据时使用。

2.2 TransactionDefinition

  1. 它是事务的定义信息对象,里面有如下方法:
TransactionDefinition

2.2.1 事务的隔离级别

事务的隔离级别

2.2.2 事务的传播行为

  1. REQUIRED :如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)。

  2. SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)。

  3. MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。

  4. REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。

  5. NOT_SUPPORTED :以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

  6. NEVER:以非事务方式运行,如果当前存在事务,抛出异常。

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

2.2.3 超时时间

默认值是-1,没有超时限制。如果有,以秒为单位进行设置。

2.2.4 是否是只读事务

建议查询时设置为只读。

2.3 TransactionStatus

  1. 此接口提供的是事务具体的运行状态,方法介绍如下图:
TransactionStatus

3. Spring 中的事务控制环境搭建

3.1 创建项目并导入依赖

  1. 创建一个不使用骨架的Maven 项目 。

  2. 导入依赖 : Spring 的 事务控制需要 AOP 的支持

    <packaging>jar</packaging>

    <dependencies>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>


        <!-- Spring整合Junit单元测试jar包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <version>4.12</version>
         </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

        <!-- 检查切入点表达式的jar spring 的事务控制需要AOP的支持 -->
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
            <scope>runtime</scope>
        </dependency>

        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

    </dependencies>

3.2 继续使用 Spring_JdbcTemplate 中的代码

  1. demo 地址 : Spring 中的 JdbcTemplate

3.3 创建业务层接口及实现类

  1. AccountService业务层接口:
public interface AccountService {


    /**
     * 根据id查询账户信息
     *
     * @param id
     * @return
     */
    Account findById(Integer id);

    /**
     * 根据name查询账户信息
     *
     * @param name
     * @return
     */
    Account findByName(String name);

    /**
     * 更新账户信息
     *
     * @param account
     */
    void update(Account account);

    /**
     * 提供 transfer zhuanZhang 的方法
     * @param sourceName
     * @param targetName
     * @param money
     */
    void transfer(String sourceName , String targetName , Float money);

}
  1. AccountServiceImpl 业务层实现类 :
public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao;

    /**
     * 提供  setter 方法进行 为其提供注入
     *
     * @param accountDao
     */
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public Account findById(Integer id) {
        return accountDao.findById(id);
    }

    public Account findByName(String name) {
        return accountDao.findByName(name);
    }

    public void update(Account account) {
        accountDao.update(account);
    }

    public void transfer(String sourceName, String targetName, Float money) {
        Account source = accountDao.findByName(sourceName);
        Account target = accountDao.findByName(targetName);

        source.setMoney(source.getMoney() - money);
        target.setMoney(target.getMoney() + money);

        accountDao.update(source);
        accountDao.update(target);
    }

}

3.4 配置 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="datasource"/>
    </bean>

    <bean id="accountService" class="com.lyp.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <bean id="accountDao" class="com.lyp.dao.impl.AccountDaoImpl">
        <!--<property name="jt" ref="jdbcTemplate"/>-->
        <property name="dataSource" ref="datasource"/>
    </bean>

    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/spring"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
</beans>

3.5 编写测试类进行测试 :

  1. 使用 Spring 整合 Junit 的方式 ,这里需要注意我们使用的 Junit版本需要是 4.12 之后的 。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class SpringTxTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void testFindById() {
        Account account = accountService.findById(1);
        System.out.println(account);
    }

    @Test
    public void testTransfer() {
        accountService.transfer("aaa","bbb",100.f);
    }
}

4. 使用基于 xml 的方式配置Spring 的声明式事务控制

  1. 导入事务和 aop的约束 :
<?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: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/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">

</beans>

4.1 Spring 中基于 xml 的声明式事务控制的步骤

  1. 配置事务管理器 :
    <!-- 1. 配置事务管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="datasource"/>
    </bean>
  1. 配置事务的通知(tx:advice标签) : 此时需要导入事务和 aop的约束。使用tx:advice标签 配置事务通知。 属性如下:
  • id : 为事务通知起一个唯一的标识。
  • transaction-manager :给事务提供一个事务管理器的引用。
    <!-- 2. 配置事务的通知 -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        
    </tx:advice>
  1. 配置 aop
     <!-- 3. 配置aop -->
    <aop:config>
        <aop:pointcut id="pc" expression="execution(* com.lyp.service.impl.*.*(..))"/>
        
    </aop:config>
  1. 建立通知和切入点表达式的对应关系: 使用 aop:advice 标签建立与事务通知的关系。
     <!-- 3. 配置aop -->
    <aop:config>
        <aop:pointcut id="pc" expression="execution(* com.lyp.service.impl.*.*(..))"/>
        <!-- 4. 建立切入点表达式与通知之间的关系  -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
    </aop:config>
  1. 配置事务的属性:是在事务的通知 tx:advice 标签内部进行配置:使用tx:attributestx:method标签
  • isolation: 用于指定事务的隔离级别。 默认的是DEFAULT,表示使用的是数据库的默认隔离级别。
  • propagation:用于指定事务的传播行为,默认值是REQUIRED,表示一定会有事务。增删改选择它,查询应选择SUPPORTS
  • read-only:用于指定事务是否只读,只有查询方法才设置为true,默认值是false,表示读写。
  • timeout:用于指定事务的超时时间。默认值是 -1 表示永不超时如果指定数值,其单位是秒。
  • rollback-for : 用于指定一个异常当产生异常时,事务回滚,产生其他异常时不会滚,没有默认值,表示任何异常都回滚
  • no-rollback-for:用于指定一个异常当产生该异常的时候,事务不回滚,产生其他异常的时候事务回滚,没有默认值,表示任何异常都回滚。
     <!-- 5. 配置事务的属性 -->
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
  1. Spring 中基于 xml 的声明式事务控制代码如下 :
    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/spring"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>

    <!-- 1. 配置事务管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="datasource"/>
    </bean>

    <!-- 2. 配置事务的通知 -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <!-- 5. 配置事务的属性 -->
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>

    <!-- 3. 配置aop -->
    <aop:config>
        <aop:pointcut id="pc" expression="execution(* com.lyp.service.impl.*.*(..))"/>
        <!-- 4. 建立切入点表达式与通知之间的关系  -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
    </aop:config>
  1. demo地址: spring中基于xml的声明式事务控制

5. Spring 基于注解的声明式事务控制

  1. 在上面的项目的基础上进行修改。

  2. 主要修改 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:context="http://www.springframework.org/schema/context"
       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/tx
            http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.lyp"/>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="datasource"/>
    </bean>


    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/spring"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>

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

    <!-- 开启spring对注解事务的支持 -->
    <tx:annotation-driven/>

</beans>
  1. 只需在业务层实现类上面加上注解 @Transactional即可实现注解配置事务的控制。

6. Spring 基于纯注解的声明式事务控制

  1. 在上面的项目的基础上进行修改;
  1. 创建一个配置类 SpringConfiguration用于代替bean.xml的作用:
@Configuration
@ComponentScan("com.lyp")
public class SpringConfiguration {
}
  1. 创建一个 JdbcConfig 类用于配置JdbcTemplateDataSource 对象 :
public class JdbcConfig {

    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Bean
    public JdbcTemplate createJdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }


    @Bean
    public DataSource getDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

}
  1. 创建一个事务管理器配置类 :
public class TransactionManagerConfig {

    @Bean
    public PlatformTransactionManager createTransactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}
  1. 创建 jdbcConfig.properties 配置文件 :
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=root
  1. 修改主配置文件内容 :
@Configuration
@ComponentScan("com.lyp")
@Import(value = {JdbcConfig.class, TransactionManagerConfig.class})
@PropertySource("JdbcConfig.properties")
@EnableTransactionManagement
public class SpringConfiguration {
}
  1. 修改测试文件中的代码并进行测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class SpringTxTest {....}
  1. demo地址: spring基于纯注解的事务控制配置。
上一篇下一篇

猜你喜欢

热点阅读