spring编程式和声明式事务控制

可优先参考:@Transactional Spring 事务的深入学习与使用【两万字】

1. 编程式事务控制

@Autowired
private RoleMapper roleMapper;

@Autowired
private PlatformTransactionManager platformTransactionManager;

@GetMapping("manualTransaction")
public Object manualTransaction() {

    DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
    // 事务隔离级别
    transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
    // 事务传播行为
    transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    // 是否只读事务
    transactionDefinition.setReadOnly(false);
    // 事务超时时间
    transactionDefinition.setTimeout(-1);

    TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);

    RoleEntity roleEntity = new RoleEntity();
    roleEntity.setName("role001");
    roleEntity.setIsDel(1);
    roleEntity.setMerchantCode("1");

    // spring事务接管mybatis事务的过程
    // SqlSessionFactoryBean -> SpringManagedTransactionFactory -> SpringManagedTransaction -> DataSourceUtils.getConnection(this.dataSource)
    roleMapper.insert(roleEntity);
    log.info("编程式事务: roleEntity->{}, {}", roleEntity);

    // if (1 > 0) {
    //     platformTransactionManager.commit(transactionStatus); // 提交事务
    //     return roleEntity;
    // }

    platformTransactionManager.rollback(transactionStatus); // 回滚事务
    return roleEntity;
}

@Autowired
private TransactionTemplate transactionTemplate;

@GetMapping("manualTransaction2")
public Object manualTransaction2() {

    // TransactionTemplate继承自DefaultTransactionDefinition(实现了TransactionDefinition接口)
    // (其实就是上一种的简化版本)
    RoleEntity roleEntity = transactionTemplate.execute(new TransactionCallback<RoleEntity>() {
        @Override
        public RoleEntity doInTransaction(TransactionStatus status) {
            RoleEntity roleEntity = new RoleEntity();
            roleEntity.setName("role001");
            roleEntity.setIsDel(1);
            roleEntity.setMerchantCode("1");
            roleMapper.insert(roleEntity);
            log.info("编程式事务: roleEntity->{}, {}", roleEntity);
            // log.info("{}",status.isRollbackOnly());
            // // status.setRollbackOnly(); // 1. 设置rollbackOnly为true, 会回滚事务
            // log.info("{}",status.isRollbackOnly());

            if (1 > 0) {
                // throw new RuntimeException(); // 2. 抛出Runtime异常, 会回滚事务
            }

            return roleEntity;
        }
    });

    log.info("执行结果: roleEntity->{}, {}", roleEntity, roleEntity.hashCode());

    return roleEntity;
}

以下参考:Spring 编程式事务实例

1、通过PlatformTransactionManager控制事务

package com.tx;
 
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
 
import java.util.List;
import java.util.Map;
 
public class TestTransaction {
 
    public static void main(String[] args) {
        //1、定义数据源
        org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/work_db?characterEncoding=UTF-8&serverTimezone=UTC");
        dataSource.setInitialSize(10);
        dataSource.setUsername("root");
        dataSource.setPassword("root");
 
        //2、定义JdbcTemplate,执行数据库操作语句
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
 
        //3、定义【事务管理器】,并指定数据源
        PlatformTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
        //4、定义【事务属性】,TransactionDefinition,它可用来配置事务的属性信息,比如事务隔离级别、事务超时时间、事务传播方式、是否是只读事务等
        TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
 
        //5.【开启事务】,返回事务状态(TransactionStatus)对象
        TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
        //6.执行业务操作
        try {
            List<Map<String, Object>> list = jdbcTemplate.queryForList("SELECT * from b_user");
            System.out.println("插入数据前:" + list);
 
            jdbcTemplate.update("insert into b_user (name) values (?)", "李清华最聪明2");
            jdbcTemplate.update("insert into b_user (name) values (?)", "张北大最智慧2");
 
            //制造异常,触发回滚
            int num = 1 / 0;
            //7.【提交事务】,注意:事务的提交放在try最后一行
            transactionManager.commit(transactionStatus);
        } catch (Exception e) {
            //8.【回滚事务】,捕获异常回滚事务
            transactionManager.rollback(transactionStatus);
        }
 
        List<Map<String, Object>> list = jdbcTemplate.queryForList("SELECT * from b_user");
        System.out.println("插入数据后:" + list);
 
    }
 
}

2、通过TransactionTemplate控制事务

package com.tx;
 
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.lang.Nullable;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
 
