Spring中的事务管理

事务的概念

事务:是数据库操作最基本单位,逻辑上的一组操作,要么都成功,如果有一个失败所有操作都失败。

事务的四大特性:

  1. 原子性
  2. 一致性
  3. 隔离性
  4. 持久性

使用代码开启事务的过程(一共分为4步):

public void accountMoney(){
        try {
            //1.手动开启事务
            //2.编写业务逻辑
//            userDao.addMoney();
//            userDao.reduceMoney();
            //3.没有发生异常,提交事务
        } catch (Exception e) {
            //4.发生异常,事务回滚
            e.printStackTrace();
        }
}

        在JavaEE的开发中主要包含三层结构:controller层(web层)、service层(业务层)、dao层,一般事务添加到业务层(service层)上

        在spring进行事务的管理主要分为两种方式:编程式事务管理和声明式事务管理,一般常用的是声明式事务管理,下面将介绍声明式事务管理。

声明式事务管理有两种实现方式:

  1. 基于注解的方式
  2. 基于xml配置文件方式

        这篇博客主要讲解三大部分:①基于xml配置文件和注解的混合方式,②基于完全xml配置文件的方式,③基于完全注解的方式

        在Spring进行事务声明式事务管理,底层使用的AOP。

目录

1、基于xml配置文件和注解的混合方式

1.1、Spring事务管理API

1.2、在pom文件中引入相关依赖

1.3、在xml文件中完成一系列的配置

1.4、在service类上面添加事务注解

1.5、编写测试类进行测试

1.6、得到运行结果

2、基于完全xml配置文件的方式

2.1、引入相关的依赖

2.2、在xml文件中进行配置

2.3、编写业务逻辑代码

2.4、测试代码的编写

2.5、得到运行结果

3、基于完全注解的方式

3.1、在pom文件中添加相关依赖

3.2、编写配置类TestConfig

3.3、编写业务逻辑代码

3.4、编写测试类

3.5、得到测试运行结果

4、总结


1、基于xml配置文件和注解的混合方式

1.1、Spring事务管理API

        提供接口PlatformTransactionManager ,接口 PlatformTransactionManage 代表事务管理器,这个接口针对不同的框架提供不同的实现类 (接口 PlatformTransactionManager),其中在spring中以及Mybatis中使用的都是 实现类DataSourceTransactionManager,而Hibernate中使用的是实现类HibernateTransactionManager

1.2、在pom文件中引入相关依赖

        在依赖引入的时候,要注意每个以来版本之间是否会有冲突,即有没有兼容的问题,不兼容的话,会爆上一篇博客的错误。地址:Error creating bean with name ‘userService‘ defined in file [D:\ajavaproject\coding\atguiguLearnSpri_网恋褙骗八万的博客-CSDN博客

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <packaging>war</packaging>

    <parent>
        <artifactId>atguiguLearnSpring5</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>Spring5_demo6</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <!--添加这个以后就是用jdk1.8编译,以致于不会出现@Override报错的情况-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>


</project>

1.3、在xml文件中完成一系列的配置

  1. 开启组件扫描
  2. 连接数据库连接池
  3. 声明JdbcTemplate对象
  4. 创建事务管理器,并注入数据源
  5. 开启事务的注解
<?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/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">

    <!--  开启组件扫描  -->
    <context:component-scan base-package="com.atguigu.spring5"></context:component-scan>

    <!--连接数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <property name="url" value="jdbc:mysql:///user_db?useUnicode=true&amp;characterEncoding=utf8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    </bean>

    <!--  JdbcTemplate对象  -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--   注入dataSource     -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--  创建事务管理器  -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--   注入数据源     -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--  开启事务注解  -->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>


</beans>

1.4、在service类上面添加事务注解

        @Transactional注解可以加到类上面,也可以把注解添加都方法上面。

        加到类上面说明这个类的所有方法都添加事务,加到方法上面说明为当前的方法添加事务。

        service类代码(为了验证事务注解有用性,在执行过程中放置一个除数为0的操作,会抛出异常)

package com.atguigu.spring5.service;

import com.atguigu.spring5.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


/**
 * @Author 不要有情绪的  ljy
 * @Date 2022/9/22 13:51
 * @Description:
 */
@Service
@Transactional
public class UserService {


    @Autowired
    private UserDao userDao;

    public void accountMoney() {

//        try {
//            //1.开启事务
//            //2.编写业务逻辑
        userDao.addMoney();
        int i = 10 / 0;
        userDao.reduceMoney();
//            //3.没有发生异常,提交事务
//        } catch (Exception e) {
//            //4.发生异常,事务回滚
//            e.printStackTrace();
//        }
    }
}

        dao层代码

package com.atguigu.spring5.dao;

/**
 * @Author 不要有情绪的  ljy
 * @Date 2022/9/22 13:50
 * @Description:
 */
public interface UserDao {

    //增加钱的功能
    public void addMoney();

    //减少钱的功能
    public void reduceMoney();

}

        dao的实现类daoImpl代码

package com.atguigu.spring5.dao;

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

/**
 * @Author 不要有情绪的  ljy
 * @Date 2022/9/22 13:50
 * @Description:
 */

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

    @Override
    public void addMoney() {
        String sql = "update t_account set money = money + ? where id = ?";
        Object[] args = new Object[]{100,1};
        int update = jdbcTemplate.update(sql, args);
        System.out.println("钱增加成功!" + update);
    }

    @Override
    public void reduceMoney() {
        String sql = "update t_account set money = money - ? where id = ?";
        Object[] args = new Object[]{100, 2};
        int update = jdbcTemplate.update(sql, args);
        System.out.println("钱减少成功!" + update);
    }
}

