zl程序教程

您现在的位置是:首页 >  数据库

当前栏目

面向面试编程连载(一)

2023-03-07 09:02:57 时间

2023年2月2日09:58:57更新

提问:请问什么是java中函数式接口?

回答:只定义了一个抽象方法的接口。(如果你也是这个回答,很遗憾肯定是不及格的)

原答案中对于此回答不是很认可,我不知道是处于语法的严谨还是个人角度理解的不同,在官方文档中如下

Package java.util.function Description
Functional interfaces provide target types for lambda expressions and method references. Each functional interface has a single abstract method, called the functional method for that functional interface, to which the lambda expression's parameter and return types are matched or adapted. Functional interfaces can provide a target type in multiple contexts, such as assignment context, method invocation, or cast context:

译文如下:

函数接口为 lambda 表达式和方法引用提供目标类型。每个函数接口都有一个抽象方法,称为该函数接口的函数方法,lambda 表达式的参数和返回类型与该方法匹配或调整。函数接口可以在多个上下文中提供目标类型,例如赋值上下文、方法调用或强制转换上下文

jdk哪个方法是用堆实现的

PriorityQueue

函数式编程的本质是什么?

函数式编程的本质是:把函数看作是数据。

Stream.foreach中类型是什么?

default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

1.请介绍下java中基本数据类型以及它们的使用场景

Java八大数据类型:

(1)整数类型:byte、short、int、long

(2)小数类型:float、double

(3)字符类型:char

(4)布尔类型:boolean

引用数据类型

String

2.为什么定义了这些基本数据类型后还要定义包装类?

之所以需要包装类型,就是因为java是一个面向对象的语言,然而基本数据类型不具备面向对象的特性,当我们把基本数据类型包装成包装类型之后,它就具有了面向对象的特性。而且,在往ArrayList、HashMap这些容器传数据的时候,基本类型int和double是传输不进去的,因为容器都是装object类型的,所以需要转为包装类型进行传输。每一个基本数据类型都有对应的包装类型.

3.包装类和String类有什么相同点吗?

都允许为null或空,

包装类除Float,Double并没有实现常量池技术,其他的和String类都存放在常量池中。

4.包装类是否重写了equals方法,为什么?

是的,先看他是否内存相等,如果不相等

5.请问我使用Integer定义两个数字,它们值都等于100,使用 == 和equals方式分别比较它们是否相等?

都是true

Integer a =100;
Integer b =100;


System.out.println(a == b);
System.out.println(a.equals(b));

输出
true
true
-------
Integer a =200;
Integer b =200;


System.out.println(a == b);
System.out.println(a.equals(b));

输出
false
true

6.导致上面结果原因是什么?如果我把值都改成200呢,结果会发生什么改变?

在内存中的缓存值是相等的。优先比较内存,200超过127的大小范围==是不相等的

7.我如何验证上述结果原因?

Integer的缓存机制:为了节省内存和提高性能,Integer类在内部通过使用相同的对象引用实现缓存和重用,Integer类默认在-128 ~ 127 之间,可以通过 -XX:AutoBoxCacheMax进行修改,且这种机制仅在自动装箱的时候有用,在使用构造器创建Integer对象时无用。

8.哪些包装类是带缓存的?默认值是多少?

Integer 、Byte 、Short 、Long 、Character 五大包装类都有缓冲机制,且缓冲的默认值范围都是-128~127

而Float,Double,Boolean 三大包装类并没有缓冲机制。

9.我是否可以改变缓存值区间?怎么做?

可以通过 -XX:AutoBoxCacheMax进行修改,且这种机制仅在自动装箱的时候有用,在使用构造器创建Integer对象时无用。

1.请简单说下mysql常用索引类型

主键索引、唯一索引、普通索引、全文索引、组合索引(联合索引,多列索引)

2.组合索引使用时有什么需要特别注意的?

