3种常见的事务回滚

4 篇文章 0 订阅
3 篇文章 0 订阅

目录

手动开启事务 -- connector

添加依赖

封装工具类 -- DBUtils

编写操作类 -- AccountDao

编写服务类 -- AccountService

编写配置类 -- db.properties

编写测试类

使用 transactionTemplate

编写操作类 -- AccountDao

编写服务类 -- AccountService

编写Spring配置类 -- spring-context.xml

编写测试类

使用注解 -- @transactional

编写操作类 -- AccountDao

编写服务类 -- AccountService

编写配置文件 -- JavaConfig

编写测试类


手动开启事务 -- connector

添加依赖

    <dependencies>
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.7</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.10</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.27</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

封装工具类 -- DBUtils

package com.qfedu.tx.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * @Author XY
 * @Date 2022/6/8 22:26
 */
public class DBUtils {
    private static final Properties PROPERTIES = new Properties();
    private static final ThreadLocal<Connection> THREAD_LOCAL = new ThreadLocal<>();
    private static DataSource ds;

    static {
        try {
            InputStream is = DBUtils.class.getResourceAsStream("/db.properties");
            PROPERTIES.load(is);
            ds = DruidDataSourceFactory.createDataSource(PROPERTIES);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static DataSource getDs() {
        return ds;
    }

    public static Connection getConnection() {
        Connection connection = THREAD_LOCAL.get();
        if (connection == null) {
            try {
                Connection con = getDs().getConnection();
                THREAD_LOCAL.set(con);
                return con;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return connection;
    }

    public static void close(ResultSet resultSet, Statement statement, Connection connection) {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

编写操作类 -- AccountDao

package com.qfedu.tx.dao;

import com.qfedu.tx.utils.DBUtils;
import org.apache.commons.dbutils.QueryRunner;

import java.sql.SQLException;

/**
 * @Author XY
 * @Date 2022/6/8 22:42
 */
public class AccountDao {
    QueryRunner queryRunner = new QueryRunner(DBUtils.getDs());

    public Integer minusMoney(String username, Integer money) throws SQLException {
        return queryRunner.update(DBUtils.getConnection(), "update account set balance = balance-? where username=?", money, username);
    }

    public Integer addMoney(String username, Integer money) throws SQLException {
        return queryRunner.update(DBUtils.getConnection(), "update account set balance = balance+? where username=?", money, username);
    }
}

编写服务类 -- AccountService

package com.qfedu.tx.service;

import com.qfedu.tx.dao.AccountDao;
import com.qfedu.tx.utils.DBUtils;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * @Author XY
 * @Date 2022/6/8 22:45
 */
public class AccountService {
    AccountDao accountDao = new AccountDao();

    public String transfer(String from, String to, Integer money) {
        Connection connection = DBUtils.getConnection();
        try {
            //关闭事务自动提交
            connection.setAutoCommit(false);
            Integer r1 = accountDao.minusMoney(from, money);
            Integer r2 = accountDao.addMoney(to, money);
            //提交事务
            connection.commit();
            if (r1 == 1 && r2 == 1) {
                return "转账成功";
            } else {
                return "转账失败";
            }
        } catch (SQLException e) {
            e.printStackTrace();
            try {
                //事务回滚
                connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }
        return "转账失败,事务已回滚";
    }
}

在转账的时候,也就是minusMoney()与addMoney()之间可以人为的添加一个异常(int i=1/0;) ,这样就可以在测试的时候,查看是否回滚。

编写配置类 -- db.properties

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test01
username=root
password=1234

在这个配置文件中可以修改相关配置,这样就可以实现跨平台 

编写测试类

public class AccountServiceTest {

    AccountService accountService = new AccountService();

    @Test
    public void transfer() {
        String transfer = accountService.transfer("zhangsan", "lisi", 50);
        System.out.println("transfer = " + transfer);
    }
}

使用 transactionTemplate

编写操作类 -- AccountDao

package com.qfedu.tx.dao;

import org.springframework.jdbc.core.JdbcTemplate;

/**
 * @Author XY
 * @Date 2022/6/8 23:09
 */
public class AccountDao {
    JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public Integer minusMoney(String username, Integer money) {
        return jdbcTemplate.update("update account set balance = balance-? where username=?;", money, username);
    }

    public Integer addMoney(String username, Integer money) {
        return jdbcTemplate.update("update account set balance = balance+? where username=?;", money, username);
    }
}

编写服务类 -- AccountService

package com.qfedu.tx.service;

import com.qfedu.tx.dao.AccountDao;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

/**
 * @Author XY
 * @Date 2022/6/8 23:11
 */
public class AccountService {

    AccountDao accountDao;
    TransactionTemplate transactionTemplate;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }

    public void transfer(String from, String to, Integer money) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                accountDao.minusMoney(from, money);
                accountDao.addMoney(to, money);
            }
        });
    }
}

在转账的时候,也就是minusMoney()与addMoney()之间可以人为的添加一个异常(int i=1/0;) ,这样就可以在测试的时候,查看是否回滚。 

编写Spring配置类 -- spring-context.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 class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
        <property name="url" value="jdbc:mysql:///test01?serverTimezone=Asia/Shanghai"/>
        <property name="username" value="root"/>
        <property name="password" value="1234"/>
    </bean>

    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <bean class="com.qfedu.tx.dao.AccountDao" id="accountDao">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>

    <bean class="com.qfedu.tx.service.AccountService" id="accountService">
        <property name="accountDao" ref="accountDao"/>
        <property name="transactionTemplate" ref="transactionTemplate"/>
    </bean>

    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <bean class="org.springframework.transaction.support.TransactionTemplate" id="transactionTemplate">
        <property name="transactionManager" ref="transactionManager"/>
    </bean>
</beans>

编写测试类

package com.qfedu.tx.service;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Author waowang
 * @Date 2022/6/8 23:14
 */
public class AccountServiceTest {

    private static ClassPathXmlApplicationContext ctx;

    @Test
    public void transfer() {
        AccountService accountService = ctx.getBean(AccountService.class);
        accountService.transfer("zhangsan", "lisi", 50);
    }

    @Before
    public void setUp() throws Exception {
        ctx = new ClassPathXmlApplicationContext("spring-context.xml");
    }

    @After
    public void tearDown() throws Exception {
        ctx.close();
    }
}

使用注解 -- @transactional

编写操作类 -- AccountDao

package com.qfedu.tx.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

/**
 * @Author XY
 * @Date 2022/6/8 23:09
 */
@Repository
public class AccountDao {
    @Autowired
    JdbcTemplate jdbcTemplate;


    public Integer minusMoney(String username, Integer money) {
        return jdbcTemplate.update("update account set balance = balance-? where username=?;", money, username);
    }

    public Integer addMoney(String username, Integer money) {
        return jdbcTemplate.update("update account set balance = balance+? where username=?;", money, username);
    }
}

编写服务类 -- AccountService

package com.qfedu.tx.service;

import com.qfedu.tx.dao.AccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Author XY
 * @Date 2022/6/9 15:20
 */
@Service
@EnableTransactionManagement
public class AccountService {
    @Autowired
    AccountDao accountDao;

    @Transactional
    public void transfer(String from, String to, Integer money) {
        accountDao.minusMoney(from, money);
        accountDao.addMoney(to, money);
    }
}

在转账的时候,也就是minusMoney()与addMoney()之间可以人为的添加一个异常(int i=1/0;) ,这样就可以在测试的时候,查看是否回滚。 

编写配置文件 -- JavaConfig

使用 transactionTemplate 的时候,我是用 xml 文件配置的,下面我用 Java 代码配置

package com.qfedu.tx.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/**
 * @Author XY
 * @Date 2022/6/9 15:39
 */
@Configuration
@ComponentScan("com.qfedu.tx")
public class JavaConfig {
    @Bean
    DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/test01?serverTimezone?Asia/Shanghai");
        dataSource.setUsername("root");
        dataSource.setPassword("1234");
        return dataSource;
    }

    @Bean
    DataSourceTransactionManager sourceTransactionManager() {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource());
        return transactionManager;
    }