1.5、编写测试类进行测试

package com.atguigu.spring5.test;

import com.atguigu.spring5.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Author 不要有情绪的  ljy
 * @Date 2022/9/23 9:29
 * @Description:
 */
public class TestAccount {

    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.accountMoney();
    }

}

1.6、得到运行结果

        由于触发了异常导致,整个过程没有全部执行完,因此事务发生了回滚,数据库中的数据没有发生改变。

2、基于完全xml配置文件的方式

2.1、引入相关的依赖

        依赖和1.2中的依赖一致,不再写出

2.2、在xml文件中进行配置

        配置文件相较于1.3有点不同,其主要分为以下部分:

  1. 开启组件扫描
  2. 连接数据库连接池
  3. 注册JdbcTemplate对象
  4. 创建事务管理器并注入数据源
  5. 配置通知并配置事务参数(即为哪个方法添加事务)
  6. 配置切入点和切面(即把当前的事务通知添加到切点上)
<?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:aop="http://www.springframework.org/schema/aop"
       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
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--  开启组件扫描  -->
    <context:component-scan base-package="com.atguigu.spring5"></context:component-scan>

    <!--连接数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <property name="url" value="jdbc:mysql:///user_db?useUnicode=true&amp;characterEncoding=utf8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    </bean>

    <!--  JdbcTemplate对象  -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--   注入dataSource     -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--  创建事务管理器  -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--   注入数据源     -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 配置通知   -->
    <tx:advice id="txadvice">
        <!--   配置事务参数     -->
        <tx:attributes>
            <!--指明是哪个函数添加事务-->
            <tx:method name="accountMoney" propagation="REQUIRED"/>
            <!--指明为accoun开头的函数添加事务-->
            <!--<tx:method name="account*"/>-->
        </tx:attributes>
    </tx:advice>

    <!-- 配置切入点 和 切面  -->
    <aop:config>
        <!--  配置切入点  -->
        <aop:pointcut id="pt" expression="execution(* com.atguigu.spring5.service.UserService.*(..))"/>
        <!--  配置切面      -->
        <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
    </aop:config>


</beans>

2.3、编写业务逻辑代码

        逻辑代码和1.4章节差不多,只是要删除@Transactional注解

package com.atguigu.spring5.service;

import com.atguigu.spring5.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


/**
 * @Author 不要有情绪的  ljy
 * @Date 2022/9/22 13:51
 * @Description:
 */
@Service
//@Transactional
public class UserService {
    @Autowired
    private UserDao userDao;

    public void accountMoney() {

//        try {
//            //1.开启事务
//            //2.编写业务逻辑
        userDao.addMoney();
        int i = 10 / 0;
        userDao.reduceMoney();
//            //3.没有发生异常,提交事务
//        } catch (Exception e) {
//            //4.发生异常,事务回滚
//            e.printStackTrace();
//        }
    }
}

2.4、测试代码的编写

 @Test
    public void test2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.accountMoney();
    }

2.5、得到运行结果

        与1.6章节一致

3、基于完全注解的方式

3.1、在pom文件中添加相关依赖

        这儿不再叙说,与前面的章节一致

3.2、编写配置类TestConfig

        此类的编写是为了代替xml配置文件,并且在该类上添加@Configuration注解,即告诉Spring容器该类是配置类。

package com.atguigu.spring5.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 org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * @Author 不要有情绪的  ljy
 * @Date 2022/9/29 21:53
 * @Description:
 */
@Configuration   //配置类
@ComponentScan(basePackages = {"com.atguigu.spring5"})   //开启注解包扫描
@EnableTransactionManagement   //开启事务
public class TestConfig {
    //创建数据库连接池
    @Bean
    public DruidDataSource getDruidDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql:///user_db?useUnicode=true&characterEncoding=utf8");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("123456");
        return druidDataSource;
    }

    //创建JdbcTemplate对象
    @Bean
    public JdbcTemplate getJdbcTemplate(DruidDataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    //创建事务管理器
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DruidDataSource dataSource) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }
}

3.3、编写业务逻辑代码

        与前面的一致,不再描述,但是记得添加上@Transactional注解

3.4、编写测试类

        由于当前的配置类代替了xml配置为文件,因此测试方法在加载时候有一些变化,此时加载的应该为配置类。

@Test
    public void test3(){
        ApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class);
        UserService userService = context.getBean("userService", UserService.class);
        userService.accountMoney();
    }

3.5、得到测试运行结果

        测试得到的结果与前面一致。

4、总结

        本博客主要讲解了三种spring管理事务的方式,每种方式原理都是一样的,要么使用注解的方式,要么配置文件的方式。整体而言使用注解方式较简单,建议在实际的开发过程中使用注解的方式,简便快速。

学习之所以会想睡觉,是因为那是梦开始的地方。
ଘ(੭ˊᵕˋ)੭ (开心) ଘ(੭ˊᵕˋ)੭ (开心)ଘ(੭ˊᵕˋ)੭ (开心)ଘ(੭ˊᵕˋ)੭ (开心)ଘ(੭ˊᵕˋ)੭ (开心)
                                                                                                        ------不写代码不会凸的小刘

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值