Spring事务

一、Spring事务的四大特性

  1. 原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不做,就像你吃一个完整的苹果,不能只吃一半就扔掉。

  2. 一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态。比如,你从一个账户转账到另一个账户,两个账户的总金额必须保持不变。

  3. 隔离性(Isolation):并发执行的事务之间不会互相干扰,就像你在图书馆看书,不会影响到旁边的人。Spring提供了多种隔离级别,让你根据需要选择。

  4. 持久性(Durability):一旦事务提交,它对数据库的修改就是永久性的,即使系统发生故障也不会丢失。就像你在纸上写的字,不会因为你合上本子就消失。

二、spring事务管理

Spring 并不直接管理事务,⽽是提供了多种事务管理器,他们将事务管理的职责委托给 Hibernate 或 者 JTA 等持久化机制所提供的相关平台框架的事务来实现。

在Spring中,事务管理主要有两种方式:编程式事务管理声明式事务管理。

  • 编程式事务管理:通过编程的方式管理事务,这种方式比较灵活,但代码侵入性强,需要在代码中显式地调用事务管理的方法。

  • 声明式事务管理:通过配置的方式管理事务,这种方式代码侵入性低,易于维护。Spring推荐使用声明式事务管理,因为它更符合Spring的“约定优于配置”的理念。

三、三大事务接口

(一)、PlatformTransactionManager 

PlatformTransaction是spring事务处理的一个核心,这个接口中定义了操作事务的方法,如下:

(二)、TransactionDefinition

字面意思即事务的定义,主要是用来描述事务的规则,也成为属性,

 

(三)、TransactionStatus 

事务的状态,查看当前事务状态

四、PlatformTransactionManager实现编程式事务

(一)、引入相关依赖

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.1.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>6.1.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.26</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

(二)、编写配置文件

<?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
 https://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.wedu"/>
<!--    引入连接数据库的文件-->
    <context:property-placeholder location="jdbc.properties"></context:property-placeholder>
<!--    配置数据源直接使用spring提供的-->
    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
<!--  提供事务管理器-->
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <property name="dataSource" ref="datasource"></property>
    </bean>
<!--  通过模板类操作操作事务  -->
    <bean class="org.springframework.transaction.support.TransactionTemplate" id="transactionTemplate">
        <property name="transactionManager" ref="transactionManager"></property>
    </bean>
<!--    通过JdbcTemplate操作数据库-->
    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
        <property name="dataSource" ref="datasource"></property>
    </bean>
</beans>

在数据库创建·counts表,并且添加数据 

 1、通过TransactionManager实现
@Service
public class CountsService {
    @Autowired
    private PlatformTransactionManager platformTransactionManager;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    //模拟转账操作
    public void transfer(){
       //开启一个事务,这里采用默认事务规则
        final TransactionStatus status = platformTransactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            jdbcTemplate.update("update counts set money=? where id=?",66,2);
            platformTransactionManager.commit(status);
        } catch (DataAccessException e) {
            platformTransactionManager.rollback(status);
            throw new RuntimeException(e);
        }

    }

}

 

public class CountsTest {
    public static void main(String[] args) {
        final ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        final CountsService countService = (CountsService) context.getBean("countsService");
        countService.transfer();
    }
}
2、通过TransactionTemplate实现
@Service
public class CountsService {
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    //模拟转账操作
    public void transfer(){
//        //开启一个事务
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    jdbcTemplate.update("update counts set money=? where id=?",99,1);
                } catch (Exception e) {
                    System.out.println("转账失败");
                    //进行事务回滚
                    status.setRollbackOnly();
                }
            }
        });
    }

}

测试

public class CountsTest {
    public static void main(String[] args) {
        final ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        final CountsService countService = (CountsService) context.getBean("countsService");
        countService.transfer();
    }
}

五、声明式事务

(一)、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
 https://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.wedu"/>
<!--    引入连接数据库的文件-->
    <context:property-placeholder location="jdbc.properties"></context:property-placeholder>
