Day70.JDBCTemplate、声明式事务基于注解、隔离级别、传播行为、回滚类型、Spring5新特性

目录

一、JDBCTemplate (了解)

二、声明式事务概念

三、基于注解的声明式事物

一、准备工作

二、应用最基本的事务控制  @Transactional  ★★★

三、事务属性:只读  readOnly = true  ★

四、超时  timeout = ?

五、回滚异常类型  rollbackFor = Exception.class  ★

六、事物隔离级别  ★

七、事物的传播行为

八、基于XML的声明式事务

四、Spring5 新特性 (了解)

1. JSR305标准相关注解

2. JSR 305

3. 相关注解

4. 整合junit5

五、Spring总结


@Transactional:对当前类所有方法、或当前方法应用事物

配置事物管理注解驱动:

    
    
        
    
    
    
    //注意:要选tx命名空间的

要使用的事物管理器:DataSourceTransactionManager

一、JDBCTemplate (了解)

为了在特定领域帮助我们简化代码,Spring 封装了很多 『Template』形式的模板类。例如:RedisTemplate、RestTemplate 等等,包括我们今天要学习的 JDBCTemplate。

事物离不开对数据库的访问,访问数据库可以使用MyBatis和JDBC。

Spring提供了整合JDBC的JDBCTemplate,因为先有Spring,再出现MyBatis,所以整合包不是由spring提供的,而是第三方提供的

JDBCTemplate和MyBatis共同点:都是对JDBC进行了封装。不同点:MyBatis封装的更彻底,提供了更多的功能

1.添加依赖


    org.springframework
    spring-orm
    5.3.1

Spring 在执行持久化层操作、与持久化层技术进行整合过程中,需要使用orm、jdbc、tx三个jar包;导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入

Day70.JDBCTemplate、声明式事务基于注解、隔离级别、传播行为、回滚类型、Spring5新特性_第1张图片

 2.进行配置



    
!




    
    
    
    


jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis-example?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
jdbc.user=root
jdbc.password=*******

3. 测试+具体操作

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring.xml")
public class TestJDBCTemplate {

    @Autowired
    DataSource dataSource;
    //新内容 jdbc模板
    @Autowired
    JdbcTemplate jdbcTemplate;

    @Test
    public void testJDBCTemplate() throws SQLException {
        Connection conn = dataSource.getConnection();
        System.out.println(conn);
        System.out.println(jdbcTemplate);
    }
    @Test
    public void testInsert(){
        this.jdbcTemplate.update("insert into t_emp values(null,?,?)","张三",123.4);
    }
    @Test
    public void testUpDate(){
        this.jdbcTemplate.update("update t_emp set emp_salary = ? where emp_id =?",543.1,32);

    }
    @Test
    public void testDelete(){
        int n = this.jdbcTemplate.update("delete from t_emp where emp_id >= ?",27);
        System.out.println(n);
    }
    @Test
    public void testCount(){
        Integer count = this.jdbcTemplate.queryForObject("select count(*) from t_emp", Integer.class);
        System.out.println(count);
    }
    @Test
    public void testFindById(){
        //自动按照驼峰命名来映射 PropertyRowMapper
        RowMapper rowMapper = new BeanPropertyRowMapper<>(Employee.class);
        Employee emp = this.jdbcTemplate.queryForObject("select * from t_emp where emp_id =?",rowMapper,2);
        System.out.println(emp);
    }
    @Test
    public void testFindAll(){
        RowMapper rowMapper = new BeanPropertyRowMapper<>(Employee.class);
        List list = this.jdbcTemplate.query("select * from t_emp", rowMapper);
        list.forEach(emp-> System.out.println(emp));
    }
}

二、声明式事务概念

  • 编程式:自己写代码实现功能
  • 声明式:通过配置让框架实现功能
  • 要使用的事物管理器:DataSourceTransactionManager

1、编程式事务:事务的相关操作全部通过编写代码来实现

缺点1:暴露了底层细节,没有进行封装,比较繁琐

