发布时间:2022-12-09 文章分类:编程知识 投稿人:赵颖 字号: 默认 | | 超大 打印

SSM

目录
  • SSM

    • Spring

      • IOC(控制反转)& DI(依赖注入)

        • Bean注入方式
      • AOP(面向切面编程)

        • 一、AOP编程思想

          • 1.1 什么是AOP
          • 1.2 为什么需要AOP
          • 1.3 AOP实现方法分类
        • 二、AOP的专有名词
        • 三、认识SpringAOP

          • 3.1 SpringAOP的特点
          • 3.2 SpringAOP使用与实现
      • 代理模式

        • 静态代理
        • JDK动态代理
        • CGlib动态代理
    • SPring MVC

      • SpringMVC核心组件
      • SpringMVC第一个程序
      • 处理请求以及响应

        • 参数传递
        • 响应 页面跳转
      • SpringMVC基本配置
      • SpringMVC文件上传
      • Restful风格接口
      • SpringMVC工作流程
    • MyBAtis

      • 实现原理:工厂模式

        • 简单工厂
        • 工厂方法
        • 抽象工厂
      • MyBatis基本应用

        • MyBatis的基本配置(MyBatis-config.xml)

          • MyBatis数据源配置

            • 数据库连接池 (druid c3p0)
          • 映射文件配置
          • 其他配置
          • MyBatis整合Log4J
        • MyBatis主要构件
      • MyBatis映射文件

        • 基本标签
        • 动态SQL
        • ResultMap

          • 基础查询 - 基本映射
          • 外联查询 - 一对一查询
          • 外联查询 - 一对多查询
      • MyBatis 一级缓存与二级缓存
    • 事务管理

      • Spring事务管理

        • 事务的特性(ACID)
        • 事务的隔离级别
        • Spring事务

          • 编程式事务
          • 声明式事务
        • 事务传播机制

Spring

IOC(控制反转)& DI(依赖注入)

IOC是容器,用来装Spring创建的Bean对象。

Bean注入方式

<!--     SpringBean的注入方式  -->
<!--      手动装配  -->
<!--      ==  set方法注入    -->
    <bean id="hello" class="com.dzqc.smbms.entity.SUser">
        <property name="userCode" value="hello"></property>
        <property name="userName" value="Spring"></property>
        <property name="role">
            <bean class="com.dzqc.smbms.entity.SRole">
                <property name="roleCode" value="这里是注入的实例对象"></property>
                <property name="roleName" value="这里是注入的实例对象的值"></property>
            </bean>
        </property>
        <property name="role" ref="myRole"></property>
    </bean>
    <bean name="myRole" class="com.dzqc.smbms.entity.SRole">
        <property name="roleName" value="管理员"></property>
        <property name="roleCode" value="SMBMS_ADMIN"></property>
    </bean>
<!--      ==  带参构造器注入  -->
    <bean name="constructor" class="com.dzqc.smbms.entity.SUser">
        <constructor-arg name="userName" value="这是通过构造器注入的userName"></constructor-arg>
        <constructor-arg name="userCode" value="这是通过构造器注入的userCode"></constructor-arg>
    </bean>
    <!--  自动装配  -->
    <!--  ==  配置包扫描,并使用注解进行注入  -->
    <context:component-scan base-package="com.dzqc.smbms">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

IOC和DI的区别:

IOC是什么:是控制反转,将new对象的权力由调用程序交给第三方IOC容器,控制权被转移给了IOC容器,在调用程序看来,new对象的动作由主动变成了被动,所以叫做控制反转。

DI是什么:依赖注入,将IOC容器中new出来的对象注入进调用程序。

IOC和ID的区别:IOC和DI是一体的,DI是IOC的另一种表现形式,是同一事件不同层面的解读。

AOP(面向切面编程)

一、AOP编程思想

1.1 什么是AOP

​ AOP (Aspect Orient Programming),直译过来就是 面向切面编程。AOP是一种编程思想,基于面向对象编程思想(OOP)的补充。面向对象编程将程序抽象成了各个切面。

1.2 为什么需要AOP

​ 在开发过程中,存在某段多次重复的代码,以面向过程编程方式的话,我们会将这段代码抽象成一个方法,在需要的位置调用该方法。当这段代码需要修改时,我们就只需要改变这一个方法就够了。

​ 但需求不是一成不变的,如果后期需要新增一个需求,又需要在多处进行修改,就需要再抽象一个方法,然后在需要的位置再分别调用这个方法。又或者,我们在业务过程中删除这个方法,我们就需要删除掉每一个地方的调用。