<!--    配置数据源直接使用spring提供的-->
    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
        <property name="dataSource" ref="datasource"></property>
    </bean>
    <!--
         xml配置事务分为三个步骤
         1、配置事务管理器
         2、配置事务通知
         3、配置aoop
    -->
    <!--配置事务管理器-->
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <property name="dataSource" ref="datasource"></property>
    </bean>
    <!-- 配置事务通知-->
    <tx:advice id="txadvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="transfer*"/>
        </tx:attributes>
    </tx:advice>
    <!--配置aop-->
    <aop:config>
        <aop:pointcut id="pointCut" expression="execution(* com.wedu.service.*.*(..))"/>
        <aop:advisor advice-ref="txadvice" pointcut-ref="pointCut"></aop:advisor>
    </aop:config>

</beans>

引入依赖

测试

@Service
public class CountsService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    //模拟转账操作
    public void transfer() {
        jdbcTemplate.update("update counts set money=? where id=?", 88, 2);

    }
}

 

public class CountsTest {
    public static void main(String[] args) {
        final ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        final CountsService countService = (CountsService) context.getBean("countsService");
        countService.transfer();
    }
}

 (二)、Java配置声明式事务

编写配置类

@Configuration
//配置包扫描
@ComponentScan(basePackages = "com.wedu")
//开启事务的注解支持
@EnableTransactionManagement
public class JavaConfig {
    //创建数据源
    @Bean
    DataSource dataSource(){
        DriverManagerDataSource dataSource=new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/wedustudy?serverTimezone=GMT%2B8&" +
                "useUnicode=true&useSSL=false&characterEncoding=utf8");
        dataSource.setUrl("root");
        dataSource.setPassword("root");
        return dataSource;
    }
    //创建JdbcTemplate
    @Bean
    JdbcTemplate jdbcTemplate(){
        return new JdbcTemplate(dataSource());
    }
    //创建事务管理器
    PlatformTransactionManager platformTransactionManager(){
        return new DataSourceTransactionManager(dataSource());
    }

}

测试

@Service
public class CountsService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    //模拟转账操作
    //开启事务
    @Transactional
    public void transfer()  {
        jdbcTemplate.update("update counts set money=? where id=?", 9, 2);
        final int i = 1 / 0;
    }
}
public class CountsTest2 {
    public static void main(String[] args) {
        final ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
        final CountsService countService = (CountsService) context.getBean("countsService");
            countService.transfer();
    }
}

 (三)、xml和java混合配置

在xml配置文件中加入事务注解支持,然后在Java代码方法或者类上添加@transactional注解即可

   <tx:annotation-driven/>

六、事务的传播行为

1、required

如果当前存在事务,则加入当前事务,如果当前不存在事务,则新建一个事务。

2、requires_new 创建一个新的事务,如果当前存在事务,则把当前事务挂起

3、nested 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行

4、mandatory如果存在事务则加入当前事务,如果不存在则抛出异常

5、supports 如果当前存在事务则加入当前事务,不存在则非事务方式运行

6、not_supported 以非事务运行,如果当前存在事务则挂起当前事务

7、never:以非事务的方式运行,有事务则抛出异常

七、声明式事务和编程是事务区别

编程式事务就像是那些喜欢自己掌控一切的运动员,他们喜欢亲自下场,精细地控制每一个动作。在编程中,这意味着开发者需要手动编写代码来管理事务的边界,包括何时开始事务、何时提交事务以及何时回滚事务。这种方式给了开发者很高的灵活性和控制力,但同时也增加了代码的复杂度和出错的可能性。

声明式事务则更像是那些擅长制定策略、让团队去执行的教练。在Spring等框架中,开发者不需要在代码中显式地管理事务,而是通过在配置文件中声明事务规则,或者通过注解等方式来指定哪些方法需要事务支持。框架会自动为这些方法创建事务,并在方法执行完毕后根据执行结果提交或回滚事务。这种方式简化了代码,降低了出错的可能性,同时也使得业务逻辑更加清晰。

简单来说,编程式事务和声明式事务的主要区别在于:编程式事务需要开发者手动控制事务的边界,而声明式事务则通过配置或注解的方式自动管理事务。两者各有优劣,选择哪种方式取决于具体的应用场景和团队的技术栈。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值