05 MyBatis之表关系的声明+事务+SqlSession三件套的作用域

news2024/11/16 3:46:56
  • MyBatis 支持一对一,一对多,多对多查询。
  • XML 文件和注解都能实现关系的操作。
  • 多对多实质就是一对多

1. 表关系的维护

1.1 @One一对一

一对一查询和多表(两表)查询很相似, 都能查询两表的全部属性
区别是一对一可以在对象中嵌套对象, 呈现包含关系; 多表呈现的是平级关系

每个Article对应一个ArticleDetail:
在这里插入图片描述
实体类代码:

@Data
public class ArticleDetail {
private Integer id;
private Integer articleId;
private String content;
}
@Data
public class Article {
private Integer id;
private Integer userId;
private String title;
private String summary;
private Integer readCount;
private LocalDateTime createTime;
private LocalDateTime updateTime;
//每个Article都包含一个ArticleDetail
private ArticleDetail articleDetail;
}

1.1.1 创建ArticleOneToOneMapper查询接口:

	public interface ArticleOneToOneMapper {

		//查询文章详情表
		/*
		*该方法名为queryContent,传入参数id执行SQL,并返回一个ArticleDetail对象
		*/
	@Select("""
		select id,article_id,content 
		from article_detail
		where article_id = #{articleId}
		""")
	@Results({
		@Result(id = true, column = "id", property = "id"),
		@Result(column = "article_id", property = "articleId"),
		@Result(column = "content", property = "content")
	})
	ArticleDetail queryContent(Integer articleId);

	//查询文章属性+文章详情
	@Select("""
		select id,
					user_id,
					title,
					summary,
					read_count,
					create_time,
					update_time
		from article
		where id = #{id}
		""")

	@Results({
		@Result(id = true, column = "id", property = "id"),
		@Result(column = "user_id", property = "userId"),
		@Result(column = "read_count", property = "readCount"),
		@Result(column = "create_time", property = "createTime"),
		@Result(column = "update_time", property = "updateTime"),
		/*
		*根据@One注解调用指定的SQL方法queryContent,
		*调用queryContent需要一个参数articleId, 由column="id"提供
		*queryContent执行完毕后返回一个ArticleDetail类型的对象
		*该对象注入到property="articleDetail"这个字段中
		*/
		@Result(column = "id", property = "articleDetail",
			one = @One(select =
		"com.sunsplanter.mapper.ArticleOneToOneMapper.queryContent",
		fetchType = FetchType.LAZY))
	})
	Article queryAllArticle(Integer id);
	}
}

1.1.2 单元测试:

@SpringBootTest
public class OneToOneTest {

@Autowired
private ArticleOneToOneMapper articleOneToOneMapper;

@Test
void testOne() {
Article article = articleOneToOneMapper.queryAllArticle(1);
System.out.println("article = " + article);
	}
}

1.1.3 执行结果

第一步先查询第一层Article

第二步查询嵌套的ArticleDetail

1.2 @Many一对多

每个Article 对应多个 comment :

1.2.1 编写实体类:

@Data
public class Comment {
private Integer id;
private Integer articleId;
private String content;
}
@Data
public class Article {
private Integer id;
private Integer userId;
private String title;
private String summary;
private Integer readCount;
private LocalDateTime createTime;
private LocalDateTime updateTime;
//一个文章对应多个评论,多个评论用类型为comment 的对象装载
private List<CommentPO> comments; 
}

1.2.2 ArticleOneToManyMapper接口

	public interface ArticleOneToManyMapper {
		@Select("""
			select id,article_id,content from comment
			where article_id = #{articleId}
			""")
	@Results(id="CommentMapper",value = {
		@Result(id = true, column = "id", property = "id"),
		@Result(column = "article_id", property = "articleId"),
		@Result(column = "content", property = "content")
		})
	List<Comment> queryComments(Integer articleId);
	
	@Select("""
		select id, user_id,title,summary,
			read_count,create_time,update_time
		from article
		where id = #{id}
		""")
	@Results(id="ArticleBaseMapper",value={
		@Result(id = true, column = "id", property = "id"),
		@Result(column = "user_id", property = "userId"),
		@Result(column = "read_count", property = "readCount"),
		@Result(column = "create_time", property = "createTime"),
		@Result(column = "update_time", property = "updateTime"),
		@Result(column = "id", property = "comments",
			many = @Many(select =
"com.mapper.ArticleOneToManyMapper.queryComments", fetchType =
FetchType.LAZY))
	})
	ArticleEntity queryArticleAndComments(Integer id);
}

