事务的概念
事务:是数据库操作最基本单位,逻辑上的一组操作,要么都成功,如果有一个失败所有操作都失败。
事务的四大特性:
- 原子性
- 一致性
- 隔离性
- 持久性
使用代码开启事务的过程(一共分为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进行事务的管理主要分为两种方式:编程式事务管理和声明式事务管理,一般常用的是声明式事务管理,下面将介绍声明式事务管理。
声明式事务管理有两种实现方式:
- 基于注解的方式
- 基于xml配置文件方式
这篇博客主要讲解三大部分:①基于xml配置文件和注解的混合方式,②基于完全xml配置文件的方式,③基于完全注解的方式
在Spring进行事务声明式事务管理,底层使用的AOP。
目录
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文件中完成一系列的配置
- 开启组件扫描
- 连接数据库连接池
- 声明JdbcTemplate对象
- 创建事务管理器,并注入数据源
- 开启事务的注解
<?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&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有点不同,其主要分为以下部分:
- 开启组件扫描
- 连接数据库连接池
- 注册JdbcTemplate对象
- 创建事务管理器并注入数据源
- 配置通知并配置事务参数(即为哪个方法添加事务)
- 配置切入点和切面(即把当前的事务通知添加到切点上)
<?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&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管理事务的方式,每种方式原理都是一样的,要么使用注解的方式,要么配置文件的方式。整体而言使用注解方式较简单,建议在实际的开发过程中使用注解的方式,简便快速。
学习之所以会想睡觉,是因为那是梦开始的地方。
ଘ(੭ˊᵕˋ)੭ (开心) ଘ(੭ˊᵕˋ)੭ (开心)ଘ(੭ˊᵕˋ)੭ (开心)ଘ(੭ˊᵕˋ)੭ (开心)ଘ(੭ˊᵕˋ)੭ (开心)
------不写代码不会凸的小刘