1、对于复合索引,在查询使用时,最好将条件顺序按找索引的顺序,这样效率最高; select * from table1 where col1=A AND col2=B AND col3=D 如果使用 where col2=B AND col1=A 或者 where col2=B 将不会使用索引

2、何时是用复合索引 根据where条件建索引是极其重要的一个原则; 注意不要过多用索引,否则对表更新的效率有很大的影响,因为在操作表的时候要化大量时间花在创建索引中

3、复合索引会替代单一索引么 如果索引满足窄索引的情况下可以建立复合索引,这样可以节约空间和时间

3.为哪个表哪个字段需要添加索引有什么依据吗?

1、表的主键、外键必须有索引;

2、数据量超过300的表应该有索引;

3、经常与其他表进行连接的表,在连接字段上应该建立索引;

4、经常出现在Where子句中的字段,特别是大表的字段,应该建立索引;

5、索引应该建在选择性高的字段上;

6、索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引;

7、复合索引的建立需要进行仔细分析;尽量考虑用单字段索引代替:

A、正确选择复合索引中的主列字段,一般是选择性较好的字段;

B、复合索引的几个字段是否经常同时以AND方式出现在Where子句中?单字段查询是否极少甚至没有?如果是,则可以建立复合索引;否则考虑单字段索引;

C、如果复合索引中包含的字段经常单独出现在Where子句中,则分解为多个单字段索引;

D、如果复合索引所包含的字段超过3个,那么仔细考虑其必要性,考虑减少复合的字段;

E、如果既有单字段索引,又有这几个字段上的复合索引,一般可以删除复合索引;

8、频繁进行数据操作的表,不要建立太多的索引;

9、删除无用的索引,避免对执行计划造成负面影响;

以上是一些普遍的建立索引时的判断依据。一言以蔽之,索引的建立必须慎重,对每个索引的必要性都应该经过仔细分析,要有建立的依据。因为太多的索引与不充分、不正确的索引对性能都毫无益处:在表上建立的每个索引都会增加存储开销,索引对于插入、删除、更新操作也会增加处理上的开销。另外,过多的复合索引,在有单字段索引的情况下,一般都是没有存在价值的;相反,还会降低数据增加删除时的性能,特别是对频繁更新的表来说,负面影响更大

4.能为较长的varchar类型字段建立索引吗?建立哪种索引?

其中M指的是可存储的字符长度(或字符数),而MySQL实际是按字节存储的,在不同的字符集下一个字符的字节长不同,因此这个M最大值在不同的字符集下值不同:

对于latin字符集下,因为一个字符占一个字节,所以M的最大值为65535(但实际只有65532);对于gbk字符集,因为一个字符占两个字节,所以M的最大值为32767;对于utf8字符集,因为一个字符占两到三个字节,所以M的最大值为21845。

此外,mysql官方文档中定义的65535长度是指同一行的所有varchar列的长度总和。如果列的长度总和超出这个长度,依然无法创建。

1、MySQL5.6的限制方式:

在MySQL5.6版本中,当某个列的varchar长度定义超过相应字符集下的最大长度时,会自动将该列转存为mediumtext类型。例如,在utf8字符集下,定义ecs_payment表test2字段长度为21846:

假如再存储一个字段test3,定义varchar长度为21845,这时没有超过最大长度限制,但在存储test3 varchar(21845)列时,发现该表上所有varchar行的总长度将会超过65535字节,因此会发生如下报错:

mysql> alter table ecs_payment add test3 varchar(21845);
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs

2、MySQL5.7的限制方式:

在MySQL5.7版本下,只要列的varchar长度超过相应字符集下的最大限制,或者表上所有varchar列总长度将会超过65535字节时,MySQL都会抛出错误提示:

mysql> alter table t1 add c1 varchar(21846);
ERROR 1074 (42000): Column length too big for column 'c1' (max = 21845); use BLOB or TEXT instead
mysql> alter table t1 add c1 varchar(21844);
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs

二、创建索引的限制