缺点2:代码复用性不高:需要事务的位置都要写相同的代码,代码冗余,不利于后期维护

try {
    // 开启事务:关闭事务的自动提交
    conn.setAutoCommit(false);
    // 核心操作
    // 提交事务
    conn.commit();
}catch(Exception e){
    // 回滚事务
    conn.rollBack();
}finally{
    // 释放数据库连接
    conn.close();
}

2、声明式事务:不编码,只是声明 (xml声明或者注解声明)

既然事务控制的代码有规律可循,代码的结构基本是确定的,所以框架就可以将固定模式的代码抽取出来,进行相关的封装。

优点1:提高开发效率。

优点2:消除了冗余的代码,方便管理。

优点3:框架会综合考虑相关领域中在实际开发环境下有可能遇到的各种问题,进行了健壮性、性能等各个方面的优化。

Day70.JDBCTemplate、声明式事务基于注解、隔离级别、传播行为、回滚类型、Spring5新特性_第2张图片

使用JDBCTemplate 要用的事务管理器是:org.springframework.jdbc.datasource.DataSourceTransactionManager,将来整合 Mybatis 用的也是这个类。

Day70.JDBCTemplate、声明式事务基于注解、隔离级别、传播行为、回滚类型、Spring5新特性_第3张图片

上级接口:PlatformTransactionManager

Day70.JDBCTemplate、声明式事务基于注解、隔离级别、传播行为、回滚类型、Spring5新特性_第4张图片

 最上级:TransactionManager 空接口

三、基于注解的声明式事物

一、准备工作

1. 添加依赖

和JdbcTemplate相同的依赖:必须有spring-orm。

    
        
        
            org.springframework
            spring-context
            5.3.1
        
        
        
        
        
            org.springframework
            spring-orm
            5.3.1
        
        
        
            org.springframework
            spring-test
            5.3.1
        
        
        
            junit
            junit
            4.12
            test
        
        
        
            mysql
            mysql-connector-java
            5.1.3
        
        
        
            com.alibaba
            druid
            1.0.31
        
    

2. 配置文件

和JdbcTemplate相同的配置

    
    
    
    
    
    
        
        
        
        
    
    
    
        
    

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis-example?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
jdbc.user=root
jdbc.password=*******

3. DAO层代码

EmpDao

@Repository    //声明当前类为Dao层
public class EmpDao {
    @Autowired
    private JdbcTemplate jdbcTemplate; 
    public void updateEmpNameById(Integer empId, String empName) {
        String sql = "update t_emp set emp_name=? where emp_id=?";
        jdbcTemplate.update(sql, empName, empId);
    }
    public void updateEmpSalaryById(Integer empId, Double salary) {
        String sql = "update t_emp set emp_salary=? where emp_id=?";
        jdbcTemplate.update(sql, salary, empId);
    }  
    public String selectEmpNameById(Integer empId) {
        String sql = "select emp_name from t_emp where emp_id=?";
    
        String empName = jdbcTemplate.queryForObject(sql, String.class, empId);
    
        return empName;
    }
}

4. 业务代码

EmpService

@Service
public class EmpService {
    @Autowired
    private EmpDao empDao;
    // 为了便于核对数据库操作结果,不要修改同一条记录
    public void updateTwice(
            // 修改员工姓名的一组参数
            Integer empId4EditName, String newName,
            // 修改员工工资的一组参数
            Integer empId4EditSalary, Double newSalary
            ) {
        // 为了测试事务是否生效,执行两个数据库操作,看它们是否会在某一个失败时一起回滚
        empDao.updateEmpNameById(empId4EditName, newName);
        empDao.updateEmpSalaryById(empId4EditSalary, newSalary);
    }
}

5. 测试代码

 Day70.JDBCTemplate、声明式事务基于注解、隔离级别、传播行为、回滚类型、Spring5新特性_第5张图片

