Spring: TransactionManager

张天宇 on 2020-03-17

Spring中的事务控制。

PlatformTransactionManager

Spring的事务管理器,提供了常用的操作事务的方法,我们在开发中都使用它的实现类。

1
2
3
4
5
6
//获取事务状态信息
TransactionStatus getTransaction(TransactionDefinition definition)
//提交事务
void commit(TransactionStatus status)
//回滚事务
void rollback(TransactionStatus status)

真正管理事务的对象

1
2
org。springframework。jdbc。datasource。DataSourceTransactionManager//使用Spring JDBC或iBatis进行持久化数据时使用
org。springframework。orm。hibernate5。HibernateTransactionManager//使用Hibernate版本进行持久化数据时使用

TransactionDefinition

事务的定义信息对象,有如下方法:

  1. String getName(): 获取事务对象名称

  2. int getIsolationLevel(): 获取事务隔离级别,设置两个事务之间的数据可见性

    事务的隔离级别由弱到强,可以分为以下五种(参考1参考2)

    1. ISOLATION_DEFAULT: Spring事务管理的的默认级别,使用数据库默认的事务隔离级别
    2. ISOLATION_READ_UNCOMMITTED: 读未提交
    3. ISOLATION_READ_COMMITTED: 读已提交,解决脏读问题
    4. ISOLATION_REPEATABLE_READ: 是否读取其他事务提交修改后的数据,解决不可重复读问题
    5. ISOLATION_SERIALIZABLE: 串行化,是否读取其他事务提交添加后的数据,解决幻影读问题
  3. int getPropagationBehavior(): 获取事务传播行为,设置新事务是否事务以及是否使用当前事务

    通常使用的是前两种REQUIREDSUPPORTS,事务传播行为如下:

    1. REQUIRED: Spring默认事务传播行为。 若当前没有事务,就新建一个事务;若当前已经存在一个事务中,加入到这个事务中。增删改查操作均可用
    2. SUPPORTS: 若当前没有事务,就不使用事务;若当前已经存在一个事务中,加入到这个事务中。查询操作可用
    3. MANDATORY: 使用当前的事务,若当前没有事务,就抛出异常
    4. REQUERS_NEW: 新建事务,若当前在事务中,把当前事务挂起
    5. NOT_SUPPORTED: 以非事务方式执行操作,若当前存在事务,就把当前事务挂起
    6. NEVER:以非事务方式运行,若当前存在事务,抛出异常
    7. NESTED:若当前存在事务,则在嵌套事务内执行;若当前没有事务,则执行REQUIRED类似的操作
  4. int getTimeout(): 获取事务超时时间

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

  5. boolean isReadOnly(): 获取事务是否只读

    建议查询时设置为只读

TransactionStatus

事务状态信息对象,提供操作事务状态的方法如下:

  1. void flush(): 刷新事务
  2. boolean hasSavepoint(): 查询是否存在存储点,事务的一步,按步提交
  3. boolean isCompleted(): 查询事务是否完成
  4. boolean isNewTransaction(): 查询是否是新事务
  5. boolean isRollbackOnly(): 查询事务是否回滚
  6. void setRollbackOnly(): 设置事务回滚

基于XML的声明式事务配置

  1. 配置事务管理器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    <!--新的约束-->
    <?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
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/tx
    https://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/aop
    https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--配置数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/test"></property>
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>
    </bean>
    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
    </bean>
    </beans>
  2. 配置事务的通知,此时需要导入事务的约束

    1
    <tx:advice id="txAdvice" transaction-manager="transactionManager"></tx:advice>
  3. 配置AOP中切入点表达式

  4. 建立事务通知和切入点表达式的对应关系

    1
    2
    3
    4
    5
    6
    <!--配置切入点表达式-->
    <aop:config>
    <aop:pointcut id="pt1" expression="execution(* com.ztygalaxy.service.impl.*.*(..))"/>
    <!--配置切入点表达式和事务通知的对应关系-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
    </aop:config>
  5. 配置事务的属性,在事务的通知tx:advice标签内部配置。

    • isolation: 用于指定事务的隔离级别,默认值是DEFAULT,表示使用数据库的默认隔离级别。
    • no-rollback-for: 用于指定一个异常,当产生该异常时,事务不回滚。没有默认值,表示任何异常都回滚。
    • propagation: 用于指定事务的传播行为,默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。
    • read-only: 用于指定事务是否只读,只有查询方法才能设置为true,默认值是false,表示读写。
    • rollback-for: 用于指定一个异常,当产生一个异常时,事务回滚,产生其他异常时不回滚。没有默认值,表示任何异常值都回滚。
    • timeout: 用于指定事务的超时时间,默认值是-1,表示永不超时,如果制定了数值,以秒为单位。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <!--配置事务的通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
    <!--通配符-->
    <tx:method name="*" propagation="REQUIRED" read-only="false"/>
    <!--查询方法-->
    <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
    </tx:attributes>
    </tx:advice>

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

  1. 配置事务管理器

  2. 开启spring对注解事务的支持

    1
    2
    3
    4
    5
    6
    //Dao中取消JDBCSupport
    @Repository("accountDao")
    public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    <?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"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/tx
    https://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/aop
    https://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.ztygalaxy"></context:component-scan>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--配置数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/test"></property>
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>
    </bean>

    <!--spring中基于声明式事务配置-->
    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--开启spring对注解事务的支持-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    </beans>
  3. 在需要事务支持的地方使用@Transaction注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Service("accountService")
    @Transactional
    public class AccountServiceImpl implements IAccountService {}
    //或者对整个类定义后,对单个方法修改
    @Service("accountService")
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public class AccountServiceImpl implements IAccountService {
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public void transfer(String sourceName, String targetName, Float money) {}
    }

基于纯注解的事务配置

  1. 事务控制配置类TransactionConfig

    1
    2
    3
    4
    5
    6
    7
    public class TransactionConfig {
    //用于创建事务管理器对象
    @Bean(name="transactionManager")
    public PlatformTransactionManager createTransactionManager(DataSource dataSource){
    return new DataSourceTransactionManager(dataSource);
    }
    }
  2. JDBC配置类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    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;
    //创建JdbcTemplate对象
    @Bean(name = "jdbcTemplate")
    public JdbcTemplate createJdbcTemplate(DataSource dataSource){
    return new JdbcTemplate(dataSource);
    }

    //创建数据源对象
    @Bean(name="dataSource")
    public DataSource createDataSource(){
    DriverManagerDataSource ds=new DriverManagerDataSource();
    ds.setDriverClassName(driver);
    ds.setUrl(url);
    ds.setUsername(username);
    ds.setPassword(password);
    return ds;
    }
    }
  3. JDBC配置文件

    1
    2
    3
    4
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/test
    jdbc.username=root
    jdbc.password=root
  4. 总配置类

    1
    2
    3
    4
    5
    6
    7
    @Configuration
    @ComponentScan("com.ztygalaxy")
    @Import({JdbcConfig.class, TransactionConfig.class})
    @PropertySource("jdbcConfig.properties")
    @EnableTransactionManagement
    public class SpringConfiguration {
    }
  5. 测试类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = SpringConfiguration.class)
    public class AccountService {
    @Autowired
    private IAccountService as;
    @Test
    public void transfer(){
    as.transfer("bbb","ccc",234f);
    }
    }

Spring编程式事务控制

Spring的编程式事务控制不能实现解耦,反而使耦合更加严重了,因此不推荐使用。

事务基础知识

什么是事务

事务四大特性

不考虑隔离性会产生的3个问题

解决办法:四种隔离级别

参考