目录
三、例2:在例1的基础上添加事务@Transactional
什么是事务,简单来说,就是要几个sql绑在一起,要么所有执行成功,要么所有执行失败,不能出现只执行部分的情况。
经典的就是银行转帐,A向B转帐,最少需要执行2步,2个sql
步骤1:查询A余额是否足够,然后在A-转账金额
步骤2:B账号添加A转账数
你不能只执行一个吧?!这是绝对不能出现只执行成功一个的,如果只有一部分执行成功,那么就要全部回滚,并提示失败。
一、知识点
1.1 声明式事务操作
1.给方法上方 @Transactional 表示当前方法是一个事务方法
2.@EnableLoadTimeWeaving 开启基于注解的事务管理功能
@EnableXXX 不开启的话,事务是不会被执行的
3.配置事务管理器来控制事务
@Bean
public PlatformTransactionManager transactionManager() throws Exception{}
1.2 相关依赖
c3p0、mchange-commons-java、mysql-connector-java、spring-jdbc
pom.xml要有如下配置:我目前的spring版本为5.2.3,涉及有spring的版本要一致,其它的版本可以去maven官网仓库查
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mchange/mchange-commons-java -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>mchange-commons-java</artifactId>
<version>0.2.20</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
c3p0:主要是读取外部配置文件,一般数据库配置文件放在外部,我这里使用yml方式
mchange-commons-java:c3p0数据库连接池的辅助包
mysql-connector-java:mysql的jdbc驱动包,是java连接mysql数据库使用的,我这里只是配置文件,并没用到mysql数据库
spring-jdbc:spring操作jdbc操作数据库用的,如mysql数据库
二、例1:普通例子
2.1 建立数据库和表
我在hua数据库如下:
表如下:
插入数据
INSERT INTO tb1_user (username,age) VALUES ('admin',18)
2.2 目录结构
2.3 实现代码
com.hualinux.tx.UserDao.java
package com.hualinux.tx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.UUID;
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void insert(){
String sql = "INSERT INTO tb1_user (username,age) VALUES (?,?)";
String username = UUID.randomUUID().toString().substring(0,5);
jdbcTemplate.update(sql,username,19);
}
}
com.hualinux.tx.UserService.java
package com.hualinux.tx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void insertUser(){
userDao.insert();
System.out.println("插入完成...");
}
}
resources-->db.yml
我是直接使用《hualinux spring 4.16:@Profile 切换环境》中的数据库配置文件db.yml,只是修改了用户名和密码
user: hua
pwd: hua123
driverClass: com.mysql.cj.jdbc.Driver
jdbcUrlTest: jdbc:mysql://127.0.0.1:3306/hua_test?serverTimezone=GMT%2B8
jdbcUrlDev: jdbc:mysql://127.0.0.1:3306/hua_dev?serverTimezone=GMT%2B8"
jdbcUrlProd: jdbc:mysql://127.0.0.1:3306/hua?serverTimezone=GMT%2B8
注:下面值不能加双引号或引号,如果加了会连引号一起获取的
#下面写法是错误的 driverClass: "com.mysql.cj.jdbc.Driver" jdbcUrlTest: "jdbc:mysql://127.0.0.1:3306/hua_test?serverTimezone=GMT%2B8" jdbcUrlDev: "jdbc:mysql://127.0.0.1:3306/hua_dev?serverTimezone=GMT%2B8" jdbcUrlProd: "jdbc:mysql://127.0.0.1:3306/hua?serverTimezone=GMT%2B8"
com.hualinux.tx.TxConf.java配置如下
package com.hualinux.tx;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
/*
* 声明式事务:
*
* 环境搭建:
* 1.导入相关依赖:数据源、数据库驱动c3p0、Springjdbc模拟
* 2.配置数据源:JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据
* */
@PropertySource(value={"classpath:/db.yml"})
@ComponentScan("com.hualinux.tx")
@Configuration
public class TxConf {
@Value("${user}")
private String user;
@Value("${pwd}")
private String pwd;
@Value("${driverClass}")
private String driverClass;
@Value("${jdbcUrlTest}")
private String jdbcUrlTest;
@Value("${jdbcUrlDev}")
private String jdbcUrlDev;
@Value("${jdbcUrlProd}")
private String jdbcUrlProd;
//数据源
@Bean
public DataSource dataSource() throws Exception{
//主要是测试一下是否读到db.yml文件数据,及是否填写正确
System.out.println(user+"\n"+pwd+"\n"+jdbcUrlProd+"\n"+driverClass);
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setDriverClass(driverClass);
//测试环境使用的数据库
dataSource.setJdbcUrl(jdbcUrlProd);
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate() throws Exception{
//Spring对@Configuration类会特殊处理;给容器中加组件的方法,多次调用都只是从容器找组件
//下面dataSource(),是找组件并不是运行一次方法,因为这个类是配置类
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
return jdbcTemplate;
}
}
src-->test-->java-->IOCTest_Tx.java代码
import com.hualinux.tx.TxConf;
import com.hualinux.tx.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
class IOCTest_Tx {
AnnotationConfigApplicationContext ctx;
@Test
public void test01() {
ctx = new AnnotationConfigApplicationContext(TxConf.class);
UserService userService = ctx.getBean(UserService.class);
userService.insertUser();
ctx.close();
}
}
2.4 运行结果
运行上面的IOCTest_Tx.java中的测试方法test01,如果数据库打开能连接用户名和密码没问题,最后会提示
查看数据库变化
三、例2:在例1的基础上添加事务@Transactional
3.1 模式事务
com.hualinux.tx.UserService.java类在最后添加一条语句模拟事务,如下:
package com.hualinux.tx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Transactional
public void insertUser(){
userDao.insert();
System.out.println("插入完成...");
//之前的是没有事务的,模拟事务添加一条,要求不成功就回滚
int i=10/0;
}
}
再运行IOCTest_TxTest.java中的test01方法,效果如下
抛异常了,查看一下表没有第3条数据插入,发现还是插入了
说明事务没生效啊!!因为默认事务是关闭的,没有开启,需要添加一个开启的注解。
@EnableLoadTimeWeaving 开启基于注解的事务管理功能
@EnableXXX 不开启的话,事务是不会被执行的
3.2 开启事务
所以事务配置类得开启一下,com.hualinux.tx.TxConf.java代码,在类顶上添加多一个注解
@EnableLoadTimeWeaving
整个代码如下:
package com.hualinux.tx;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
/*
* 声明式事务:
*
* 环境搭建:
* 1.导入相关依赖:数据源、数据库驱动c3p0、Springjdbc模拟
* 2.配置数据源:JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据
* 3.给方法上歀 @Transactional 表示当前方法是一个事务方法
* 4.@EnableLoadTimeWeaving 开启基于注解的事务管理功能
* @EnableXXX 不开启的话,事务是不会被执行的
* */
@EnableLoadTimeWeaving
@PropertySource(value={"classpath:/db.yml"})
@ComponentScan("com.hualinux.tx")
@Configuration
public class TxConf {
@Value("${user}")
private String user;
@Value("${pwd}")
private String pwd;
@Value("${driverClass}")
private String driverClass;
@Value("${jdbcUrlTest}")
private String jdbcUrlTest;
@Value("${jdbcUrlDev}")
private String jdbcUrlDev;
@Value("${jdbcUrlProd}")
private String jdbcUrlProd;
//数据源
@Bean
public DataSource dataSource() throws Exception{
//主要是测试一下是否读到db.yml文件数据,及是否填写正确
System.out.println(user+"\n"+pwd+"\n"+jdbcUrlProd+"\n"+driverClass);
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setDriverClass(driverClass);
//测试环境使用的数据库
dataSource.setJdbcUrl(jdbcUrlProd);
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate() throws Exception{
//Spring对@Configuration类会特殊处理;给容器中加组件的方法,多次调用都只是从容器找组件
//下面dataSource(),是找组件并不是运行一次方法,因为这个类是配置类
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
return jdbcTemplate;
}
}
再运行IOCTest_TxTest.java中的test01方法,发现报如下错误:
Error creating bean with name 'loadTimeWeaver' defined in org.springframework.context.annotation.LoadTimeWeavingConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.instrument.classloading.LoadTimeWeaver]: Factory method 'loadTimeWeaver' threw exception; nested exception is java.lang.IllegalStateException: ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader] does NOT provide an 'addTransformer(ClassFileTransformer)' method. Specify a custom LoadTimeWeaver or start your Java virtual machine with Spring's agent: -javaagent:spring-instrument-{version}.jar
再去看一下数据库是否有变化,发现不有变化,所以完成了。
3.3 配置事务管理器来控制事务
com.hualinux.tx.TxConf.java代码,添加多一段代码
//注册事务管理器在容器中
@Bean
public PlatformTransactionManager transactionManager() throws Exception{
return new DataSourceTransactionManager(dataSource());
}
整个代码如下:
package com.hualinux.tx;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
/*
* 声明式事务:
*
* 环境搭建:
* 1.导入相关依赖:数据源、数据库驱动c3p0、Springjdbc模拟
* 2.配置数据源:JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据
* 3.给方法上歀 @Transactional 表示当前方法是一个事务方法
* 4.@EnableLoadTimeWeaving 开启基于注解的事务管理功能
* @EnableXXX 不开启的话,事务是不会被执行的
* */
@EnableLoadTimeWeaving
@PropertySource(value={"classpath:/db.yml"})
@ComponentScan("com.hualinux.tx")
@Configuration
public class TxConf {
@Value("${user}")
private String user;
@Value("${pwd}")
private String pwd;
@Value("${driverClass}")
private String driverClass;
@Value("${jdbcUrlTest}")
private String jdbcUrlTest;
@Value("${jdbcUrlDev}")
private String jdbcUrlDev;
@Value("${jdbcUrlProd}")
private String jdbcUrlProd;
//数据源
@Bean
public DataSource dataSource() throws Exception{
//主要是测试一下是否读到db.yml文件数据,及是否填写正确
System.out.println(user+"\n"+pwd+"\n"+jdbcUrlProd+"\n"+driverClass);
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(pwd);
dataSource.setDriverClass(driverClass);
//测试环境使用的数据库
dataSource.setJdbcUrl(jdbcUrlProd);
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate() throws Exception{
//Spring对@Configuration类会特殊处理;给容器中加组件的方法,多次调用都只是从容器找组件
//下面dataSource(),是找组件并不是运行一次方法,因为这个类是配置类
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
return jdbcTemplate;
}
//注册事务管理器在容器中
@Bean
public PlatformTransactionManager transactionManager() throws Exception{
return new DataSourceTransactionManager(dataSource());
}
}