Spring

Spring 简介

Spring 核心内容

  • Spring 是由 Rod Johnson 创建的一个开源容器框架,目的是为了解决企业开发的复杂性问题
  • 优点:
    • 是一个轻量级、非侵入式的框架
    • 支持 IOC 和 AOP
    • 支持事务处理、支持对框架的整合
  • 缺点:配置过于繁琐

Spring 组件说明

  • Spring Core:核心模块,利用 IOC 容器来管理类的依赖关系
  • Spring AOP:面向切面,提供对面向切面的支持,采用纯 Java 实现,AOP 代理的生成、管理、依赖关系也一并交由 IOC 容器管理
  • Spring ORM:对象关系映射,提供了与第三方持久层框架的良好整合
  • Spring DAO:持久层模块,对 JDBC 的抽象,简化了 DAO 的开发,以统一的方式使用数据库访问技术
  • Spring Context:应用上下文,它是一个配置文件,提供框架式的 Bean 访问方式,及企业级功能 (JNDI、定时任务、国际化等)
  • Spring Web:Web 模块,提供了针对 Web 开发的集成特性
  • Spring MVC:MVC 模块,提供面向 Web 应用的 Model - View - Controller 实现

Spring 使用步骤

  1. 搭建 Maven 工程

  2. 引入 依赖

    spring-webmvc
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.3.19</version>
    </dependency>
    
  3. 编写 applicationContext.xml 放在 resource 目录下

    applicationContext.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:p="http://www.springframework.org/schema/p"
           xmlns:c="http://www.springframework.org/schema/c"
           xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- p 标签使用 -->
        <bean id="addressTwo" class="com.spring.entity.Address" p:address="蜀国*荆州"/>
    
        <!-- c 标签使用 -->
        <bean id="user" class="com.spring.entity.UserThree" c:name="朱六" c:age="20"/>
    
        <!-- 定义引用类 -->
        <bean id="address" class="com.spring.entity.Address">
            <property name="address" value="蜀国*汉中"/>
        </bean>
    
        <!-- 复杂对象装配 -->
        <bean id="student" class="com.spring.entity.Student">
            <!-- 普通属性注入 -->
            <property name="name" value="赵云"/>
    
            <!-- 引用属性注入 -->
            <property name="address" ref="address"/>
    
            <!-- 数组注入 -->
            <property name="books">
                <array>
                    <value>红楼梦</value>
                    <value>西游记</value>
                    <value>水浒传</value>
                    <value>三国演义</value>
                </array>
            </property>
    
            <!-- List 注入 -->
            <property name="hobby">
                <list>
                    <value>听歌</value>
                    <value>写字</value>
                    <value>Code</value>
                </list>
            </property>
    
            <!-- Map 注入 -->
            <property name="tag">
                <map>
                    <entry key="姓" value="赵"/>
                    <entry key="名" value="云"/>
                    <entry key="字" value="子龙"/>
                </map>
            </property>
    
            <!-- Set 注入 -->
            <property name="games">
                <set>
                    <value>澄海3C</value>
                    <value>魔兽争霸</value>
                    <value>红色警戒</value>
                    <value>真三国无双</value>
                </set>
            </property>
    
            <!-- Properties 注入 -->
            <property name="info">
                <props>
                    <prop key="学号">蜀007</prop>
                    <prop key="姓名">赵云</prop>
                    <prop key="性别"></prop>
                </props>
            </property>
        </bean>
    </beans>
    
  4. 加载容器:ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

  5. 从容器中获取 Bean:Student student = context.getBean("student", Student.class);

Spring 核心功能

IOC 控制反转

  • IOC 即:控制反转,是指控制权限的转移,程序不再负责对象的创建和维护,而是交由容器负责。
  • IOC 内部使用一个单例对象的 Map 对 Bean 进行管理,====> map(BeanName, 该 Bean 的实例)

DI 依赖注入

  • DI 即:依赖注入,是 IOC 思想的一种实现
  • 依赖是指:Bean 对象的创建依赖于容器
  • 注入是指:Bean 依赖的资源由容器类进行设置和装配
  • 所谓依赖注入:就是由 IOC 容器在运行期间,动态的将依赖关系注入到对象中