对于varchar列,当varchar长度过长时,会对索引的创建有限制,在MySQL5.6和5.7下的限制行为的表现形式不同。

1、MySQL5.6的限制

在MySQL5.6中,对ecs_payment表的test varchar(1024)列创建索引,并查看创建后的情况:

可以看到test列上建立了一个前缀索引,前缀长度为255字节。在MySQL5.6下,varchar长度超过255字节时是不适合建立索引的,MySQL会自动只建立255字节长的前缀索引,而不是抛出错误。

2、MySQL5.7的限制

在MySQL5.7版本下,varchar列上可建索引的最大长度是3072字节,超过此长度在建索引时会报错:

mysql> alter table t1 add column c4 varchar(1025);
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql> create index i_2 on t1(c4);
ERROR 1071 (42000): Specified key was too long; max key length is 3072 bytes

表t1是utf8字符集。

5.在你之前开发经验中,你还有哪些索引使用规范?

1、 只为用于搜索、排序或分组的列创建索引。

重点关注 where 语句后边的情况

2、 当列中不重复值的个数在总记录条数中的占比很大时,才为列建立索引。

例如手机号、用户 ID、班级等,但是比如一张全校学生表,每条记录是一名学生,where 语句是查询所有’某学校‘的学生,那么其实也不会提高性能。

3、 索引列的类型尽量小。

无论是主键还是索引列都尽量选择小的,如果很大则会占据很大的索引空间。

4、 可以只为索引列前缀创建索引,减少索引占用的存储空间。

alter table single_table add index idx_key1(key1(10))

5、 尽量使用覆盖索引进行查询,以避免回表操作带来的性能损耗。

select key1 from single_table order by key1

6、 为了尽可能的少的让聚簇索引发生页面分裂的情况,建议让主键自增。

7、 定位并删除表中的冗余和重复索引。

冗余索引: 指的是不同的联合索引组合,某一列或者几列字段被多组索引覆盖,一般称这些列存在冗余索引

查询冗余索引SQL

SELECT a.TABLE_SCHEMA, a.TABLE_NAME, a.COLUMN_NAME, 
a.INDEX_NAME AS 'index1', b.INDEX_NAME AS 'index2'
FROM information_schema.STATISTICS a 
JOIN information_schema.STATISTICS b 
ON a.TABLE_SCHEMA = b.TABLE_SCHEMA    
AND a.TABLE_NAME = b.TABLE_NAME 
AND a.SEQ_IN_INDEX = b.SEQ_IN_INDEX   
AND a.COLUMN_NAME = b.COLUMN_NAME 
WHERE a.SEQ_IN_INDEX = 1 AND a.INDEX_NAME <> b.INDEX_NAME

单列索引:(字段 1)

联合索引:(字段 1 字段 2)

重复索引:在一个字段上添加了普通索引、唯一索引、主键等多个索引

6.一般我们是如何查看一条sql语句索引有没有起作用的?

explain执行分析计划

我们只需要注意一个最重要的type 的信息很明显的提现是否用到索引:

type结果值从好到坏依次是:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

一般来说,得保证查询至少达到range级别,最好能达到ref,否则就可能会出现性能问题。

possible_keys:sql所用到的索引

key:显示MySQL实际决定使用的键(索引)。如果没有选择索引,键是NULL

rows: 显示MySQL认为它执行查询时必须检查的行数

3、profiling分析

想要优化一条query sql ,就要清楚这条query的性能瓶颈在哪里,mysql的profiler是一个非常方便的查询诊断分析工具,通过该工具可以获取一条查询在整个执行过程中多种资源的消耗情况,例如内存消耗、I/O消耗和CPU消耗

profile的语法结构:

show profile type ,type …

for query n

limit row_count offset offset

其中type参数可选含义如下:

all:显示所有信息

block io:显示输入输出操作阻塞的数量

context switches:显示自动或非自动context switches的数量

cpu:显示系统和用户CPU使用的时间

