Spring 事务

目录

没有事务的转账场景

Spring基于XML的事务

Spring 基于注解的事务

Spring事务传播行为require


没有事务的转账场景

resources下db.properties spring_config.xml

db.properties

#我用的是mysql的8.0.16版本,对于8系列版本有2项注意事项
# 1.jdbcUrl后面这个serverTimezone=UTC必须加
# 2. driverClass(cj必须加):com.mysql.cj.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/mydb?serverTimezone=UTC
driverClass=com.mysql.cj.jdbc.Driver
user=root
password=root

spring-config.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"
       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">
    <context:component-scan base-package="com.spring.ljj.transaction"></context:component-scan>
    <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="${jdbcUrl}"></property>
        <property name="driverClass" value="${driverClass}"></property>
        <property name="user" value="${user}"></property>
        <property name="password" value="${password}"></property>
    </bean>
    <!-- 就加一个jdbcTemplate用的时候直接注入就行-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>

UserDao.java

@Repository
public class UserDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public int transfer(String username, BigDecimal amount) {
        int result = this.jdbcTemplate.update("update amount set amount=amount+? where username=?", amount, username);
        return result;
    }
}

UserService.java

@Service
public class UserService {
    @Autowired
    private UserDao userDao;

    public void transfer() {
        //张三向李四转账1000
        int result = this.userDao.transfer("张三", BigDecimal.valueOf(-1000));
        System.out.println("result = " + result);
        int amount = this.userDao.transfer("李四", BigDecimal.valueOf(1000));
        System.out.println("amount = " + amount);
    }
}

测试类:

public class TestTransaction {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring_config4.xml");
        UserService userService = (UserService) context.getBean("userService");
        userService.transfer();
    }
}

运行结果:

初始金额都是10000,一次转账后结果如下:

现在在UserServie里抛异常。

UserService.java

 public void transfer() {
        //张三向李四转账1000
        int result = this.userDao.transfer("张三", BigDecimal.valueOf(-1000));
        System.out.println("result = " + result);
        System.out.println(1/0);//抛出异常
        int amount = this.userDao.transfer("李四", BigDecimal.valueOf(1000));
        System.out.println("amount = " + amount);
    }

再次运行测试类进行transfer转账,运行结果如下:

数据库(张三少了1000,但是李四并没有增加1000):

所以要使用事务,将transfer方法交由事务管理,就不会出现这种情况了。

Spring基于XML的事务

spring_config.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"
       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.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.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">
    <context:component-scan base-package="com.spring.ljj.transaction"></context:component-scan>
    <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="${jdbcUrl}"></property>
        <property name="driverClass" value="${driverClass}"></property>
        <property name="user" value="${user}"></property>
        <property name="password" value="${password}"></property>
    </bean>
    <!-- 就加一个jdbcTemplate用的时候直接注入就行-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--事务管理器(注入数据源)-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--配置事务增强器(在哪些方法上启动事务(并没有说明具体哪些包哪些类下,(默认实现了advice接口)))-->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="transfer" propagation="REQUIRED" rollback-for="Exception"/>
        </tx:attributes>
    </tx:advice>
    <!--配置事务所切的范围-->
    <aop:config>
        <aop:pointcut id="myPointCut" expression="execution(* com.spring.ljj.transaction.*.*(..))"/>
        <!--  <aop:advisor>和 <aop:aspect>区别
                1.<aop:advisor advice-ref配置增强器,此增强器已经不用再手动规定前置和后置方法了,
                因为必须是实现了advice接口的类,已经自动重写了相应的方法
                2.<aop:aspect是引用切面类,并不是完全配置好的,
                需要自己配置哪些方法是前置,哪些是后置-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointCut"/>
    </aop:config>

</beans>

再次运行测试类:

数据库(无变化):

实现了我们的目标,报异常,整个事务回滚

Spring 基于注解的事务