依赖注入方式:

  • 构造器注入:将被依赖对象通过构造函数的参数注入给依赖对象,并且在初始化对象时注入
    • 优点:对象初始化完成后即可获得可用对象
    • 缺点:当需要注入的对象很多时,构造器的参数列表将会很长,不够灵活;若有多重注入方式,每种方式只需注入几个依赖,就需要提供多个重载的构造函数,非常麻烦。
  • Setter 方法注入:IOC 通过调用成员变量的 set 方法将被依赖对象注入给依赖类
    • 优点:灵活,可以选择性的注入所需的对象
    • 缺点:依赖对象初始化完成后由于还尚未注入被依赖对象,因此还不能使用
  • 接口注入:依赖类必须要实现指定接口,然后实现该接口的一个函数,该函数就是用于依赖注入,该函数的参数就是要注入的对象
    • 优点:接口注入中,接口的名字、函数的名字都不重要,只要保证函数的参数是要注入的对象类型即可
    • 缺点:侵入性太强,不建议使用
依赖注入配置
<!-- 通过该方式创建 Bean,默认使用无参构造方法 -->
<bean id="userOne" class="com.spring.entity.UserOne">
  <!-- 通过该方式注入,则默认使用 Setter 方法,即实体中必须定义 Setter 方法 -->
  <property name="name" value="张三"/>
</bean>

<!-- 通过有参构造方法注入对象 -->
<bean id="userTwo" class="com.spring.entity.UserTwo">
  <!-- 该方式注入,参数个数与构造函数中的参数个数保持一致,且值的类型要与实体中定义的类型保持一致 -->
  <constructor-arg index="0" value="李四"/>
  <constructor-arg index="1" value="18"/>
</bean>

<!-- 通过有参构造方法注入对象 -->
<bean id="userThree" class="com.spring.entity.UserThree">
  <!-- 该方式注入,参数个数与构造函数中的参数个数保持一致,并指定属性所对应的类型当两个属性的类型一样时,容易出错,故不建议使用 -->
  <constructor-arg type="java.lang.String" value="王五"/>
  <constructor-arg type="java.lang.Integer" value="19"/>
</bean>

p 命名空间和 c 命名空间

即:在 <beans> 标签引入 xmlns:p 和 xmlns:c 的约束

  • p 标签:对应 Setter 方法注入
  • c 标签:对应构造器注入
p/c 标签使用
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 原 Setter 注入 -->
    <bean id="address" class="com.spring.entity.Address">
        <property name="address" value="蜀国*汉中"/>
    </bean>
    <!-- p 标签使用 -->
    <bean id="addressTwo" class="com.spring.entity.Address" p:address="蜀国*荆州"/>


    <!-- 原构造器注入 -->
    <bean id="userTwo" class="com.spring.entity.UserTwo">
        <constructor-arg index="0" value="李四"/>
        <constructor-arg index="1" value="18"/>
    </bean>
    <!-- c 标签使用 -->
    <bean id="user" class="com.spring.entity.UserThree" c:name="朱六" c:age="20"/>

</beans>

Java 配置类实现自动装配

  1. 定义一个 Java 类

  2. 添加 @Configuration 注解

  3. 使用 @Bean 注解需要装配的 Bean

    RestTemplate 示例
    @Configuration
    public class ConfigBean {
    
        @Bean
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }
    
  4. 通过注解容器加载配置

    获取资源
    // 通过容器读取配置
    ApplicationContext context = new AnnotationConfigApplicationContext(ConfigBean.class);
    // 通过 ConfigBean 中的方法名获取需要装配的类
    RestTemplate restTemplate = context.getBean("getRestTemplate", RestTemplate.class);
    

xml 实现自动装配

  1. byName:需保证所有 bean 的 id 唯一,且要装配的 bean (Cat、Dog) 要与 set 方法的值一致

    byName
    <!-- 定义 Cat、Dog -->
    <bean id="cat" class="com.spring.entity.autowired.Cat"/>
    <bean id="dog" class="com.spring.entity.autowired.Dog"/>
    
    <!-- 定义 Person 并自动装配 Cat 和 Dog -->
    <bean id="person" class="com.spring.entity.autowired.Person" autowire="byName">
        <property name="name" value="张飞"/>
    </bean>
    
  2. byType:可以不定义 id 属性,但需保证 bean 的 class 唯一,且要装配的 bean (Cat、Dog) 的类型要与 set 方法的类型保持一致

    byType
    <!-- 定义 Cat、Dog -->
    <bean class="com.spring.entity.autowired.Cat"/>
    <bean class="com.spring.entity.autowired.Dog"/>
    
    <!-- 定义 Person 并自动装配 Cat 和 Dog -->
    <bean id="person" class="com.spring.entity.autowired.Person" autowire="byType">
        <property name="name" value="张飞"/>
    </bean>
    