public class TestTransaction {
    @Autowired
    private EmpService empService;
    @Test
    public void testUpdate(){
        //全部成功
        //empService.updateTwice(1,"tomcat",2,600.0);
        //失败
        empService.updateTwice(1,"tom",2,6003453453.0);
    }
}
出现了什么问题:部分修改成功,部分修改失败。这不允许出现

二、应用最基本的事务控制  @Transactional  ★★★

1. 配置事务管理器



    
    

2. 开启基于注解的声明式事务功能  transaction-manager

    
    
        
    
    
    

注意:要选tx命名空间的,不然会出错
Day70.JDBCTemplate、声明式事务基于注解、隔离级别、传播行为、回滚类型、Spring5新特性_第6张图片

3. 在需要事务的方法上使用注解(事务加载业务层) @Transactional

@Service
@Transactional //当前类所有方法都应用事物
public class EmpService { 
    @Autowired
    private EmpDao empDao;
    //修改第一个员工的姓名,修改第二个员工的工资
    // 为了便于核对数据库操作结果,不要修改同一条记录
    @Transactional //当前方法应用事物
    public void updateTwice(
            // 修改员工姓名的一组参数
            Integer empId1, String newName,

            // 修改员工工资的一组参数
            Integer empId2, Double newSalary
            ) {
    
        // 为了测试事务是否生效,执行两个数据库操作,看它们是否会在某一个失败时一起回滚
        empDao.updateEmpNameById(empId1, newName);
        empDao.updateEmpSalaryById(empId2, newSalary);
    }
}

4. 添加依赖、启用logback日志

成功提交事务Day70.JDBCTemplate、声明式事务基于注解、隔离级别、传播行为、回滚类型、Spring5新特性_第7张图片失败回滚事务Day70.JDBCTemplate、声明式事务基于注解、隔离级别、传播行为、回滚类型、Spring5新特性_第8张图片

三、事务属性:只读  readOnly = true  ★

对一个查询操作来说,如果我们把它设置成只读,就能够明确告诉数据库,这个操作不涉及写操作。这样数据库就能够针对查询操作来进行优化。

Service
    //readOnly = true  只读事物 只有查询可以设为只读,可以提高查询效率
    @Transactional(readOnly = true)
    public String getEmpName(Integer empId){
        String empName = this.empDao.getEmpName(empId);
        return empName;
    }
Dao层
    public String getEmpName(Integer empId) {
        String name = jdbcTemplate.queryForObject("select emp_name from t_emp where emp_id = ?", String.class, empId);
        return name;
    }

如果不是查询:Queries leading to data modification are not allowed

问题:查询不使用事务, 还是使用只读事务??

应用场合:

如果执行单条sql查询语句则没有必要启用事务,数据库默认支持SQL执行期间的读一致性;

如果需要执行多条sql查询语句来合成最终的统计结果则有必要启用事物可以利用事务隔离级别保证数据查询的一致性
例如统计查询,报表查询,在这种场景下,多条查询SQL必须保证整体的读一致性
,否则,在前条SQL查询之后,后条SQL查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持。