ipc:显示信息发送和接受的数量

memory:内存的信息

page faults:显示主要的page faults数量

source:显示函数的名称,并且是那些函数所在文件的名字和行数

swaps:显示swap数量

开启profile

set profiling = 1;

开启query profiler功能之后,MySQL就会自动记录所有执行的query的profile信息

select count(*) from customers1;

通过执行show profiles 命令获取当前系统中保存的多个query的profile的概要信息

针对单个query获取详细的profile信息(根据概要信息中的query_id来获取)

show profile for query 5;

7.有没有了解过为什么添加索引可以加快查询速度?(数据结构B树和B+树)

首先明白为什么索引会增加速度,DB在执行一条Sql语句的时候,默认的方式是根据搜索条件进行全表扫描,遇到匹配条件的就加入搜索结果集合。如果我们对某一字段增加索引,查询时就会先去索引列表中一次定位到特定值的行数,大大减少遍历匹配的行数,所以能明显增加查询的速度。

MySQL官方对于索引的定义为:索引是帮助MySQL高效获取数据的数据结构。即可以理解为:索引是数据结构。

我们知道,数据库查询是数据库最主要的功能之一,我们都希望查询数据的速度尽可能的快,因此数据库系统的设计者会从查询算法的角度进行优化。最基本的查询算法当然是顺序查找,当然这种时间复杂度为O(n)的算法在数据量很大时显然是糟糕的,于是有了二分查找、二叉树查找等。但是二分查找要求被检索数据有序,而二叉树查找只能应用于二叉查找树,但是数据本身的组织结构不可能完全满足各种数据结构。所以,在数据之外,数据库系统还维护者满足特定查找算法的数据结构,这些数据结构以某种方式引用数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。

数据库索引采用B+树的主要原因是 B树在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题。正是为了解决这个问题,B+树应运而生。B+树只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作(或者说效率太低)

8.二者有什么区别?(可以给一个例子让其画出来)

缺页查询,减少io

9.结合树的特点说说,为什么推荐使用自增ID来做索引?为什么不使用红黑树、hash树?

自增主键的插入数据模式,正符合了递增插入的场景。每次插入一条新记录,都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂。

而有业务逻辑的字段做主键,则往往不容易保证有序插入,这样写数据成本相对较高。

@Service 和@Component 注解的差别?

@Component spring基础的注解,被spring管理的组件或bean,用于将对象实例化到Spring容器中

而@Service源码中是包含@Component注解的,也就是说service实现component的功能,但service用于服务层,处理业务逻辑

各种 Restful 请求格式以及各种 http 请求返回码。

API,英文全称Application Programming Interface,翻译为“应用程序编程接口”。就是将一些功能(逻辑)封装成组件,目的是提供一个应用程序接口给其它程序与开发人员访问,而这些访问人员不需要访问源码以及理解内部工作原理就可以直接使用
RESTFUL是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用 XML 格式定义或 JSON 格式定义。最常用的数据格式是JSON。由于JSON能直接被JavaScript读取,所以,使用JSON格式的REST风格的API具有简单、易读、易用的特点。
而http返回状态码常见200.400.500等

1.请说下Springboot相比较Spring来说,你认为的最重要的三个特点是什么?

自动化装配(以规约大于配置思想,做到了很多功能模块的自动化装配)、内嵌容器化(可以独立以jar包方式运行无需外部web容器支持)、开发运维化(基于一些devops思想做了一些endpoint来支持监控管理化)

2.请问springboot的自动化装配技术,哪些技术来源与spring体系,哪些是自己新增的?

SpringBoot中的一些特征:

1、创建独立的 Spring应用。

2、嵌入式 Tomcat、 Jetty、 Undertow容器(无需部署war文件)。

3、提供的 starters 简化构建配置

4、尽可能自动配置 spring应用。 5、提供生产指标,例如指标、健壮检查和外部化配置

6、完全没有代码生成和 XML配置要求

