前提知识
Spring-IOC容器注解方式使用https://blog.csdn.net/m0_61160520/article/details/136784799?spm=1001.2014.3001.5501
切点表达式https://blog.csdn.net/m0_61160520/article/details/136782885?spm=1001.2014.3001.5501
案例
1.创建项目
2.导入依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>6.0.6</version>
</dependency>
3.数据库准备
-- ----------------------------
-- Table structure for students
-- ----------------------------
DROP TABLE IF EXISTS `students`;
CREATE TABLE `students` (
`id` int NOT NULL,
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`gender` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`age` int NULL DEFAULT NULL,
`class` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of students
-- ----------------------------
INSERT INTO `students` VALUES (1, '喜羊羊', '男', 18, '高中一班');
INSERT INTO `students` VALUES (2, '美羊羊', '女', 19, '高中二班');
INSERT INTO `students` VALUES (3, '懒羊羊', '男', 18, '高中一班');
INSERT INTO `students` VALUES (4, '沸羊羊', '男', 18, '高中三班');
INSERT INTO `students` VALUES (5, '暖羊羊', '女', 19, '高中二班');
INSERT INTO `students` VALUES (6, '软绵绵', '男', 60, '高中一班');
INSERT INTO `students` VALUES (7, '灰太狼', '男', 30, '高中三班');
INSERT INTO `students` VALUES (8, '红太狼', '女', 30, '高中二班');
SET FOREIGN_KEY_CHECKS = 1;
4.外部配置文件jdbc.properties
cx.url=jdbc:mysql://localhost:3306/studb
cx.driver=com.mysql.cj.jdbc.Driver
cx.username=root
cx.password=123456
5.编写配置类
@Configuration
@ComponentScan("com.example")
@PropertySource("classpath:jdbc.properties")
//@EnableAspectJAutoProxy //开启aspectj注解的支持
@EnableTransactionManagement //开启事务注解的支持
public class JavaConfig {
/*从配置文件中读取数据库连接的相关信息。*/
@Value("${cx.driver}")
private String driver;
@Value("${cx.url}")
private String url;
@Value("${cx.username}")
private String username;
@Value("${cx.password}")
private String password;
//druid连接池,使用 Druid 连接池来管理数据库连接。
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
//创建一个 JdbcTemplate bean,将上面创建的数据源对象注入其中,用于执行数据库查询和更新操作。
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//创建一个用于管理事务的 TransactionManager bean,将数据源对象注入其中,以便在事务中控制数据库的操作。
@Bean
public TransactionManager transactionManager(DataSource dataSource){
//内部要进行事务的操作,基于的连接池
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
//需要连接池对象
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
6.简单准备一个dao/service层
dao
@Repository
public class StudentDao {
//声明一个 JdbcTemplate 对象作为成员变量来执行数据库操作。
private JdbcTemplate jdbcTemplate;
@Autowired
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void updateNameById(String name, Integer id){
String sql = "update students set name = ? where id = ? ;";
int rows = jdbcTemplate.update(sql, name, id);
}
public void updateAgeById(Integer age,Integer id){
String sql = "update students set age = ? where id = ? ;";
jdbcTemplate.update(sql,age,id);
}
}
service(重要内容)
添加事务:
@Transactional
位置: 方法 | 类上
方法: 当前方法有事务
类上: 类下的所有方法都有事务
1.只读模式
只读模式可以提升查询事务的效率! 推荐事务中只有查询代码,使用只读模式!
默认: boolean readOnly() default false;
解释: 一般情况下,都是通过类添加注解添加事务!
类下的所有方法都有事务!
查询方法可以通过再次添加注解,设置为只读模式,提高效率!
2.超时时间
默认: 永远不超时 -1
设置 timeout = 时间 秒数 超过时间,就会回滚事务和释放异常! TransactionTimedOutException
如果类上设置事务属性,方法也设置了事务注解! 方法会不会生效??
不会生效: 方法上的注解覆盖了类上的注解!
3.指定异常回滚和指定异常不回滚:
默认情况下,指定发生运行时异常事务才会回滚!
我们可以指定Exception异常来控制所有异常都回滚!
rollbackFor = Exception.class
noRollbackFor = 回滚异常范围内,控制某个异常不回滚!
4.隔离级别设置
推荐设置第二个隔离级别!
isolation = Isolation.READ_COMMITTED
@Transactional(timeout = 3)//超时时间
@Service
public class StudentService {
private StudentDao studentDao;
@Autowired
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
@Transactional(readOnly = false ,rollbackFor = Exception.class , noRollbackFor = FileNotFoundException.class
,isolation = Isolation.READ_COMMITTED)
public void changeInfo() throws FileNotFoundException {
studentDao.updateAgeById(99, 1);
new FileInputStream("xxxx");
studentDao.updateNameById("test3", 1);
}
@Transactional(readOnly = true)
public void changeInfo2() {
//查询 没有必要添加事务!
//获取学生信息 查询数据库 不修改
}
/**
* 声明两个独立修改数据库的事务业务方法
* propagation = Propagation.REQUIRED 父方法有事务,我们就加入到父方法的事务!
* 最终是同一个事务! 推荐使用默认值!!
*
* propagation = Propagation.REQUIRES_NEW
* 不管父方法是否有事务,我都是独立的事务!
* 两个事务或者三个事务!
*/
@Transactional(propagation = Propagation.REQUIRED)
public void changeAge(){
studentDao.updateAgeById(8,1);
}
@Transactional(propagation = Propagation.REQUIRED)
public void changeName(){
studentDao.updateNameById("二狗子",1);
int i = 1/0; //报错
}
}
封装类TopService
@Service
public class TopService {
@Autowired
private StudentService studentService;
@Transactional
public void topService(){
studentService.changeAge();
studentService.changeName();
}
}
为什么要这个封装类呢?移步以下博客
主要涉及了事务的控制与管理 https://blog.csdn.net/m0_61160520/article/details/136966627?spm=1001.2014.3001.5501
7.测试
@SpringJUnitConfig(JavaConfig.class)
public class TxTest {
@Autowired
private TopService topService;
@Autowired
private StudentService studentService;
@Test
public void testTx() throws FileNotFoundException {
topService.topService();
}
}
结果:数据库中age被修改,因为使用的Propagation.REQUIRES_NEW,表示每个方法都会创建一个新的事务,独立于外部事务,即使其他方法报错,正常方法会被执行。