​ 在这种情况之下,我们就可以通过使用AOP来解决。

1.3 AOP实现方法分类

​ 首先我们要知道AOP的目的,AOP要达到在开发者不修改源代码的前提下,去为系统中的业务添加某种通用的功能。

​ AOP的实现方式大致可以分为两类:

类别 机制 原理
静态AOP 静态织入 在编译前,切面直接以字节码的形式编译入目标代码的字节码文件中,对性能无影响,但灵活性不够。
动态AOP JDK动态代理 在运行期间,目标类加载之后为接口动态生成代理类,将切面织入到代理类中,相对于静态AOP更灵活一些。但切入点需要实现接口,对系统的性能有一定影响。
动态字节码生成 CGlib 在运行期间,目标类加载之后,动态生成目标类的子类,然后将切面的逻辑加入到子类中,相对于JDK动态代理的方式,CGlib不需要接口也可以实现织入,但是当扩展类的实例方法使用final修饰时无法进行织入。

二、AOP的专有名词

AOP中的特性术语大致有一下几种:

SpringAOP在AOP的基础上又多出几个延伸概念:

三、认识SpringAOP

3.1 SpringAOP的特点

​ AOP的框架有很多,实现方式各不相同,Spring的AOP则是通过动态代理进行实现的。下面简单对代理模式进行介绍。

3.2 SpringAOP使用与实现

Execution表达式:

​ 声明具体方法的位置的表达式 :返回值 包路径.类名.方法名(参数列表)

代理模式

​ 代理模式分为两种:

静态代理

​ 被代理类接口:

package com.dzqc.smbms.proxy;
/**
 * 游戏接口
 */
public interface Game {
    public void gameStart();
    public void gameOver();
}

被代理类:

package com.dzqc.smbms.proxy;
public class Mario implements Game{
    @Override
    public void gameStart() {
        System.out.println("超级马里奥,游戏开始!");
    }
    @Override
    public void gameOver() {
        System.out.println("够不着旗杆,游戏结束");
    }
}

代理类:

package com.dzqc.smbms.proxy;
/**
 * 本类为 静态代理 类
 * 代理类 需要 持有 目标对象
 */
public class GameStaticProxy implements Game{
    private Game targetGame;
    public Game getProxy(Game targetGame){
        this.targetGame = targetGame;
        return this;
    }
    @Override
    public void gameStart() {
        System.out.println("游戏机启动");
        System.out.println("游戏启动!");
        targetGame.gameStart();
        System.out.println("游戏进行中,mario他跳起来了");
    }
    @Override
    public void gameOver() {
        System.out.println("mario 打败了魔王,找到了旗杆,要爬旗杆。 ");
        targetGame.gameOver();
        System.out.println("真菜,不玩了,破游戏什么时候关服!");
    }
}

测试类:

package com.dzqc.smbms.proxy;
import org.junit.Test;
import java.lang.reflect.Proxy;
public class ProxyTest {
    @Test
    public void staticProxy(){
        Mario mario = new Mario();
        GameStaticProxy gameStaticProxy = new GameStaticProxy();
        Game marioProxy = gameStaticProxy.getProxy(mario);
        marioProxy.gameStart();
        marioProxy.gameOver();
    }
}

测试结果:

游戏机启动
游戏启动!
超级马里奥,游戏开始!						//原始内容
游戏进行中,mario他跳起来了
mario 打败了魔王,找到了旗杆,要爬旗杆。 
够不着旗杆,游戏结束						 //原始内容
真菜,不玩了,破游戏什么时候关服!

JDK动态代理

​ JDK动态代理的实现逻辑大致如下:

接口与被代理类与上述一致

代理类:

package com.dzqc.smbms.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
 * JDK代理类
 */
public class GameJDKProxy implements InvocationHandler {
    private Game targetGame;
    public GameJDKProxy(Game targetGame){
        this.targetGame = targetGame;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String name = method.getName();
        if (name.equals("gameStart")){
            Object o = gameStart(method, args);
        }else {
            Object o = gameOver(method, args);
        }
        return null;
    }
    private Object gameStart(Method method , Object[] args) throws Throwable{
        System.out.println("mario游戏启动了");
        Object invoke = method.invoke(targetGame, args);
        System.out.println("正在操作mario通水管");
        return null;
    }
    private Object gameOver(Method method , Object[] args) throws Throwable{
        System.out.println("水管打不通");
        Object invoke = method.invoke(targetGame, args);
        System.out.println("什么破游戏!");
        return null;
    }
}