import java.util.function.Consumer;
 
public class TestTransaction2 {
 
    public static void main(String[] args) {
        //定义数据源
        org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/work_db?characterEncoding=UTF-8&serverTimezone=UTC");
        dataSource.setInitialSize(10);
        dataSource.setPassword("root");
        dataSource.setUsername("root");
 
        //定义JdbcTemplate
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
 
        //1.定义事务管理器
        PlatformTransactionManager platformTransactionManager = new DataSourceTransactionManager(dataSource);
        //2.定义事务属性
        DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
        transactionDefinition.setTimeout(10);//如:设置超时时间10s
        //3.创建TransactionTemplate对象
        TransactionTemplate transactionTemplate = new TransactionTemplate(platformTransactionManager, transactionDefinition);
 
        //无返回值的,需传递一个Consumer对象,在accept方法中做业务操作
        transactionTemplate.executeWithoutResult(new Consumer<TransactionStatus>() {
            @Override
            public void accept(TransactionStatus transactionStatus) {
                jdbcTemplate.update("insert into b_user (name) values (?)", "张三无返回");
 
            }
        });
 
        //有返回值的,需要传递一个TransactionCallback对象,在doInTransaction方法中做业务操作
        Integer result = transactionTemplate.execute(new TransactionCallback<Integer>() {
            @Nullable
            @Override
            public Integer doInTransaction(TransactionStatus status) {
                int result = jdbcTemplate.update("insert into b_user (name) values (?)", "张三有返回");
                return result;
            }
        });
 
        System.out.println("执行返回数据:" + result);
        System.out.println("最终查询结果:" + jdbcTemplate.queryForList("SELECT * from b_user"));
 
 
    }
 
}
 
 

2. 声明式事务控制

1、基于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:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.alibaba.com/schema/stat http://www.alibaba.com/schema/stat.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--开启Spring IoC组件注解包扫描,方便操作-->
    <context:component-scan base-package="com.spring.tx.xml"/>

    <!--druid数据源连接池-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url"
                  value="jdbc:mysql://47.94.229.245:3306/test?useSSL=false&amp;allowPublicKeyRetrieval=true&amp;serverTimezone=UTC"/>
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!--jdbcTemplate 使用Spring JDBC模板类来操作数据库-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--配置一个druidDataSource数据源-->
        <constructor-arg name="dataSource" ref="druidDataSource"/>
    </bean>

    <!--DataSourceTransactionManager事务管理器 用来管理数据库事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--配置一个druidDataSource数据源-->
        <constructor-arg name="dataSource" ref="druidDataSource"/>
    </bean>


    <!--配置一个事务的AOP通知,通过transaction-manager属性关联一个事务管理器
    默认查找id/name为transactionManager的事务管理器,因此如果事务管理器beanName就是transactionManager,那么该属性可以省略 -->
    <!--该标签可以配置多个,可以使用不同的事务管理器-->
    <!--此标签可以定义定义许多方法的事务语义(其中事务语义包括传播设置、隔离级别、回滚规则等)-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--一个标签集,内部可以配置多个<tx:method/>标签 -->
        <tx:attributes>
            <!--配置某些方法的事务属性,这些方法是从aop:pointcut切入点表达式匹配到的方法中进一步筛选出来的-->
            <!-- 该标签可配置的属性
            name: 表示匹配的方法名,可以使用通配符*
            read-only:是否是只读事务。默认false,不是。
            isolation:指定事务的隔离级别。默认值为DEFAULT,即对应ISOLATION_DEFAULT
            propagation:指定事务的传播行为。默认值为REQUIRED,即对应PROPAGATION_REQUIRED
            timeout:指定超时时间。默认值为:-1。永不超时。
            rollback-for:指定将触发回滚的一个或多个异常,使用","分隔。如不指定则使用默认策略。
            no-rollback-for:指定不会触发回滚的一个或多个异常,使用","分隔。如不指定则使用默认策略。
            -->
            <!--以"get"开始的所有方法都配置只读属性-->
            <tx:method name="get*" read-only="true" timeout="30"/>
            <!-- 其他剩余方法使用默认事务属性配置-->
            <tx:method name="*" timeout="30"/>
        </tx:attributes>
    </tx:advice>

    <!-- AOP配置,确保上述事务通知正常运行-->
    <aop:config>
        <!--配置aop切入点表达式   该表达式用于确定到底有哪些方法需要进行事务管理-->
        <!--这里,我们配置TxStudyServiceImpl的所有方法均需要被事务管理-->
        <aop:pointcut id="studyServicePt" expression="execution(* com.spring.tx.xml.TxStudyServiceImpl.*(..))"/>
        <!--配置aop通知器 使用advice-ref引用上面定义的事务通知(通过指向它的id) 通过pointcut-ref引入上面定义的aop切入点表达式(通过指向它的id)
        这样,通过pointcut-ref引入的切入点表达式匹配的方法就可以应用于通过advice-ref引入的事务通知机制以及各种属性
        -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="studyServicePt"/>
    </aop:config>
    