1.2.3 单元测试

@SpringBootTest
public class OneToManyTest {

@Autowired
private ArticleOneToManyMapper articleOneToManyMapper;

@Test
void testOnetoMany() {
	Article article = articleOneToManyMapper.queryArticleAndComments(1);
	System.out.println("ArticleEntity = " + article);
	}
}

2 事务

  • 事务分为全局事务与本地事务

  • 本地事务是特定于资源的,例如与 JDBC 连接关联的事务。本地事务更容易使用,但有一个显著的缺点:它们不能跨多个事务资源工作。比如在方法中处理连接多个数据库的事务,本地事务是无效的。

  • Spring 解决了全局和本地事务的缺点。它允许应用程序开发人员在任何环境中使用一致的编程模型。只需编写一次代码,就可以从不同环境中的不同事务管理策略中获益。

  • Spring 框架同时提供声明式和编程式事务管理。

  • 推荐声明式事务管理。

  • Spring Framework 的声明式事务管理通过 **Spring AOP 的环绕通知(TransactionInterceptor)**实现.

  • 事务控制的属性:
    Propagation : 传播行为。代码可以继续在现有事务中运行(常见情况),也可以暂停现有事务并创建新事务
    Isolation: 隔离级别。此事务与其他事务的工作隔离的程度。例如,这个事务能看到其他事务未提交的写吗?
    Timeout 超时时间:该事务在超时和被底层事务基础结构自动回滚之前运行的时间。
    Read-only 只读状态:当代码读取但不修改数据时,可以使用只读事务。

  • 声明式事务的方式
    a. XML 配置文件:全局配置
    b. @Transactional 注解驱动 :和代码一起提供,比较直观。和代码的耦合比较高。【建议只使用@Transactional 注释具体类(以及具体类的方法),而不是注释接口。当然,可以将@Transactional 注解放在接口(或接口方法)上,但这只有在使用基于接口的代理时才能正常工作】

  • @Transaction注解的生效范围:
    @Transactional注解 仅生效于public方法.
    在受保护的、私有的或包可见的方法上使用该注解,虽然不会引发错误,但事务不生效。
    如果需要受保护的、私有的方法具有事务考虑使用 AspectJ。而不是基于代理的机制。

2.1 准备事务的演示环境

目标: 两张表:文章表和文章详情表, 使用事务, 仅当两表都被插入成功时才提交事务.

在这里插入图片描述

2.1.1

Spring Initializr 新建一个模块, 3.0.3版本,依赖如下:
在这里插入图片描述

2.1.2 建表及实体类

在这里插入图片描述
本次只是简单演示插入事务, 无需一对一关系, 故Article中不嵌套ArticleDetail对象

import lombok.Data;

import java.time.LocalDateTime;

@Data
public class Article {
    private Integer id;
    private Integer userId;
    private String title;
    private String summary;
    private Integer readCount;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}
import lombok.Data;

@Data
public class ArticleDetail {
    private Integer id;
    private Integer articleId;
    private String content;
}

2.1.3 创建ArticleMapper接口(@Option注解)

import com.sunsplanter.trans.pojo.Article;
import com.sunsplanter.trans.pojo.ArticleDetail;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Options;

public interface ArticleMapper {

    //添加文章的属性 Article
    /*
    *调用 insertArticle 方法并传入一个 Article 对象时,MyBatis 会执行插入操作。
    *插入完成后,如果表有自动生成的主键,该主键的值会被设置回传入的 Article 对象的 id 属性中。
     */
    @Insert("""
            insert into
            article(user_id,title,summary,read_count,create_time,update_time) \
            values(#{userId},#{title},#{summary},#{readCount},#{createTime},#{updateTime})
            """)
    /*
    *useGeneratedKeys = true 表示 MyBatis 将会使用 JDBC 的 getGeneratedKeys 方法来获取数据库自动生成的主键。
    *keyProperty指定了 MyBatis 应该将获取到的主键值设置回哪个属性上,在这里是 article 对象的 id 属性。
    *keyColumn = "id" 表明主键在数据库表中的列名是 id。
     */
    @Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id")
    int insertArticle(Article article);

    //添加文章的内容 ArticleDetail
    @Insert("""
            insert into article_detail(article_id,content)
            values(#{articleId},#{content})
            """)
    int insertArticleContent(ArticleDetail detail);
}

2.1.3 创建 ArticleService 接口,声明发布文章的方法, 并实现其

import com.sunsplanter.trans.pojo.Article;