四、超时  timeout = ?

    @Transactional(//timeout = 5 操作不能超过五秒)
    public void updateTwice(
            // 修改员工姓名的一组参数
            Integer empId1, String newName,

            // 修改员工工资的一组参数
            Integer empId2, Double newSalary
            ) {
    
        // 为了测试事务是否生效,执行两个数据库操作,看它们是否会在某一个失败时一起回滚
        empDao.updateEmpNameById(empId1, newName);
        try {
            Thread.sleep(6000); //等待6秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        empDao.updateEmpSalaryById(empId2, newSalary);
    }

如果超时:爆出异常TransactionTimedOutException: Transaction timed out

注意Thread.sleep()的位置,如果此时Sql语句已经全部执行完毕,事务还是会提交。

五、回滚异常类型  rollbackFor = Exception.class  ★

默认只针对运行时异常回滚编译时异常不回滚

出现了检查异常,如果catch,继续执行,异常被无视,不会回滚;如果异常抛出,也会提交事务,造成数据不一致。

修改方案一:rollbackFor = Exception.class

所有异常都回滚,除了SQLException
@Transactional(rollbackFor=Exception.class,noRollbackFor = SQLException.class)
public void updateTwice(){

}
所有的异常都不回滚,除了SQLException
@Transactional(rollbackFor=SQLException.class,noRollbackFor = Exception.class) 
public void updateTwice(){

}

修改方案二:异常链。底层出现了检查异常,进行try catch处理,并向上层抛出一个新的异常一般是运行异常)。既可以传递异常到上层,还可以避免方法签名中使用throws。在Spring事务中还可以实现所有异常都回滚的效果。

@Transactional //当前方法应用事务
public void updateTwice(
        Integer empId1, String newName,
        Integer empId2, Double newSalary
        ){

    empDao.updateEmpNameById(empId1, newName);

    try {
        InputStream is = new FileInputStream("d:/abc/cdf/ghi.txt");//出现了检查异常
    } catch (FileNotFoundException e) {
        e.printStackTrace();
        //抛出一个运行时异常
        throw new RuntimeException(e.getMessage());
    }

    empDao.updateEmpSalaryById(empId2, newSalary);
}

常见的编译时异常:

SQLException、.IOexception、FileNotFoundException、ClassNotFoundException...

常见的运行时异常:

ArrayIndexOutOfBoundsExceptio、NullPointerException、IllegaArguementException

六、事物隔离级别  

  • 脏读一个事务读取了另一个事务未提交数据;

  • 不可重复读同一个事务中前后两次读取同一条记录不一样。因为被其他事务修改了并且提交了。(同一事务中查询的数据应保持一致性)

  • 幻读一个事务读取了另一个事务新增、删除的记录情况,记录数不一样,像是出现幻觉。

  • 不可重复读的重点是修改,幻读重点是新增或删除

隔离级别 脏读 不可重复读 幻读
READ_UNCOMMITTED (读未提交)
READ_COMMITTED (读提交) ×
REPEATABLE_READ (可重复读 锁当条记录) × ×
SERIALIZABLE (序列化) × × ×

Spring的隔离级别

public enum Isolation {
    DEFAULT(-1),
    READ_UNCOMMITTED(1),
    READ_COMMITTED(2),
    REPEATABLE_READ(4),
    SERIALIZABLE(8);
}

如果选择DEFAULT,默认值,由底层数据库自动判断应该使用什么隔离级别。

对于互联网高并发项目来说,如采用隔离级别SERIALIZABLE,固然安全,担心性能会受到严重影响。此时一般将隔离级别降低,保证效率,再配合悲观锁、乐观锁等技术保证安全性。

事务的隔离级别要得到底层数据库引擎的支持, 而不是应用程序或者框架的支持。Oracle 支持的 2 种事务隔离级别:READ_COMMITED(默认) , SERIALIZABLE。MySQL 支持 4种事务隔离级别,默认REPEATABLE READ。

注意:mysql中使用了MVCC多版本控制技术,在REPEATABLE READ(可重复读)这个级别也可以避免幻读,解决了效率与安全性间取舍的问题

七、事物的传播行为

事务传播行为(propagation behavior)
事务传播行为是指,多个拥有事务的方法在嵌套调用时的事务控制方式

例如:methodA方法调用methodB方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。

@Transactional 注解通过 propagation 属性设置事务的传播行为

名称 含义
REQUIRED 如果当前没有事务,就新建一个事务如果已经存在一个事务中,加入到这个事务中。这是默认值
REQUIRES_NEW 新建事务如果当前存在事务,把当前事务挂起
SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
NESTED 如当前存在事务,则在嵌套事务内执行。如当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作

问题1:事物层在业务层,事物也是应用于业务层,业务层怎么会调用业务层?

① service 应用了通知 

②过滤器或拦截器等类似组件Day70.JDBCTemplate、声明式事务基于注解、隔离级别、传播行为、回滚类型、Spring5新特性_第9张图片

 问题2:一个事物包含多个DML操作,要么都成功,要么什么都不做。可以改变吗?

1.如果都成功,或者都失败 REQUIRED

2.如果希望empservice修改员工成功了,但是logservice添加日志失败了,不希望日志失败影响员工修改, REQUIRES_NEW

八、基于XML的声明式事务

1. 加入依赖:

相比于基于注解的声明式事务,基于 XML 的声明式事务需要一个额外的依赖:

       
            org.springframework
            spring-aspects
            5.3.1
        

2、迁移代码

将上一个基于注解的 module 中的代码转移到新module。去掉 @Transactional 注解。

3、修改 Spring 配置文件

去掉 tx:annotation-driven 标签,然后加入下面的配置:


    
    
    
    
    
    

    




    
    
        
        
        
        
        
    
        
        
        
        
        
        
        
        
        
    

四、Spring5 新特性 (了解)

1. JSR305标准相关注解

①JCP

JCP(Java Community Process) 是一个由SUN公司发起的,开放的国际组织。主要由Java开发者以及被授权者组成,负责Java技术规范维护,Java技术发展和更新

JCP官网地址:The Java Community Process(SM) Program

 ②JSR

JSR 的全称是:Java Specification Request,意思是 Java 规范提案。谁向谁提案呢?任何人都可以向 JCP 提出新增一个标准化技术规范的正式请求。JSR已成为Java界的一个重要标准。登录 JCP 官网可以查看所有 JSR 标准。

2. JSR 305

JSR 305: Annotations for Software Defect Detection

This JSR will work to develop standard annotations (such as @NonNull) that can be applied to Java programs to assist tools that detect software defects.

主要功能:使用注解(例如@NonNull等等)协助开发者侦测软件缺陷。

Spring 从 5.0 版本开始支持了 JSR 305 规范中涉及到的相关注解

package org.springframework.lang;
​
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
​
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierNickname;
​
/**
 * A common Spring annotation to declare that annotated elements cannot be {@code null}.
 *
 * 

Leverages JSR-305 meta-annotations to indicate nullability in Java to common * tools with JSR-305 support and used by Kotlin to infer nullability of Spring API. * *

Should be used at parameter, return value, and field level. Method overrides should * repeat parent {@code @NonNull} annotations unless they behave differently. * *

Use {@code @NonNullApi} (scope = parameters + return values) and/or {@code @NonNullFields} * (scope = fields) to set the default behavior to non-nullable in order to avoid annotating * your whole codebase with {@code @NonNull}. * * @author Sebastien Deleuze * @author Juergen Hoeller * @since 5.0 * @see NonNullApi * @see NonNullFields * @see Nullable */ @Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Nonnull @TypeQualifierNickname public @interface NonNull { }

3. 相关注解

注解名称 含义 可标记位置
@Nullable 可以为空 @Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
@NonNull 不应为空 @Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
@NonNullFields 在特定包下的字段不应为空 @Target(ElementType.PACKAGE)
@TypeQualifierDefault(ElementType.FIELD)
@NonNullApi 参数和方法返回值不应为空 @Target(ElementType.PACKAGE)
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})

