Spring-声明式事务实例(有详细注释)

前提知识

Spring-IOC容器注解方式使用icon-default.png?t=N7T8https://blog.csdn.net/m0_61160520/article/details/136784799?spm=1001.2014.3001.5501
切点表达式icon-default.png?t=N7T8https://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();
    }
}

为什么要这个封装类呢?移步以下博客

主要涉及了事务的控制与管理 icon-default.png?t=N7T8https://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,表示每个方法都会创建一个新的事务,独立于外部事务,即使其他方法报错,正常方法会被执行。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值