public interface ArticleService {
    boolean postNewArticle(Article article, String content);
}
import com.sunsplanter.trans.mapper.ArticleMapper;
import com.sunsplanter.trans.pojo.Article;
import com.sunsplanter.trans.pojo.ArticleDetail;
import com.sunsplanter.trans.service.ArticleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ArticleServiceImpl implements ArticleService {

    @Autowired
    private ArticleMapper articleMapper;

    @Override
    public boolean postNewArticle(Article article, String content) {
        //新增文章,无需前置操作
        articleMapper.insertArticle(article);

        //新增文章内容, 必须先知道往哪个文章(id)插入内容
        ArticleDetail detail = new ArticleDetail();
        detail.setArticleId(article.getId());
        detail.setContent(content);
        articleMapper.insertArticleContent(detail);

        return true;
    }
}

2.14 配置文件及启动类

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/study
    username: root
    password: ???
    hikari:
      auto-commit: true
      maximum-pool-size: 10
      connection-test-query: select 1

mybatis:
  configuration:
      log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan(basePackages = "com.sunsplanter.trans.mapper")
@SpringBootApplication
public class TransApplication {
    public static void main(String[] args) {
        SpringApplication.run(TransApplication.class, args);
    }
}

2.1.5 单元测试

   @Autowired
    private ArticleService articleService;

    @Test
    void testAddArticle() {
        Article article = new Article();
        article.setTitle("Spring 事务管理");
        article.setSummary("Spring 事务属性,事务实现");
        article.setUserId(2001);
        article.setReadCount(0);
        article.setCreateTime(LocalDateTime.now());
        article.setUpdateTime(LocalDateTime.now());
        boolean add = articleService.postNewArticle(article, "Spring 统一事务管理。事务管理器管理本地事务");
        System.out.println("add = " + add);
    }

尽管两张表都插入成功了, 但不是用事务机制插入的, 有失败(例如只插入了一张表)的风险, 从控制台输出也可以看出提示未开启同步事务:
在这里插入图片描述

2.2 增加事务注解

  • 前述提到, 最好在具体的类或具体的方法上使用@Transactional注解
  • Transactional内有rollback属性, 声明抛出何种异常时回滚事务,默认值是运行时异常
  • .

2.2.1 事务回滚规则

  • 默认情况下,抛出RuntimeException时实例或子类时回滚事务

  • 也可以在属性中指定何时回滚

  • Error 也会回滚事务

  • 已检查异常不会回滚。默认提交事务

  • @Transactional 注解的属性控制回滚
     rollbackFor
     noRollbackFor
     rollbackForClassName
     noRollbackForClassName

2.2.2 开启事务所需注解

我们在 ArticleserviceImpl 上增加@Transaction注解(默认运行时异常回滚)
并且在其内定义各种应该抛出的异常, 例如阅读量的异常:

if( article.getReadCount() < 1) {
   //Spring发现抛出的是运行时异常, 会执行回滚
	throw new RuntimeException("已读数量不能 < 1 ");
}
  1. 启动类上增加@EnableTransactionManagement (该注解默认存在, 即使不加也可以)

2.3 无效的事务声明

2.3.1 方法调方法

  • 首先, 仅当public修饰的类或方法才能令事务生效

  • 然而,并不是所有的public修饰的类或方法都能令事务生效

    在 Spring 中,事务是通过代理(Proxy)来实现的。在一个 Spring 管理的 bean 中调用另一个方法时,这种调用的性质(内部调用 vs 外部调用)会影响事务的行为。

外部调用 vs 内部调用
外部调用:

当一个方法从外部类(比如另一个 Spring Bean)被调用时,Spring 的代理机制可以拦截这个调用,确保事务相关的切面(例如 @Transactional 注解)被应用。这是事务正常工作的场景。
内部调用:

当一个方法在同一个类中直接调用另一个方法时,这被称为内部调用。在这种情况下,由于调用发生在代理之内,Spring 的代理机制并不会介入,因此不会处理事务相关的切面。即一个方法内部调用另一个带有 @Transactional 注解的方法,事务注解不会生效。

在上述例子中, ArticleService接口中增加方法 managerArticles, 并在impl中实现其:

//接口中增加方法
boolean managerArticle(String action,ArticlePO article,String content);
//实现类方法:
@Override
public boolean managerArticle(String action, ArticlePO article, String content) {
	return postNewArticle(article,content);
}

其后, 在测试方法中:

//原本:boolean add = articleService.postNewArticle("add",article, "222 Spring 统一事务管理。事务管理器管理本地事务")
boolean add = articleService.managerArticle("add",article, "222 Spring 统一事务管理。事务管理器管理本地事务")

这样不会执行事务.
通过方法调方法的方式, 事务不会执行.

2.3.1 不同线程之间不会同步事务

在 Spring 中,事务管理通常是通过线程本地存储(Thread Local)来实现的,这意味着事务信息是与执行它的线程绑定的。当在一个新的线程中运行代码时,这个新线程不会自动继承原始线程的事务上下文。

  • 同一线程中的事务
    在同一个线程中,事务的边界被定义为方法调用的开始和结束。在这个边界内,所有的数据库操作都是在同一个事务上下文中执行的。
  • 新线程中的事务
    当在一个新的线程中执行代码时,这个线程将不会有访问原始线程事务上下文的能力。
    任何在新线程中创建的事务将是全新的,与原始线程的事务完全隔离。
    这意味着在新线程中执行的操作要么自己管理事务,要么不在事务的控制之下。

3 SqlSession三件套的生命周期及作用域

  • SqlSessionFactoryBuilder

SqlSessionFactory一旦被创建,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

  • SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

  • SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式

mybatis中的dao层一般也叫做mapper层,即UserDao类也可以叫做UserMapper类

以后只要专注于接口和mapper.xml文件,实现类和方法都可以动态生成

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1421420.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kotlin快速入门系列9

Kotlin对象表达式和对象声明 对象表达式 有时&#xff0c;我们想要创建一个对当前类有些许修改的对象同时又不想重新声明一个子类。如果是Java&#xff0c;可以用匿名内部类的概念来解决这个问题。kotlin的对象表达式和对象声明就是为了实现这一点(创建一个对某个类做了轻微改…

Java 开发环境 全套包含IDEA

一、JDK配置 1.下载 JDK Builds from Oracle 去这边下载open JDK 2.JDK环境变量配置 按win&#xff0c;打开设置 找到环境变量编辑 这边输入的是你下载的那个JDK的bin的路径 检擦配置是否正确在cmd中输入 二、IDEA安装配置 1.下载&#xff08;社区版&#xff09; JetBrai…

干货 | 大模型在图数据分析、推荐系统和生物科学中的综合应用

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 图机器学习、推荐系统与大语言模型的融合正成为新的前沿热点。图机器学习通过利用图结构数据&#xff0c;能够有效地捕捉和分析复杂关系和模式。同时&#xff0c;推荐系统正逐步成为我们日常生活的一部分&#…

华为——NGFW Module安装在集群交换机上,二层双机负载分担部署,交换机重定向引流

NGFW Module安装在集群交换机上&#xff0c;二层双机负载分担部署&#xff0c;交换机重定向引流 业务需求 如图1所示&#xff0c;两台交换机集群组网&#xff0c;两块NGFW Module分别安装在两台交换机的1号槽位组成双机负载分担组网。NGFW Module工作在二层&#xff0c;也就是…

走进水稻种植教学基地可视化:科技与农业知识的完美结合

随着科技的不断发展&#xff0c;农业领域也在不断创新和进步。水稻种植教学基地可视化系统是一种基于现代信息技术手段的教学方式&#xff0c;通过虚拟现实、3D建模等技术&#xff0c;将水稻种植的全过程进行模拟和展示。这种教学方式打破了传统农业教学的局限性&#xff0c;使…

腾讯云部署vue+node项目

文章目录 一、安装宝塔二、vue项目部署三、node项目部署 前言: 关于项目部署,一开始也是找了很多资料,费了点时间,所以记录一下。希望能对各位有所帮助。 一、安装宝塔 1.首先在控制台,进入云服务器的终端界面 2.输入命令和密码获取权限,并且安装宝塔界面 yum install -y w…

关于在Tkinter + Pillow图片叠加中出现的问题

这段时间我一直在尝试对多图层图片进行一个叠加的操作&#xff0c;想用tkinter实现出来&#xff0c;先看错误 这里我其实已经选择了图片&#xff0c;但是发现是ValueError&#xff0c;我尝试断点检测但是也无动于衷&#xff0c;因为设置变量检测的时候发现变量并没有错误&…

Opencv——图片卷积

图像滤波是尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可缺少的操作,其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。 线性滤波是图像处理最基本的方法,它允许我们对图像进行处理,产生很多不同的效果。首先,我们需要一个二…

大数据学习之Redis,十大数据类型的具体应用(三)