注解实现自动装配

  1. 引入 context 约束并开启自动装配 xmlns:context="http://www.springframework.org/schema/context"

    引入 context 并开启自动装配
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!-- 定义 Cat、Dog -->
        <bean id="cat" class="com.spring.entity.autowired.Cat"/>
        <bean id="dog" class="com.spring.entity.autowired.Dog"/>
    
        <!-- region 引入 context 通过注解实现自动装配 -->
        <!-- 该方式需引入 context 并在 Person 的属性中添加 @Autowired 注解 -->
        <!-- 开启自动配置 -->
        <context:annotation-config/>
    
        <bean id="personTwo" class="com.spring.entity.autowired.PersonTwo">
            <property name="name" value="赵云"/>
        </bean>
    </beans>
    
  2. 属性中添加 @Autowired 注解;此方式基于反射实现,无需 set 方法 (该注解也可加在 set 方法上)

    实体上注解的使用
    public class PersonTwo {
    
        private String name;
    
        @Autowired
        private Cat cat;
        @Autowired
        private Dog dog;
    
        ......
    }
    

AOP 面向切面编程

  • 在不影响原有业务的情况下实现动态增强

静态代理

角色:

  • 抽象角色:接口或者抽象类
  • 真实角色:需要被代理的角色
  • 代理角色:代理对象,拥有真实角色的能力,且具备扩展能力
  • 访问角色:访问代理对象的角色

优点:

  • 可以使真实角色的操作更加纯粹,只做自己该做的事
  • 实现业务的分工
  • 公共业务发生扩展时,方便集中管理

缺点:一个真实角色需要对应一个代理角色,代码量会翻倍 -- 使用动态代理解决

动态代理

角色:同静态代理一样
区别:在于动态代理的代理类是动态生成的,而不是由程序员直接写死的
实现方式:

  • 基于接口的实现:JDK 动态代理
  • 基于类的实现:cglib
  • 基于 Java 字节码的实现:javassist

动态代理相关类:Proxy、InvocationHandler

核心概念

  • 横切关注点:跨越多个模块的方法或功能,即与业务功能无关的,但又是我们关注的部分
  • 切面 (Aspect):将关注点抽象成一个类,这个类就叫切面
  • 通知 (Advice):切面里的方法就叫通知
  • 目标 (Target):被通知对象,接口或方法
  • 代理 (Proxy):向目标对象应用通知之后创建的对象,即生成的代理类
  • 切入点 (PointCut):切面通知执行的地点,即在哪执行
  • 连接点 (JointPoint):与切入点匹配的执行点

通知类型

  • 前置通知:在方法执行之前执行
  • 后置通知:在方法执行之后执行
  • 返回通知:在方法返回结果中执行
  • 异常通知:在方法抛出异常时执行
  • 环绕通知:围绕着方法执行