测试类:

package com.dzqc.smbms.proxy;
import org.junit.Test;
import java.lang.reflect.Proxy;
public class ProxyTest {
    @Test
    public void jdkProxy(){
        Mario mario = new Mario();
        GameJDKProxy gameJDKProxy = new GameJDKProxy(mario);
//        使用JDK动态代理类获取代理对象
        Game marioProxy  = (Game) Proxy.newProxyInstance(
                this.getClass().getClassLoader(), //类加载器
                new Class[]{Game.class},  // 代理接口的字节码文件
                gameJDKProxy// 自己的代理类,进行目标方法扩充
        );
        marioProxy.gameStart();
        marioProxy.gameOver();
    }
}

测试结果

mario游戏启动了
超级马里奥,游戏开始!				//原始内容
正在操作mario通水管
水管打不通
够不着旗杆,游戏结束				 //原始内容
什么破游戏!

​ 由上述代码不难看出,动态代理的实现需要以下几点:

  1. JDK动态代理需要声明接口。想要创建一个动态代理类,就必须给这个类声明一个接口,否则无法在Proxy.newProxyInstance时传入对应的接口类字节码文件。
  2. 在代理类中需要通过构造传入原有的bean,因为处理完附加功能外,需要执行原有bean中的方法,以实现代理的目的。

CGlib动态代理

代理类:

package com.dzqc.smbms.proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class GameCglibProxy implements MethodInterceptor {
    private Game targetGame;
    public GameCglibProxy(Game targetGame){
        this.targetGame = targetGame;
    }
    /**
     * 获取代理对象
     * @return
     */
    public Game getProxy(){
//        获取cglib操作类
        Enhancer enhancer = new Enhancer();
//        指定代理类 的 父类类型,要代理哪个类就使用哪个类做代理类的父类
        enhancer.setSuperclass(Mario.class);
//        指定代理对象,把当前代理类作为代理对象
        enhancer.setCallback(this);
//        创建并返回代理对象
        return (Game) enhancer.create();
    }
    /**
     * 代理增强的实际操作处
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        String name = method.getName();
        if (name.equals("gameStart")){
            gameStart(method , objects);
        }else {
            gameOver(method , objects);
        }
        return null;
    }
    private Object gameStart(Method method , Object[] args) throws Throwable{
        System.out.println("魂斗罗启动!!!!");
        Object invoke = method.invoke(targetGame, args);
        System.out.println("正在操作Mario和魂斗罗兄弟决斗");
        return null;
    }
    private Object gameOver(Method method , Object[] args) throws Throwable{
        System.out.println("Mario挖了下水道,坑了魂斗罗兄弟");
        Object invoke = method.invoke(targetGame, args);
        System.out.println("不要串台!");
        return null;
    }
}

测试类:

package com.dzqc.smbms.proxy;
import org.junit.Test;
import java.lang.reflect.Proxy;
public class ProxyTest {
    @Test
    public void cglibProxy(){
        Mario mario = new Mario();
        GameCglibProxy gameCglibProxy = new GameCglibProxy(mario);
        Game proxy = gameCglibProxy.getProxy();
        proxy.gameStart();
        proxy.gameOver();
    }
}

测试结果:

魂斗罗启动!!!!
超级马里奥,游戏开始!					//原始内容
正在操作Mario和魂斗罗兄弟决斗
Mario挖了下水道,坑了魂斗罗兄弟
够不着旗杆,游戏结束					 //原始内容
不要串台!

​ 由上述代码不难看出,CGlib在进行动态代理的过程中,对被实现的类要求较少,而且更为灵活,使用者可以根据实际情况进行选择。

SPring MVC

MVC是什么?MVC是设计模式,包含Model(模型)、View(视图)、Controller(控制器)。

SpringMVC就是基于MVC设计模式创建的一个控制层框架,用来简化视图与后台的数据交互工作,并且规范开发方式。SpringMVC是对Servlet的封装,可将SpringMVC的本质看作Servlet。

SpringMVC核心组件

SpringMVC第一个程序

处理请求以及响应

参数传递

类需要使用@Controller注解,在类中声明一个Handler方法,在方法的参数列表中声明需要的参数,在Handler方法被调用时,就可以正确传入对应的参数。

响应 页面跳转

  1. 只响应数据:数据接口,在Handler方法上需要添加@ResponseBody注解,添加完成后,返回值不会被视图解析器进行解析,在返回时可以按照正常的数据进行返回。
  2. 默认页面返回:默认的响应方式,本质上是请求转发,在Handler方法进行return后,会将返回的数据转发至DispatcherServlet,然后由DispatcherServlet调度视图解析器,解析返回值中的数据与视图并响应。
  3. 重定向:重定向可以重定向到任意路径,只需要在返回值上、ModelAndView的视图名前 加上 redirect,这里的重定向与Servlet的重定向功能保持一致,流程有变动。在Handler(处理器)方法进行返回后,将请求转发至DispatcherServlet(中央处理器),由DispatcherServlet(中央处理器)调度HttpServletResponse(响应),由HttpServletResponse(响应)进行重定向操作。

SpringMVC基本配置

在web.xml种配置 dispatcherServlet

  <!--  配置中央处理器,与Servlet一起初始化  -->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath*:spring-mvc.xml</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  <!--  配置SpringIOC容器  -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>

在Spring文件当中配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
    <!--  配置包扫描,扫描所有使用Controller注解的类  -->
    <context:component-scan base-package="com.dzqc.smbms">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    <!--  处理器适配器  -->
    <bean name="handlerAdapter"
          class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean>
    <!--  处理器映射器  -->
    <bean name="handlerMapping"
          class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>
    <!--  视图解析器  -->
    <bean name="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/views/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
    <!--  配置静态资源过滤器  -->
    <mvc:default-servlet-handler/>
    <mvc:resources location="/statics/" mapping="/static/*"></mvc:resources>
    <!--  文件上传解析器  -->
    <bean name="multipartResolver"
          class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--  最大支持文件大小,此处配置50MB大小限制  -->
        <property name="maxUploadSize" value="52428800"></property>
    </bean>
</beans>

SpringMVC文件上传

依赖:

<!--  文件上传  -->
<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.4</version>
</dependency>

Restful风格接口

Rest注解:

SpringMVC工作流程

准备工作:

/*  在SpringMVC首次刷新或重新加载时,会调用SpringMVC初始化策略  */
protected void onRefresh(ApplicationContext context) {
    this.initStrategies(context);
}
/**  初始化策略方法  */
protected void initStrategies(ApplicationContext context) {
    this.initMultipartResolver(context);       		// 初始化文件上传解析器
    this.initLocaleResolver(context); 	 			// 初始化本地解析器
    this.initThemeResolver(context);	 			// 初始化中心解析器
    this.initHandlerMappings(context);   			// 初始化处理器映射器
    this.initHandlerAdapters(context);   			// 初始化处理器适配器
    this.initHandlerExceptionResolvers(context);    // 初始化处理器异常解析器
    this.initRequestToViewNameTranslator(context);  // 初始化 请求视图 翻译器
    this.initViewResolvers(context);				// 初始化视图解析器
    this.initFlashMapManager(context);				// 初始化 映射刷新管理器
}

2:请求处理流程

  1. DispathcerServlet(前端控制器)接收请求DispatcherServlet 通过 doService()方法 接收用户请求。调用 doDispatch() 方法进行请求调度。
  2. DispatcherServlet(前端控制器)调用HandlerMapping(处理器映射器):DispatcherServlet 通过getHandler() 调用HandlerMapping获取HandlerExecutionChain
  3. HandlerMapping将(HandlerExecutionChain)处理器执行链返回给DispatcherServlet
  4. DispathcerServlet(前端控制器)通过HandlerAdapter(处理器适配器),获取对应的Handler处理器方法。
  5. HandlerAdapter(处理器适配器)调用Handler处理器方法,处理用户请求。
  6. Handler处理器方法将返回值返回给HandlerAdapter(处理器适配器)
  7. HandlerAdapter将Handler处理器方法的返回值以ModelAndView的形式返回给DispatherServlet:
  8. DispatcherServlet将ModelAndView交给ViewResolver(视图解析器)解析。
  9. ViewResolver(视图解析器)将ModelAndView中的数据渲染到View(视图)中。
  10. ViewResolver返回的视图信息到DispatcherServlet。
  11. DispatcherServlet将视图信息及返回值返回给客户端。

image-20221204094023826

MyBAtis

​ MyBatis是一款轻量级的ORM(对象关系映射)(持久化)框架,能够将JDBC相关的持久化代码通过配置文件的形式实现,提高开发效率。

实现原理:工厂模式

手机工厂:MI,IPHONE,HUAWEI

简单工厂

工厂对象

抽象实体

具体实体

工厂方法

抽象工厂

抽象产品

实体工厂

实体产品

抽象工厂

MyBatis基本应用

MyBatis使用分为

MyBatis的基本配置(MyBatis-config.xml)

MyBatis数据源配置
<!--  环境配置标签 default中引用的为默认使用的环境  -->
<environments default="development">
    <!--  环境标签 id 唯一,不可重复  -->
    <environment id="development">
        <!--  事务管理器配置  -->
        <transactionManager type="JDBC"/>
        <!--  数据源配置 type默认为POOLED(默认启用数据库连接池模式) ,UNPOOLED 不启用连接池  -->
        <dataSource type="POOLED">
            <!--  属性标签,该处属性为数据源配置属性  -->
            <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/smbms_3"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </dataSource>
    </environment>
</environments>
数据库连接池 (druid c3p0)
映射文件配置
<mappers>
    <!--  映射文件的地址,引用相对路径  -->
    <mapper resource="mappers/SUserMapper.xml"></mapper>
</mappers>
其他配置
    <!--  MyBatis别名配置 只能配置实体类  -->
    <typeAliases>
<!--        <typeAlias type="com.dzqc.smbms.entity.SUser" alias="u"></typeAlias>-->
        <package name="com.dzqc.smbms.entity"/>
    </typeAliases>
    <settings>
        <!--  配置控制台日志组件  -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
MyBatis整合Log4J

MyBatis主要构件

获取资源:Resources.getResourceAsStream("MyBatis-config.xml");

SqlSessionFactoryBuilder:构建工厂的建筑队,需要给资源后,调用build方法才能构建工厂。

SqlSessionFactory:工厂建好后,调用openSession方法,产出SqlSession

SqlSession:产出的SqlSession可以通过Mapper接口的字节码(.class)来获取SqlSession中生产的代理对象,从而操作数据库。

MyBatis映射文件

基本标签

<select id="唯一,不可重复" parameterType="参数类型" resultType="返回值类型" resultMap=""
        >查询SQL</select>
<insert id="唯一,不可重复" parameterType="" keyColumn="" keyProperty="" useGeneritedKeys="true">插入语句</insert>
<update id="唯一,不可重复" parameterType="" >修改语句</update>
<delete id="唯一,不可重复" parameterType="" >删除语句</delete>

动态SQL

ResultMap

resultMap是MyBatis实现ORM特性的一个最重要的标签。

基础查询 - 基本映射

代表主键列 :column - 数据库列名 , property:实体类属性名

普通列 :column - 数据库列名 , property:实体类属性名

外联查询 - 一对一查询
<association property="role" javaType="srole" column="u.userRole">
    <id column="userRole" property="id"></id>
    <result column="roleCode" property="roleCode"></result>
    <result column="roleName" property="roleName"></result>
</association>
<association property="role" javaType="srole" column="userRole"
            select="com.dzqc.smbms.mapper.SRoleMapper.selectById" >
</association>

property:实体类的属性名

column:外关联字段的字段名

javaType:property对应的实体类的类型。

select:外关联查询的方法

外联查询 - 一对多查询
<collection property="roleUsers" column="id" javaType="list" ofType="suser"
            select="com.dzqc.smbms.mapper.ISUserMapper.selectByRoleId">
</collection>

property:实体类的属性名

column:外关联字段的字段名

javaType:一对多关联集合类型。

ofType:一对多关联集合中存放的数据的类型。

select:外关联查询的方法

MyBatis 一级缓存与二级缓存

事务管理

Spring事务管理

什么是事务:事务的本身其实就是一组业务逻辑,在业务逻辑当中包含有针对于数据库持久化的操作。在事务当中的所有业务逻辑被视为一个整体。

事务的特性(ACID)

事务的隔离级别

事务不隔离会产生的问题:

脏读:当前事务读到了其他事务未提交的数据

幻读(虚读):当前事务重复读取同样的数据,读取到了其他事务新增的数据,导致短时间内两次读取的数据不一致。

不可重复读:当前事务读到了其他事务已提交的数据,在短时间,两次查询的结果不一致。

Spring事务

编程式事务

优点:对事务的操作精细,能够更加精确的对事务进行管理

缺点:对代码的侵入度高,在后期进行维护、功能变更时会提高维护难度。

声明式事务

事务传播机制

事务传播机制一共有7种。

  1. requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,(事务默认传播机制)。
  2. supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
  3. mandatory:使用当前事务,如果没有当前事务,就抛出异常。
  4. required_new:新建事务,如果当前存在事务,把当前事务挂起。
  5. not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  6. never:以非事务方式执行操作,如果当前事务存在则抛出异常。
  7. nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作