目录 3.7 Redis位图&#xff08;bitmap&#xff09; 概念 需求 是什么 说明 能干嘛? 基本命令 3.7 Redis位图&#xff08;bitmap&#xff09; 概念 由0和1状态表现的二进制位的bit数组 需求 用户是否登陆过&#xff1f;Y / N 广告是否被点击过&#xff1f; 钉钉打…

【机器学习】常见算法详解第2篇:KNN之kd树介绍(已分享,附代码)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论机器学习算法相关知识。机器学习算法文章笔记以算法、案例为驱动的学习&#xff0c;伴随浅显易懂的数学知识&#xff0c;让大家掌握机器学习常见算法原理&#xff0c;应用Scikit-learn实现机器学习算法的应用&#xff0…

外汇监管牌照解析:确保交易安全与合规性

外汇交易中&#xff0c;资金安全与平台监管是大家最关心的话题。监管是评估外汇经纪商是否值得信赖、是否具备相关资质的关键依据&#xff0c;因此选择一家拥有海外合法监管的经济商至关重要。 那么&#xff0c;今天我们就来聊聊全球权威的几大监管机构 — FCA、ASIC、NFA、FSA…

linux -- 内存管理 -- 页面分配器

linux内存管理 为什么要了解linux内存管理 分配并使用内存&#xff0c;是内核程序与驱动程序中非常重要的一环。内存分配函数都依赖于内核中一个非常复杂而重要的组件 - 内存管理。 linux驱动程序不可避免要与内核中的内存管理模块打交道。 linux内存管理可以总体上分为两大…

Tensorflow2.0笔记 - Tensor的限值clip操作

本笔记主要记录使用maximum/minimum,clip_by_value和clip_by_norm来进行张量值的限值操作。 import tensorflow as tf import numpy as nptf.__version__#maximum/minimumz做上下界的限值 tensor tf.random.shuffle(tf.range(10)) print(tensor)#maximum(x, y, nameNone) #对…

数据可视化 pycharts实现地理数据可视化(全球地图)

自用版 紧急整理一点可能要用的可视化代码&#xff0c;略粗糙 以后有机会再改 requirements&#xff1a; python3.6及以上pycharts1.9 数据格式为&#xff1a; 运行结果为&#xff1a; import pandas as pd from pyecharts.charts import Map, Timeline from pyecharts im…

Unity3D正则表达式的使用

系列文章目录 unity工具 文章目录 系列文章目录前言一、匹配正整数的使用方法1-1、代码如下1-2、结果如下 二、匹配大写字母2-1、代码如下1-2、结果如下 三、Regex类3-1、Match&#xff08;&#xff09;3-2、Matches()3-3、IsMatch&#xff08;&#xff09; 四、定义正则表达式…

TypeScript 学习笔记(Day2)

「写在前面」 本文为 b 站黑马程序员 TypeScript 教程的学习笔记。本着自己学习、分享他人的态度&#xff0c;分享学习笔记&#xff0c;希望能对大家有所帮助。推荐先按顺序阅读往期内容&#xff1a; 1. TypeScript 学习笔记&#xff08;Day1&#xff09; 目录 3 TypeScript 常…

Java-并发高频面试题

1.说一下你对Java内存模型&#xff08;JMM&#xff09;的理解&#xff1f; 其实java内存模型是一种抽象的模型&#xff0c;具体来看可以分为工作内存和主内存。 JMM规定所有的变量都会存储再主内存当中&#xff0c;再操作的时候需要从主内存中复制一份到本地内存&#xff08;c…

面试题:MySQL数据库索引失效的10连问你学会了吗?

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通Golang》…

【2024.1.30练习】李白打酒加强版(25分)

题目描述 题目思路 在最多数据的情况下&#xff0c;有100个店100朵花&#xff0c;总情况为的天文数字&#xff0c;暴力枚举已经不可能实现&#xff0c;考虑使用动态规划解决问题。最后遇到的一定是花&#xff0c;所以思路更倾向于倒推。 建立二维数组&#xff0c;容易联想到为…

vxe-table从2.0升级到3.0,vxe-table-plugin-virtual-tree虚拟滚动失效

问题&#xff1a;系统一直使用的vxe-table2.0&#xff0c;vxe-table2.0不支持树的虚拟滚动&#xff0c;为了解决这个问题&#xff0c;引入了vxe-table-plugin-virtual-tree插件&#xff0c;现在系统vxe-table升级3.0&#xff0c;vxe-table-plugin-virtual-tree的虚拟滚动失效了…