Jdk 动态代理 实现 AOP

  1. 新建接口及实现类

  2. 新建切面类

    定义切面
    public class MyAspect {
    
        public void start() {
            System.out.println("事务开始~~");
        }
    
        public void end() {
            System.out.println("事务结束~~");
        }
    }
    
  3. 新建代理类实现 InvocationHandler 接口;重写 invoke 方法;并定义一个方法用于返回代理后对象

    JdkProxy 代理类
    public class JdkProxy implements InvocationHandler {
    
        private Object object;
    
        /**
         * 创建代理
         * @param object 被代理对象
         * @return 返回增强后对象
         */
        public Object createProxy(Object object) {
            this.object = object;
    
            // 类加载器
            ClassLoader classLoader = JdkProxy.class.getClassLoader();
    
            // 被代理对象实现的所有接口
            Class<?>[] clazz = object.getClass().getInterfaces();
    
            // 使用代理进行增强,返回的是代理后的对象
            return Proxy.newProxyInstance(classLoader, clazz, this);
        }
    
        /**
         * 所有动态代理类的方法调用,都会交由 invoke() 方法去处理
         * @param proxy 被代理后的对象
         * @param method 将要执行的方法
         * @param args 方法时需要的入参
         * @return 返回
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 声明切面
            MyAspect myAspect = new MyAspect();
    
            // 切入到程序执行前
            myAspect.start();
    
            // 在目标上调用
            Object invoke = method.invoke(object, args);
    
            // 切入到程序执行后
            myAspect.end();
    
            return invoke;
        }
    }
    
  4. 新建测试类

    测试方法
    @Test
    public void test() {
        // 创建代理对象
        JdkProxy jdkProxy = new JdkProxy();
    
        // 创建目标对象
        UserDao userDao = new UserDaoImpl();
    
        // 从代理对象中获取增强后的目标对象
        UserDao proxy = (UserDao) jdkProxy.createProxy(userDao);
    
        // 执行添加用户方法
        proxy.addUser();
        proxy.deleteUser();
        proxy.updateUser();
        proxy.queryUser();
    }
    

Spring 实现 AOP

  1. 引入织入包

    引入织入依赖
    <!-- aspect aop 织入 -->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.9.1</version>
    </dependency>     
    
  2. 新建切面,并实现相应的 Advice

    编写切面
    public class BeforeLoggerAspect implements MethodBeforeAdvice {
    
        /**
         * 前置通知
         * @param method 要执行的目标方法
         * @param args 参数
         * @param target 目标对象
         * @throws Throwable 抛出异常
         */
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println(target.getClass().getName() + " 的 " + method.getName() + " 方法被执行了");
        }
    }
    
    /**
     * 提供返回结果的后置通知
     */
    public class AfterLoggerAspect implements AfterReturningAdvice {
        @Override
        public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
            System.out.println("执行了 " + method.getName() + " 方法;返回了 " + returnValue);
        }
    }
    
  3. 配置 AOP,需引入 aop 的约束

    AOP 配置
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!-- 注册 Bean -->
        <bean id="userDaoImplTwo" class="com.spring.aop.dao.UserDaoImplTwo"/>
        <bean id="beforeLoggerAspect" class="com.spring.aop.aspect.BeforeLoggerAspect"/>
        <bean id="afterLoggerAspect" class="com.spring.aop.aspect.AfterLoggerAspect"/>
    
        <!-- region 方式一 -->
        <!-- 配置 AOP -->
        <aop:config>
            <!-- 切入点配置
            expression 表达式:
                * : 任意位置执行
                com.spring.aop.dao.UserDaoImplTwo.* : UserDaoImplTwo 类下的所有方法
                (..) : 任意参数
            -->
            <aop:pointcut id="pointcut" expression="execution(* com.spring.aop.dao.UserDaoImplTwo.*(..))"/>
    
            <!-- 执行环绕增强 -->
            <!-- 将 beforeLoggerAspect 类,切入到 pointcut 方法上 -->
            <aop:advisor advice-ref="beforeLoggerAspect" pointcut-ref="pointcut"/>
            <!-- 将 afterLoggerAspect 类,切入到 pointcut 方法上 -->
            <aop:advisor advice-ref="afterLoggerAspect" pointcut-ref="pointcut"/>
        </aop:config>
        <!-- endregion -->
    
        <!-- region 方式二 -->
        <bean id="myAspect" class="com.spring.aop.aspect.MyAspect"/>
    
        <aop:config>
            <!-- 自定义切面 -->
            <aop:aspect ref="myAspect">
                <!-- 切入点 -->
                <aop:pointcut id="point" expression="execution(* com.spring.aop.dao.UserDaoImplTwo.*(..))"/>
                <!-- 通知 -->
                <aop:before method="start" pointcut-ref="point"/>
                <aop:after method="end" pointcut-ref="point"/>
            </aop:aspect>
        </aop:config>
        <!-- endregion -->
    </beans>
    
  4. 编写测试类

    测试方法
    @Test
    public void springImplAopTest() {
        ApplicationContext context = new ClassPathXmlApplicationContext("aspect.xml");
    
        // 注册时要配置 Bean 的实现类,而此处代理的要指向接口
        UserDao userDao = context.getBean("userDaoImplTwo", UserDao.class);
        userDao.addUser();
    }
    

注解实现 AOP

  1. 新建自定义类,添加 @Aspect 注解;自定义方法并开启注解

    自定义切面
    @Aspect
    public class AnnotationAspect {
    
        @Before("execution(* com.spring.aop.dao.UserDaoImplThree.*(..))")
        public void before() {
            System.out.println("======== 方法执行前执行 ========");
        }
    
        @After("execution(* com.spring.aop.dao.UserDaoImplThree.*(..))")
        public void after() {
            System.out.println("======== 方法执行后执行 ========");
        }
    
        @Around("execution(* com.spring.aop.dao.UserDaoImplThree.*(..))")
        public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("环绕前:" + proceedingJoinPoint);
            Object proceed = proceedingJoinPoint.proceed();
            System.out.println("环绕后:" + proceed);
            return proceed;
        }
    }
    
  2. 开启 AOP 配置

    AOP 配置
    <!-- region 方式三 -->
    <bean id="userDaoImplThree" class="com.spring.aop.dao.UserDaoImplThree"/>
    <bean id="annotationAspect" class="com.spring.aop.aspect.AnnotationAspect"/>
    <!-- 开启切面自动配置 -->
    <aop:aspectj-autoproxy/>
    <!-- endregion -->
    

    注意:<aop:aspectj-autoproxy proxy-target-class="true" /> 中 proxy-target-class 的属性默认为 false,即 JDK 实现;若改为 true 则为 cglib 实现

  3. 编写测试类

    测试方法
    @Test
    public void annotationImplAopTest() {
        ApplicationContext context = new ClassPathXmlApplicationContext("aspect.xml");
    
        UserDao userDao = context.getBean("userDaoImplThree", UserDao.class);
        userDao.addUser();
    }
    

