前提知识
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 连接池来管理数据库连接。@Beanpublic DataSource dataSource(){DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}//创建一个 JdbcTemplate bean,将上面创建的数据源对象注入其中,用于执行数据库查询和更新操作。@Beanpublic JdbcTemplate jdbcTemplate(DataSource dataSource){JdbcTemplate jdbcTemplate = new JdbcTemplate();jdbcTemplate.setDataSource(dataSource);return jdbcTemplate;}//创建一个用于管理事务的 TransactionManager bean,将数据源对象注入其中,以便在事务中控制数据库的操作。@Beanpublic 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;@Autowiredpublic 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;@Autowiredpublic 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 {@Autowiredprivate StudentService studentService;@Transactionalpublic 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 {@Autowiredprivate TopService topService;@Autowiredprivate StudentService studentService;@Testpublic void testTx() throws FileNotFoundException {topService.topService();}
}
结果:数据库中age被修改,因为使用的Propagation.REQUIRES_NEW,表示每个方法都会创建一个新的事务,独立于外部事务,即使其他方法报错,正常方法会被执行。