    @Bean
    JdbcTemplate jdbcTemplate() {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource());
        return jdbcTemplate;
    }
}

编写测试类

package com.qfedu.tx.service;

import com.qfedu.tx.config.JavaConfig;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Author XY
 * @Date 2022/6/9 15:25
 */
public class AccountServiceTest {

    @Test
    public void test01() {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
        AccountService accountService = ctx.getBean(AccountService.class);
        accountService.transfer("zhangsan", "lisi", 50);
        ctx.close();
    }
}

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring事务回滚是在Spring框架中提供的一机制,用于在事务发生异常或错误时撤销已执行的操作,使数据回滚到事务开始之前的状态。在Spring中,有几方式可以实现事务回滚。 首先是编程式事务,这方式需要在代码中手动开启事务、手动提交和手动回滚。虽然可以灵活控制事务的执行,但代码会变得冗长和重复。 其次是声明式事务,通过配置SpringAop来实现事务的控制,大大简化了编码的复杂性。需要注意的是,切入点表达式必须正确配置。 还有注解事务,直接在Service层的方法上加上@Transactional注解即可实现事务控制。这方式相对简单,是我个人比较喜欢使用的方式。 通常情况下,事务回滚的原因是由于抛出了RuntimeException异常。在声明式事务和注解事务中,当被切面切中或者是加了注解的方法中抛出了RuntimeException异常时,Spring会进行事务回滚。但如果抛出的异常不属于运行时异常,比如IO异常,事务是不会回滚的。 常见的导致事务不回滚的原因有以下几: 1. 声明式事务配置切入点表达式写错,没有切中Service中的方法。 2. Service方法中捕获了异常,但只是打印了异常信息而未手动抛出RuntimeException异常。 3. Service方法中抛出的异常不属于运行时异常,因为Spring默认情况下只会回滚运行时异常。 以上是关于Spring事务回滚的一些介绍和常见原因。希望对您有所帮助。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Spring事务管理——回滚(rollback-for)控制](https://blog.csdn.net/ryelqy/article/details/80019106)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [spring的事务回滚机制你懂得多少?](https://blog.csdn.net/weixin_45985053/article/details/125958535)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值