Spring 事务管理

  • 事务:即作为单个逻辑工作单元执行的一系列操作

  • 分为:编程式事务管理和声明式事务管理

  • 编程式事务:在代码中采用 try catch 方式处理

    编程式事务管理
    public void createUser() {
        TransactionStatus txStatus = 
            transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            userMapper.insertUser(user);
        } catch (Exception e) {
            transactionManager.rollback(txStatus);
            throw e;
        }
        transactionManager.commit(txStatus);
    }
    
  • 声明式事务:采用声明的方式来处理事务,即:在配置文件中声明

    声明式事务管理
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/tx
            https://www.springframework.org/schema/tx/spring-tx.xsd">
    
        <!-- 注册 DataSource -->
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/pro?useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=UTC"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </bean>
    
        <!-- 配置声明式事务;即开启事务 -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <constructor-arg ref="dataSource" />
        </bean>
    
        <!-- region 结合 AOP 实现事务的织入 -->
        <!-- 配置事务通知 -->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <!-- 给哪些方法配置事务 -->
            <tx:attributes>
                <!-- 可在此直接配置隔离级别和传播行为 -->
                <tx:method name="update" isolation="DEFAULT" propagation="REQUIRED"/>
                <tx:method name="*"/>
            </tx:attributes>
        </tx:advice>
    
        <!-- 配置事务切入 -->
        <aop:config>
            <!--  切入点 -->
            <aop:pointcut id="txPointCut" expression="execution(* com.spring.aop.dao.*.*(..))"/>
            <!--  通知 -->
            <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
        </aop:config>
        <!-- endregion -->
    </beans>
    

事务的 ACID 特性

  • 原子性:要么都执行,要么都不执行
  • 一致性:事务开始前和事务结束后,数据库的完整性不能被破坏
  • 隔离性:与其他并发事务所做的修改保持隔离、互不干扰
  • 持久性:事务完成后对系统的影响是永久的

Spring 事务的隔离级别

@Transactional(isolation=Isolation.DEFAULT)

  • DEFAULT:默认,使用数据库的隔离级别
    • MySQL 默认:可重复读;Oracle 默认:读已提交
  • READ_UNCOMMITTED (读-未提交):会出现脏读、幻读、不可重复读问题
  • READ_COMMITTED (读-已提交):避免脏读,但可能出现幻读和不可重复读
  • REPEATABLE_READ (可重复读):避免脏读和不可重复读,但可能出现幻读
  • SERIALIZABLE (串行化):避免以上所有问题,最安全、性能影响极大

Spring 事务的传播行为

@Transaction(propagation=Propagation.REQUIRED)

  • 事务传播是指:多个事务方法进行调用时,事务是如何传播的;即:方法 A 是一个事务方法,A 在执行过程中调用了方法 B,那么方法 B 要不要事务?要的话又该怎么执行?

保证在同一事务中

  • REQUIRED; // 支持当前事务,若不存在则新建一个 (即:若 A 存在事务,则 B 加入到该事务中;若 A 不存在事务,则 B 另起事务执行)
  • SUPPORTS; // 支持当前事务,若不存在则不使用事务
  • MANDATORY; // 支持当前事务,若不存在则抛出异常
  • NESTED; // 支持当前事务,若 A 存在事务,则执行一个嵌套事务 (即 A 为父事务,B 为子事务);若不存在则新建事务

保证不在同一事务中

  • REQUIRES_NEW; // 总是创建新事务 (不管 A 是否有事务,B 都另起事务执行)
  • NOT_SUPPORTED; // 存在事务则挂起,一直执行非事务操作
  • NEVER; // 总是执行非事务,若当前存在事务则抛出异常
posted @   kaishen  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示