4. 整合junit5

1.添加依赖


    org.junit.jupiter
    junit-jupiter-api
    5.7.0
    test

3.使用复合注解 进行测试  @SpringJUnitConfig

@SpringJUnitConfig(locations = "classpath:spring.xml")
//@ExtendWith(SpringExtension.class)
//@ContextConfiguration(locations = "classpath:spring.xml")
public class TestTransaction2 {
    @Autowired
    private EmpService empService;
    @Test
    public void testUpdate() {
        //全部成功
        empService.updateTwice(1,"tomcat",2,600.0);
        //失败
        //empService.updateTwice(1,"tom",2,50012423423423.0);
    }
    @Test
    public void testGetEname(){
        String empName = empService.getEmpName(1);
        System.out.println(empName);
    }
}

3.区别

特征

JUNIT 4

JUNIT 5

声明一种测试方法

@Test

@Test

在当前类中的所有测试方法之前执行

@BeforeClass

@BeforeAll

在当前类中的所有测试方法之后执行

@AfterClass

@AfterAll

在每个测试方法之前执行

@Before

@BeforeEach

每种测试方法后执行

@After

@AfterEach

禁用测试方法/类

@Ignore

@Disabled

测试工厂进行动态测试

NA

@TestFactory

嵌套测试

NA