spring_config.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"
       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.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.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">
    <context:component-scan base-package="com.spring.ljj.transaction"></context:component-scan>
    <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="${jdbcUrl}"></property>
        <property name="driverClass" value="${driverClass}"></property>
        <property name="user" value="${user}"></property>
        <property name="password" value="${password}"></property>
    </bean>
    <!-- 就加一个jdbcTemplate用的时候直接注入就行-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--事务管理器(注入数据源)-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
 <!--开启事务注解驱动-->
    <tx:annotation-driven transaction-manager="txManager"/>
</beans>

UserServie.java变为:

@Service
public class UserService {
    @Autowired
    private UserDao userDao;

    @Transactional(rollbackFor = Exception.class)
    public void transfer() {
        //张三向李四转账1000
        int result = this.userDao.transfer("张三", BigDecimal.valueOf(-1000));
        System.out.println("result = " + result);
        System.out.println(1 / 0);//抛出异常
        int amount = this.userDao.transfer("李四", BigDecimal.valueOf(1000));
        System.out.println("amount = " + amount);
    }
}

运行转账测试类,运行结果和我们期望的一致,事务实现了回滚。

Spring事务传播行为Require

就是指定Spring中一个事务方法调用另一个事务方法时。处理的行为

今天研究一种最常见的也是默认的事务传播行为require:

Require:支持当前事务,如果没有事务,就建一个新的,这是最常见的;

UserService.java

@Service
public class UserService {
    @Autowired
    private UserDao userDao;

    @Transactional(rollbackFor = Exception.class)
    public void transfer1() {
        int result = this.userDao.transfer("张三", BigDecimal.valueOf(1000));
    }

    @Transactional(rollbackFor = Exception.class)
    public void transfer2() {
        int result = this.userDao.transfer("李四", BigDecimal.valueOf(1000));
    }

    @Transactional(rollbackFor = Exception.class)
    public void transfer3() throws Exception {
        int result = this.userDao.transfer("李四", BigDecimal.valueOf(1000));
        throw new RuntimeException("抛出了异常");
    }

    @Transactional(rollbackFor = Exception.class)
    public void transfer4() {
        try {
            int result = this.userDao.transfer("李四", BigDecimal.valueOf(1000));
            System.out.println(1 / 0);
        } catch (RuntimeException e) {
            e.printStackTrace();
        }
    }
}

CallUserService1.java

@Service
public class CallUserService {
    @Autowired
    private UserService userService;

    //初始都是10000
    public void test1() {
// test1方法没有事务,所以this.userService.transfer1()开启自己独立的事务
        this.userService.transfer1();//11000
        this.userService.transfer2();//11000
        throw new RuntimeException("发生异常");
    }

    public void test2() throws Exception {
        this.userService.transfer1();//11000
        this.userService.transfer3();//10000 因为transfer3方法抛出了异常
    }

    public void test3() throws Exception {
        this.userService.transfer1();//11000
        this.userService.transfer4();//11000 因为transfer4有异常但是并没有抛出去,而是在方法内部处理掉了,所以不回滚
    }
}

CallUserService2.java

@Service
@Transactional
public class CallUserService2 {
    @Autowired
    private UserService userService;

    //初始都是10000
    public void test1() {
        // test1方法有事务,所以this.userService.transfer1()和transfer2()都加入此事务,是同一个事务
        //以下3句是同一个事物,任何一个地方抛出异常,都会回滚
        this.userService.transfer1();//10000
        this.userService.transfer2();//10000
        throw new RuntimeException("发生异常");
    }

    public void test2() throws Exception {
        //以下俩句是 同一个事务,任何一个地方抛出异常,都会回滚
        this.userService.transfer1();//10000
        this.userService.transfer3();//10000 因为transfer3方法抛出了异常
    }

    public void test3() throws Exception {
        //以下俩句是 同一个事务,任何一个地方抛出异常,都会回滚
        this.userService.transfer1();//11000
        this.userService.transfer4();//11000 因为transfer4有异常但是并没有抛出去,而是在方法内部处理掉了,所以不回滚
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值