3-N:对上文的模式注解、模块装配、条件装配知识点进行具体有层次的提问

Spirng模式注解装配

@Component作为一种由Spirng容器托管的通用模式组件,任何被@Component标准的组件均为组件扫描的候选对象.类似的,凡是被@Component原标注的注解,如@Service,任何组件标注它时,也将被是做组件扫描的候选对象.

Spring @Enable模块装配

@EnableFeignClients(basePackages = {""})
@EnableDiscoveryClient
@EnableTransactionManagement
@SpringBootApplication(scanBasePackages = {""})
@EnableScheduling
@ServletComponentScan
@EnableAsync(proxyTargetClass = true)

Spirng条件装配

Spring Boot 提供的条件注解

@ConditionalOnBean:当容器里有指定 Bean 的条件下
@ConditionalOnMissingBean:当容器里没有指定 Bean 的情况下
@ConditionalOnSingleCandidate:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean
@ConditionalOnClass:当类路径下有指定类的条件下
@ConditionalOnMissingClass:当类路径下没有指定类的条件下
@ConditionalOnProperty:指定的属性是否有指定的值
@ConditionalOnResource:类路径是否有指定的值
@ConditionalOnExpression:基于 SpEL 表达式作为判断条件
@ConditionalOnJava:基于 Java 版本作为判断条件
@ConditionalOnJndi:在 JNDI 存在的条件下差在指定的位置
@ConditionalOnNotWebApplication:当前项目不是 Web 项目的条件下
@ConditionalOnWebApplication:当前项目是 Web 项 目的条件下

拿模块装配为例子可以继续提问:自定义的模块装配有几种实现方式? 自动化注解方式和selector接口编程的方式这两种比较各有什么特点?我们如何选择? 可以各举个spring中实际实现的例子吗?

关于Java的Selector,其实也没什么好说的。说高级点就是就是多路复用。而多路复用是由于操作系统的支持,才能得以实现。适合实时性要求高的场景

而对于自动化注解则是常用的驱动方式,适合方面是编码以及优化方面的

@Transactional 事务里的事务隔离级别和事务传播机制概念。

定义一个事务受其他并发事务影响程度。事务并发引发的问题。

脏读:一个事务读取到另一个事务修改但还未提交的数据

不可重复读:一个事务读取数据之后,该数据被其他事务修改,此时第一个事务读取到的事务就是错误的(强调修改)

幻读:一个事务读取了某些数据,没提交再读取时数据多了或者少了,类似幻觉(强调增删)

丢失修改:两个事务都读取了数据,其中一个事务修改之后,另一个事务也做了修改,前者的修改丢失

//可以放在 类上 或者 方法上。 
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

    /**
     * 区别于 transactionManager属性
     * 查看 transactionManager
     */
    @AliasFor("transactionManager")
    String value() default "";

    /**
     * 选定特定的 事务管理器 SpringBoot中默认不用配置 可以是beanName或者特定的值
     */
    @AliasFor("value")
    String transactionManager() default "";

    /**
     * 设置传播级别
     */
    Propagation propagation() default Propagation.REQUIRED;

    /**
     * 设置事务隔离级别
     */
    Isolation isolation() default Isolation.DEFAULT;

    /**
     * 事务超时
     */
    int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

    /**
     * 事务只读 默认是可以修改的 如果设置为true只能执行查询相关
     */
    boolean readOnly() default false;

    /**
     * 指定错误回滚 当未抛出该类型的错误或者其子类的错误时 不会回滚 默认是任何错误(异常)都会回滚
     */
    Class<? extends Throwable>[] rollbackFor() default {};

    /**
    * 略 
    */
    String[] rollbackForClassName() default {};

    /**
    * 如果配置这个 如果抛出此异常将不会回滚
    */
    Class<? extends Throwable>[] noRollbackFor() default {};

    /**
    * 略 
    */
    String[] noRollbackForClassName() default {};

}