</beans>

甚至在声明式的事务管理配置中,也可以以编程方式指示所需的回滚。虽然简单,但此过程非常具有侵入性。

private void test() throws IOException {
    try {
        // 业务代码
    } catch (Exception ex) {
        // 捕获异常之后,以编程方式手动触发回滚
        // currentTransactionStatus方法:获取当前事务,如果开启了事务,那么会返回事务,如果没有事务,那么会抛出异常!
        // setRollbackOnly方法:用于设该事务的唯一结果就回滚,用来代替抛出异常
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

2、基于xml声明式注解事务控制

```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:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       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 https://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启Spring IoC组件注解包扫描,方便操作-->
    <context:component-scan base-package="com.spring.tx.ann"/>

    <!--druid数据源连接池-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url"
                  value="jdbc:mysql://47.94.229.245:3306/test?useSSL=false&amp;allowPublicKeyRetrieval=true&amp;serverTimezone=UTC"/>
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!--jdbcTemplate 使用Spring JDBC模板类来操作数据库-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--配置一个druidDataSource数据源-->
        <constructor-arg name="dataSource" ref="druidDataSource"/>
    </bean>

    <!--DataSourceTransactionManager事务管理器 用来管理数据库事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--配置一个druidDataSource数据源-->
        <constructor-arg name="dataSource" ref="druidDataSource"/>
        <qualifier value="11"/>
    </bean>

	<!-- 使用< tx:annotation-driven />标签开启事务注解的支持。此时我们不必再配置< tx:advice/>和< aop:config/>,可以直接在Spring管理的bean中添加事务注解
		1. transaction-manager属性,用于指定驱动事务的事务管理器的 beanName。此属性不是必需的,默认值为transactionManager,只有在所需的事务管理器的 beanName不是"transactionManager"时才需要显式指定。
		2. proxy-target-class属性,用于指定控制为使用@Transactional注解注解的类创建的事务代理类型。如果proxy-target-class属性设置为true,则创建基于类的代理。如果proxy-target-class为false或者省略了该属性,则会默认创建基于标准JDK接口的代理。
		3. order属性,使用@Transactional注解的bean,当多个通知在特定连接点执行时,控制事务通知器的执行顺序。
	-->
    <!--启用基于注解的事务行为配置,通过transaction-manager属性关联一个事务管理器 -->
    <!--默认值为transactionManager,因此如果事务管理器beanName就是transactionManager,那么该属性可以省略-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
</beans>

3、纯注解的声明式事务

/* 
 	@EnableTransactionManagement注解,用于替代< tx:annotation-driven/>标签来开启事务注解驱动
 */
@ComponentScan
@Configuration
@EnableTransactionManagement
public class PureAnnStart {
    /**
     * 配置Druid数据源
     */
    @Bean
    public DruidDataSource druidDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        //为了方便,直接硬编码了,我们可以通过@Value引入外部配置,
        //如果使用Spring boot就更简单了,直接使用@ConfigurationProperties引入外部配置
        //简单的配置数据库连接信息,其他连接池信息采用默认配置
        druidDataSource.setUrl("jdbc:mysql://47.94.229.245:3306/test?useSSL=false&allowPublicKeyRetrieval=true");
        druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("123456");
        return druidDataSource;
    }

    /**
     * 配置JdbcTemplate
     * 直接使用spring-jdbc来操作某一个数据库,不使用其他外部数据库框架
     */
    @Bean
    public JdbcTemplate jdbcTemplate() {
        //传入一个数据源
        return new JdbcTemplate(druidDataSource());
    }


    /**
     * 配置DataSourceTransactionManager
     * 用于管理某一个数据库的事务
     */
    @Bean
    public DataSourceTransactionManager transactionManager() {
        //传入一个数据源
        return new DataSourceTransactionManager(druidDataSource());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值