@Nested

标记和过滤

@Category

@Tag

注册自定义扩展

NA

@ExtendWith

五、Spring总结

Day70.JDBCTemplate、声明式事务基于注解、隔离级别、传播行为、回滚类型、Spring5新特性_第10张图片

  1. 简介
    1. Spring是一家公司,更是一门技术。现在属于Pivotal公司
    2. Spring技术发展的四个阶段: spring / springboot /springcloud/spring dataflow
    3. Java世界最成功的框架
    4. 2004 年 03 月,Spring 1.0 版发布。2017 年09 月,Spring 5.0 发布,基于JDK8。
    5. Spring的成功来自理念,而不是技术。最核心的理念是IoC(控制反转)和AOP(面向切面编程)。基础是IoC,AOP的最典型应用当属数据库事务的处理。
    6. Spring Framework五大功能模块: test/ ioc /aop/ 整合DAO  /整合web
  2. IoC 控制翻转
    1. 概念
      • IoC:控制翻转,将创建对象由代码转移到外部xml文件中;用户不new对象,而是从IoC容器中获取已经创建的对象,直接使用即可。
      • IoC容器:管理Bean的容器。Spring认为一切类都是Bean,比如实体类、DAO类、业务层、控制类、通知类等。
      • Spring Bean的生命周期(面试题)
      • IoC容器 API :ApplicationContext  ClassPathXmlApplicationContext
    2. 基于XML的IoC
      • 创建Bean:普通Bean  工厂Bean  集合Bean
      • 获取Bean:根据id获取;根据类名获取(只有一个);根据接口获取(实现类的Bean只有一个)
      • 注入属性1:通过setter方法注入;通过构造方法注入
      • 注入属性2:注入简单属性、注入引用属性、注入集合属性
      • 注入属性3:手动注入;自动装配autowire
      • Bean的作用域:singleton 只有一个 /prototype 每次获取一个新的Bean
    3. 基于注解的IoC
      • 创建Bean:@Component @Controller @Service @Repository
      • 注入属性:@Value @Autowired  @Qualifier
      • 注意自动装配的步骤  byType  byName @Qualifier 异常
      • 整合JUnit4  整合JUnit5
  3. AOP 面向切面编程
    1. 概念和原理
      • 解决什么问题:横切性关注点 作用类似过滤器
      • 底层原理:动态代理(JDK动态代理+CGLib的动态代理)
      • 常用术语:切面aspect  通知(增强 干什么)advice  切入点(表达式 哪里干)pointcut  连接点 joinpoint  目标target  代理proxy
    2. 基于注解实现AOP
      • @Aspect 这是一个切面类
      • @Before @After @AfterReturning @AfterThrowing @Around 通知
      • @PonitCut(“execution (* com.atguigu.service.*.*(..))”)
      • @Order
      • proxy-target-class="false"/>
    3. 基于XML实现AOP(了解)
  4. 声明式事务
    1. 概念
      • JDBCTemplate(了解)
      • 理解声明式事务(替代了编程式事务)
      • API:TransactionManager PlatformTransactionManager DataSourceTransactionManager
    2. 基于注解实现声明式事务
      •  
      • @Transactional
      • @Transactional的属性
        1. 传播特性 propagation  根据业务需求选择合适的传播特性
        2. 隔离级别 isolation  MySQL提供了四种隔离记录,Oracle只有两种隔离级别
        3. 超时时间  timeout  -1 代表永不超时
        4. 指定事务 readonly 查询可以只读,提高效率
        5. 回滚异常类型  rollBackFor  noRollBackFor 默认运行时异常自动回滚
    3. 基于XML实现声明式事务(了解)

你可能感兴趣的:(SSM,ssm,spring,transaction,aop)