public enum Isolation {

    /**
     * 使用默认的隔离级别
     * 所有其他级别对应于JDBC隔离级别
     * 具体隔离级别 查看 java.sql.Connection
     * Mysql 默认 RR(REPEATABLE_READ)
     * Oracle 默认 RC(READ_COMMITTED)
     */
    DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),

    /**
     * 未提交读 什么问题都不解决
     * @see java.sql.Connection#TRANSACTION_READ_UNCOMMITTED
     */
    READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),

    /**
     * 提交读 解决脏读
     * @see java.sql.Connection#TRANSACTION_READ_COMMITTED
     */
    READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),

    /**
     * 可重复读 解决脏读和不可重复读 不解决幻读
     */
    REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),

    /**
     * 串行读 解决所有的事务隔离问题
     */
    SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);


    private final int value;


    Isolation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }

}

传播行为

public enum Propagation {

    /**
     * Support a current transaction, create a new one if none exists.
     * Analogous to EJB transaction attribute of the same name.
     * <p>This is the default setting of a transaction annotation.
     * 外围有事务则加入形成同一个事务,外围无事务则新开启,内部事务之间相互独立
     */
    REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

    /**
     * Support a current transaction, execute non-transactionally if none exists.
     * Analogous to EJB transaction attribute of the same name.
     * <p>Note: For transaction managers with transaction synchronization,
     * {@code SUPPORTS} is slightly different from no transaction at all,
     * as it defines a transaction scope that synchronization will apply for.
     * As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)
     * will be shared for the entire specified scope. Note that this depends on
     * the actual synchronization configuration of the transaction manager.
     * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
     * 若外围没有事务则非事务执行,有事务则同 REQUIRED
     */
    SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

    /**
     * Support a current transaction, throw an exception if none exists.
     * Analogous to EJB transaction attribute of the same name.
     * 使用外围事务,若外围无事务则抛出异常
     */
    MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

    /**
     * Create a new transaction, and suspend the current transaction if one exists.
     * Analogous to the EJB transaction attribute of the same name.
     * <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
     * on all transaction managers. This in particular applies to
     * {@link org.springframework.transaction.jta.JtaTransactionManager},
     * which requires the {@code javax.transaction.TransactionManager} to be
     * made available to it (which is server-specific in standard Java EE).
     * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
     * 外围有无事务都开启新事务,相互独立,且与外围事务相互独立开 如果当前有事务,就把当前的事务挂起
     */
    REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

    /**
     * Execute non-transactionally, suspend the current transaction if one exists.
     * Analogous to EJB transaction attribute of the same name.
     * <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
     * on all transaction managers. This in particular applies to
     * {@link org.springframework.transaction.jta.JtaTransactionManager},
     * which requires the {@code javax.transaction.TransactionManager} to be
     * made available to it (which is server-specific in standard Java EE).
     * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
     * 非事务执行,若外围存在事务则挂起该事务
     */
    NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

    /**
     * Execute non-transactionally, throw an exception if a transaction exists.
     * Analogous to EJB transaction attribute of the same name.
     * 非事务执行,当外围有事务则抛出异常
     */
    NEVER(TransactionDefinition.PROPAGATION_NEVER),

    /**
     * Execute within a nested transaction if a current transaction exists,
     * behave like {@code REQUIRED} otherwise. There is no analogous feature in EJB.
     * <p>Note: Actual creation of a nested transaction will only work on specific
     * transaction managers. Out of the box, this only applies to the JDBC
     * DataSourceTransactionManager. Some JTA providers might support nested
     * transactions as well.
     * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
     * 外围无事务,则同 REQUIRED 内部开启新事务相互独立。外围有事务,则内部事务是其子事务,主事务回滚则子事务全部回滚,子事务回滚不影响其他子事务和主事务
     */
    NESTED(TransactionDefinition.PROPAGATION_NESTED);


    private final int value;


    Propagation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }

}