基于MyBatis-plus的SpringBoot开发

news2025/1/12 20:48:20

目录

一、SpringBoot整合mybatis

二、SpringBoot整合mybatis-plus

1、什么是mybatis-plus?

2、mybatis-plus的特性

3、mybatis-plus的使用

(1)编写注解配置实体类与关系表映射关系 

(2)mapper层

(3)test层

        新增:

        修改ID:

        修改:

        查询ID:

        查询ID:

        查询count:

        查询list:

        分页查询:

        查询delete:

        基于条件查询 Account 表中的记录:

        模拟动态查询1:

        模拟动态查询2:

        投影查询-字段查询:

        逻辑删除:


一、SpringBoot整合mybatis

        主要是融合mybatis对数据库操作的方式和SpringBoot框架进行整合,大概的框架结构为:

        首先是在pom.xml中导入mybatis坐标

    <dependencies>
        <!--mybatis坐标-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

        然后是创建实体类pojo,再创建mapper层,用来定义方法:

// @Mapper //注册注入一个mapper
public interface AccountMapper {
    @Select("select * from account")
    public List<Account> findAll();

    public List<Account> find();
}

        通过@MapperScan实现注册注入多个mapper(以包为单位),指定一个或多个包路径,Spring 会扫描这些包中的所有 Mapper 接口,并将它们注册为 Spring 容器中的 Bean。可以避免在每个 Mapper 接口上单独添加 @Mapper 注解,简化了 MyBatis 与 Spring 的集成配置。

        @Mapper是标识某个接口为 MyBatis 的 Mapper,适合单个接口的配置,通常用于不使用 @MapperScan 的情况下。

        然后在resources底下创建mappers包,里面创建AccountMapper.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


<mapper namespace="com.apesource.mybatis_springboot.mapper.AccountMapper">
    <select id="find" resultType="com.apesource.mybatis_springboot.pojo.Account">
        select * from account;
    </select>
</mapper>

        其中namespace是将这个xml文件映射到目标路径的接口中去,其中的对应接口中的find()方法完成数据库操作。

        yml文件中的配置为:

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/homework?serverTimezone=GMT
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  mapper-locations: mappers/*.xml

        其中配置了数据库的相关信息连接和mapper-locations: mappers/*.xml,指定 MyBatis 的 Mapper XML 文件的位置模式。mappers/*.xml 表示 Spring Boot 会在类路径下的 mappers 目录中查找所有以 .xml 结尾的文件,这些文件定义了 MyBatis 与数据库之间的 SQL 映射关系。

二、SpringBoot整合mybatis-plus

1、什么是mybatis-plus?

        MyBatis-Plus 是一个基于 MyBatis 的增强工具,它旨在简化 MyBatis 的开发过程,提供了很多开箱即用的功能,使得开发者可以更加高效地进行持久层开发。MyBatis-Plus 不改变 MyBatis 原有的 API 和功能,而是在其基础上提供了更丰富的功能和自动化工具。

        MyBatis-Plus 提供了一套通用的 CRUD 接口,只需要简单地继承这些接口就可以获得常用的 CRUD 操作方法,如 insert、delete、update 和 select,减少了大量重复的代码。

        MyBatis-Plus 还可以实现自动填充、逻辑删除、分页插件、代码生成器等功能。

2、mybatis-plus的特性

3、mybatis-plus的使用

        首先是导入坐标,mp坐标添加后,mybatis坐标就可以移除。

         <dependency>
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-boot-starter</artifactId>
             <version>3.1.1</version>
         </dependency>

        然后是实体类Account:

@TableName("account")
public class Account {
    @TableId(value = "aid",type = IdType.AUTO)
    private int aid;
    @TableField("aname")
    private String aname;
    @TableField("amoney")
    private int amoney;
    @TableField("aage")
    private int aage;
    @TableField("ahobby")
    private String ahobby;

    // 构造方法
    // set/get方法 toString方法...
}
(1)编写注解配置实体类与关系表映射关系 

        @TableName:

        用途:指定该类映射的数据库表名为 account。

        作用:@TableName 注解告诉 MyBatis-Plus,这个实体类 Account 对应的数据库表名是 account。这个表名可能与类名不同,注解通过明确表名来建立映射关系

        @TableId:

        用途:指定主键字段 aid 的映射关系和主键生成策略,默认雪花算法。

        属性说明:

  • value = "aid": 表示这个字段在数据库表中的列名是 aid。
  • type = IdType.AUTO: 指定主键生成策略为自增 (AUTO),即数据库会自动生成主键值(通常是自增的整数)。

        

        @TableField:

        用途:指定字段 aname 在数据库表中的列名,主要是用于害怕字段名与属性名称不相同,用于对应映射关系

        作用:@TableField("aname") 注解将类中的 aname 字段映射到数据库表中的 a_name 列。MyBatis-Plus 会根据这个映射关系进行数据库操作

(2)mapper层

        接口继承了BaseMapper接口:

public interface AccountMapper extends BaseMapper<Account> {

    @Select("select * from account")
    public List<Account> findAll();

    public List<Account> find();

}

        BaseMapper通常是一个通用的MyBatis接口,提供了基本的CRUD操作。AccountMapper继承了它,因此可以使用BaseMapper中定义的所有基本数据库操作方法。

        

        config层定了一个叫MyBatisPlusConfig的类:

@Configuration
public class MyBatisPlusConfig {

    //注入mp拦截器
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        //1.实例化拦截器
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        //2.分页拦截器
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }

}

        MybatisPlusInterceptor创建了一个 MybatisPlusInterceptor 的实例。这个拦截器是MyBatis Plus提供的,用于处理SQL执行过程中的各种拦截任务。

        (PaginationInnerInterceptor)添加到 MybatisPlusInterceptor 中。分页拦截器用于处理分页查询的SQL语句,它会自动将分页参数(如页码、页大小)应用到查询语句中。

        return mybatisPlusInterceptor是将配置好的 MybatisPlusInterceptor 实例返回,并将其注册为Spring容器中的一个Bean。这意味着MyBatis Plus将使用这个配置的拦截器来处理SQL语句。

(3)test层

        测试层中去写相关方法:

        新增:
    @Test
    public void show1(){
        Account account = new Account("郝喵喵", 2000);
        int row = mapper.insert(account);
        System.out.println("主键回填id:"+account.getAid());
        System.out.println("影响行数:"+row);
    }

        新建实体类对象,调用CRUD 接口的方法insert插入数据库,返回受影响行数。并且mubatis-plus中的方法实现了自动主键回填。

        修改ID:
    @Test
    public void test02()throws Exception{
        Account account = new Account(3,"郝苗苗",3000);
        int row =  mapper.updateById(account);
        System.out.println("影响行数:"+row);
    }

        同样调用了CRUD接口中的方法。

        修改:
    @Test
    public void test03()throws Exception{
        //1.数据
        Account account = new Account();
        account.setAhobby("打麻将");
        account.setAage(18);
        //2.修改条件
        QueryWrapper<Account> wrapper = new QueryWrapper<Account>();
        wrapper.eq("aname","郝苗苗");
        mapper.update(account,wrapper);
    }

        其中的创建一个 QueryWrapper 对象,用于指定更新操作的条件。wrapper.eq("aname","郝苗苗");设置条件,表示选择 aname 列的值等于 "郝苗苗" 的记录。这是一个等值匹配条件。mapper.update(account, wrapper); 调用 mapper 对象的 update 方法,相当于CRUD接口中的方法。

        查询ID:
    @Test
    public void test04()throws Exception{
        Account account = mapper.selectById(3);
        System.out.println(account);
    }

        调用的CRUD接口中selectById的方法。

        查询ID:
    @Test
    public void test05()throws Exception{
        List<Account> list = mapper.selectBatchIds(Arrays.asList(5,1,3));
        for (int i = 0; i < list.size(); i++) {
            Account account =  list.get(i);
            System.out.println(account);
        }
    }

        调用了 mapper 对象的 CRUD接口中 selectBatchIds 方法,从数据库中批量查询ID为 5、1、3 的 Account 记录。

        

        查询count:
    @Test
    public void test06()throws Exception{
        int count = mapper.selectCount(null);
        System.out.println(count);
    }

        查询list:
    public void test07()throws Exception{
        QueryWrapper<Account> queryWrapper = new QueryWrapper();
//        queryWrapper.eq("ahobby","打麻将");
//        queryWrapper.eq("aage","18");
        queryWrapper.eq("ahobby","打麻将").or().eq("aage","18");

        List<Account> list = mapper.selectList(queryWrapper);
        for (int i = 0; i < list.size(); i++) {
            Account account =  list.get(i);
            System.out.println(account);
        }
    }

        可以使用上面注释掉的,分别构建查询条件,或者是下面的用or()方法连接的查新条件,表示只需要满足其中一个条件,而不必同时满足两个条件。

        分页查询:
    @Test
    public void test08()throws Exception{

        //1.定义分页规则
        Page<Account> page = new Page<Account>();
        page.setSize(3);//每页记录数
        page.setCurrent(2);//当前页码

        //2.查询条件(可选)
        QueryWrapper queryWrapper = new QueryWrapper();

        IPage<Account> iPage = mapper.selectPage(page,null);

        List<Account> list = iPage.getRecords();//分页结果
        System.out.println("总记录数:"+iPage.getTotal());
        System.out.println("总记页数:"+iPage.getPages());

        for (int i = 0; i < list.size(); i++) {
            Account account =  list.get(i);
            System.out.println(account);
        }
    }

        这个是基于mybatis-plus的分页使用,注意page.setCurrent(2);当前页码从1开始,分页需要配置插件。

        首先通过创建Page对象定义分页的规则,page.setSize(3); 设置每页显示的记录数为 3,page.setCurrent(2); 设置当前页码为 2。这意味着查询结果将从第二页开始,每页包含3条记录。

        这里调用selectPage方法,传入分页规则 page 和查询条件(这里传入 null 表示没有额外条件)。

        最后通过.getRecords()方法从 iPage 对象中提取出分页查询的结果,即当前页的 Account 记录列表,输出打印。

        查询delete:
    @Test
    public void test09()throws Exception{
        mapper.deleteById(4);
    }

        基于条件查询 Account 表中的记录:
    @Test
    public void show1(){
        //1.查询条件
        // LambdaQueryWrapper 是 MyBatis-Plus 提供的一个查询构建器,用于构建查询条件。这里创建了一个针对 Account 实体类的查询构建器实例。
        LambdaQueryWrapper<Account> lambdaQueryWrapper =  new LambdaQueryWrapper<Account>();
        // 查询大于18的account对象
        lambdaQueryWrapper.gt(Account::getAage,18);
        //2.查询
        List<Account> list =  mapper.selectList(lambdaQueryWrapper);
        for (int i = 0; i < list.size(); i++) {
            Account student =  list.get(i);
            System.out.println(student);
        }
    }

        创建一个 LambdaQueryWrapper 对象,用于构建SQL查询的条件。LambdaQueryWrapper 是 MyBatis-Plus 提供的一个查询构建器,主要用于在查询时构建更安全和灵活的条件,特别适用于避免字符串拼接带来的潜在问题。使用 gt 方法指定查询条件,这里的 gt 表示 "greater than"(大于)

        Account::getAage 指定 Account 实体类中的 aage 属性,意味着我们要查询 aage 大于 18 的记录。

        模拟动态查询1:
@Test
    public void show2(){
        //1.前端发送来的数据
        Integer num1 = null;
        Integer num2 = 30;

        //1.查询条件
        LambdaQueryWrapper<Account> lambdaQueryWrapper =  new LambdaQueryWrapper<Account>();
        //2.判断
        if(null != num2){
            lambdaQueryWrapper.lt(Account::getAage,num2);
        }
        if(null != num1){
            lambdaQueryWrapper.gt(Account::getAage,num1);
        }
        //3.查询
        List<Account> list =  mapper.selectList(lambdaQueryWrapper);
        for (int i = 0; i < list.size(); i++) {
            Account student =  list.get(i);
            System.out.println(student);
        }
    }

        num1 为 null,表示没有传递下限的条件,num2 为 30,表示查询的上限年龄为30,即查询小于30岁人。

        模拟动态查询2:
@Test
    public void show3(){
        //1.前端发送来的数据
        Integer num1 = null;
        Integer num2 = 30;

        //1.查询条件
        LambdaQueryWrapper<Account> lambdaQueryWrapper =  new LambdaQueryWrapper<Account>();
        //2.判断
        lambdaQueryWrapper.lt(null != num2,Account::getAage,num2);
        lambdaQueryWrapper.gt(null != num1,Account::getAage,num1);
        //3.查询
        List<Account> list =  mapper.selectList(null);
        for (int i = 0; i < list.size(); i++) {
            Account student =  list.get(i);
            System.out.println(student);
        }
    }

        其他均一样,第一个参数 null != num2 是一个布尔值,用于控制这个条件是否会被加入到查询中。因为 num2 不为 null,所以后面的这个条件会被应用。

        第一个是判断条件,条件成立后,从Acount中取getAage属性,并把第三个参数作为条件。

        投影查询-字段查询:
@Test
    public void show4() {
        // 查询只包含的条件的对象
        //1.条件
        LambdaQueryWrapper<Account> lambdaQueryWrapper  = new LambdaQueryWrapper<Account>();
        // 这行代码使用 select() 方法指定只查询 Account 表中的 Aname(姓名)和 Ahobby(爱好)两个字段。
        lambdaQueryWrapper.select(Account::getAname,Account::getAhobby);
        //2.查询
        List<Account> list =  mapper.selectList(lambdaQueryWrapper);
        //4.遍历
        for (int i = 0; i < list.size(); i++) {
            Account student =  list.get(i);
            System.out.println(student);
        }
    }

        这行代码使用 select() 方法指定只查询 Account 表中的 Aname(姓名)和 Ahobby(爱好)两个字段,只查询拥有Aname和Ahobby的对象。

        逻辑删除:
  • 物理删除:业务数据从数据库中丢弃,执行的是delete操作。
  • 逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,数据保留在数据库中,执行的是update操作。

        实现步骤:

        步骤1:修改数据库表添加`deleted`列,比如`0`代表正常,`1`代表删除,可以在添加列的同时设置其默认值为`0`正常。

        步骤2:实体类添加属性以及注解,@TableLogic(value="0",delval="1"),用于实现逻辑删除功能。value="0": 表示记录在未被删除的情况下,该字段的值是什么。通常,0 表示数据正常。delval="1": 表示记录被逻辑删除时,该字段的值会被设置为 1。逻辑删除是指不直接从数据库中删除记录,而是通过改变字段值来标识数据已被删除。

@TableLogic(value = "0",delval = "1")
private int delval;

        示例代码:

    //逻辑删除
    @Test
    public void show7() {
        mapper.deleteById(5);
    }

        这样就会根据delval属性进行逻辑删除了,但是不会改变数据库中的信息,删除完成后数据库中的delval属性从0变为1。

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

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

相关文章

Sandbox: rsync.samba(80134) deny(1) file-write-create

Xcode15运行报错:Sandbox: rsync.samba(80134) deny(1) file-write-create/xxx/xxx 如下图: 解决办法: Build Settings 搜索 sandbox&#xff0c;把 Build Options 中的 User Script Sandboxing改为 NO

iPhone很多空相簿怎么删除:简化你的照片库

随着我们使用iPhone拍摄越来越多的照片和视频&#xff0c;管理这些内容的需求也随之增加。创建相簿是组织照片的一种便捷方式&#xff0c;但随着时间的推移&#xff0c;可能会产生许多未使用或空的相簿。这些空相簿不仅占用了照片应用的界面空间&#xff0c;也让照片库显得更加…

代码随想录算法训练营第四十二天|广度优先搜索理论基础、岛屿数量:深搜版、岛屿数量:广搜版

广度优先搜索理论基础 广搜&#xff08;bfs&#xff09;是一圈一圈的搜索过程。因为广搜是从起点出发&#xff0c;以起始点为中心一圈一圈进行搜索&#xff0c;一旦遇到终点&#xff0c;记录之前走过的节点就是一条最短路。 广搜的过程&#xff1a;我们从图中可以看出&#x…

HTML+CSS进阶用法(上)——平面转换、渐变、空间转换

欢迎来到CSS变换的世界&#xff0c;这里充满了创意和可能性。在本篇博客中&#xff0c;我们将一起学习如何使用transform属性来实现各种平面和空间转换效果&#xff0c;包括位移、旋转、缩放&#xff0c;以及如何通过渐变和动画来增强我们的网页设计。无论你是初学者还是有经验…

【区块链+金融服务】基于区块链的区域股权金融综合服务平台 | FISCO BCOS应用案例

区域性股权市场是我国资本市场的重要组成部分&#xff0c;是多层次资本市场体系的基石。区块链技术与区域性股权市场 分散特征天然匹配&#xff0c;从新型金融基础设施层面为场外参与各方提供公共的可信服务&#xff0c;以技术手段完善市场基础条 件&#xff0c;弥补区域性短板…

仿RabbiteMq实现简易消息队列正式篇(需求分析)

TOC 目录 MQ的实现方法 RabbitMq中的相关概念 消息队列系统模块划分 总体划分 服务端模块 数据管理模块 虚拟机数据管理模块 交换机路由模块 消费者管理模块 信道&#xff08;通信&#xff09;管理模块 连接管理模块 服务端BrokerServer模块 客户端模块 消费者管…

iPhone 16 机模视频曝光,五种颜色各有千秋

科技博主的最新视频分享了苹果 iPhone 16 标准版的机模上手体验。 视频中展示了五种颜色的 iPhone 16&#xff1a;深邃的蓝色、柔和的粉色、纯净的白色、经典的黑色和生机勃勃的绿色。 与 iPhone 15 相比&#xff0c;iPhone 16 弃用了黄色&#xff0c;新增了白色&#xff0c;…

等保测评中的供应链安全管理:构建安全的数字生态

在数字化转型的浪潮中&#xff0c;供应链已成为企业运营的核心环节&#xff0c;同时也是信息安全的潜在脆弱点。等保测评&#xff0c;作为我国信息安全等级保护制度的重要组成部分&#xff0c;对供应链安全管理提出了明确的要求&#xff0c;旨在构建一个安全、可靠的数字生态。…

Vue 3+Vite+Eectron从入门到实战系列之(六)一工作台界面开发及实现响应式

工作台&#xff0c;是每个后台系统必备的&#xff0c;也是管理系统的首页。这次我们来实现一个工作台&#xff0c;并适配他的响应性 实现效果 代码实现 <template><div class"dashboard"><el-row :gutter"20"><el-col class"mb…

小程序学习day06-wx:for续、WXSS模版样式、rpx尺寸单位、@import、全局样式与局部样式、全局配置

25、wx&#xff1a;for续 &#xff08;5&#xff09;手动指定索引和当前项的变量名 1&#xff09;使用wx:for-index可以指定当前循环项的索引变量名 2&#xff09;使用wx:for-item可以指定当前项的变量名 &#xff08;6&#xff09;wx:key的使用 类似于Vue列表渲染中的:key&a…

2025考研西安电子科技大学马克思主义理论经验贴

学长从西安电子科技大学马克思主义理论专业毕业&#xff0c;现从事高校思政课工作&#xff0c;拥有三年西安电子科技大学马理论考研辅导经验&#xff0c;现写出2025西安电子科技大学马克思主义理论经验贴供大家参考。 一、选学校 选学校首先考虑的是就业&#xff0c;因为咱们…

【书生大模型实战营(暑假场)】基础任务四 InternLM+LlamaIndex RAG 实践

基础任务四 InternLMLlamaIndex RAG 实践 任务文档视频Retrieval-Augmented Generation for Large Language Models: A Survey 文章目录 基础任务四 InternLMLlamaIndex RAG 实践1 理解RAG&#xff0c;使用 RAG1.1 RAG技术概览1.2 RAG工作原理1.3 向量数据库 Vector-DB1.4 RAG…

多态(二)

1. final &#xff1a;修饰虚函数&#xff0c;表示该虚函数不能再被重写 class Car { public:virtual void Drive() final {} }; class Benz :public Car { public:virtual void Drive() {cout << "Benz-舒适" << endl;} }; 2. override: 检查派生类…

如何选取网络中间的输出?并极简方法可视化

文章目录 一、提供几种方法1. 直接修改模型的 forward 方法&#xff1a;2. 使用 hook 函数&#xff1a;3. 直接访问模型的子模块&#xff1a;4. 使用 nn.Sequential 来提取中间层&#xff1a; 2. 如何选择方法3. 实际操作——提取特征并可视化1. 可视化2. 提取特征 一、提供几种…

无人机之电池注意事项

1、外场作业时&#xff0c;电池一定要放置在阴凉处&#xff0c;避免太阳直射&#xff1b; 2、刚作业完的电池发热严重时&#xff0c;请降至室温再充电&#xff1b; 3、注意电池状态&#xff0c;一旦发现电池出现鼓包、漏液等现象&#xff0c;必须马上停止使用&#xff1b; 4…

基于springboot的医院资源管理系统

TOC springboot158基于springboot的医院资源管理系统 第1章 绪论** 1.1 课题背景 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供…

Redmi 13C 5G 红米13R 5G 解锁BL 降级 MIUI 秒解锁BL 澎湃OS 降级

机型名称 &#xff1a;Redmi 13C 5G/ Redmi 13R 5G/ POCO M6 5G 机型代号 &#xff1a;air HyperOS 降级MIUI 14 澎湃OS 或者miui都可以秒解锁BL&#xff0c; Redmi 13C 5G 红米13R 5G 解锁BL 降级 MIUI 秒解锁BL 澎湃OS 降级,手机ROM - Powered by Discuz!点击上方 下载对…

大迈u盘文件不见了怎么办?全面解析与恢复策略

在数字化时代&#xff0c;U盘作为我们日常工作、学习和生活中不可或缺的存储设备&#xff0c;承载着大量的重要文件。然而&#xff0c;面对突如其来的U盘文件丢失问题&#xff0c;许多用户往往会感到手足无措&#xff0c;甚至因此遭受不可估量的损失。本文将围绕“大迈U盘文件丢…

算法与数据结构笔记

算法是什么 算法定义 算法&#xff08;algorithm&#xff09;是在有限时间内解决特定问题的一组指令或操作步骤&#xff0c;它具有以下特性。 ‧ 问题是明确的&#xff0c;包含清晰的输入和输出定义。 ‧ 具有可行性&#xff0c;能够在有限步骤、时间和内存空间下完成。 ‧…

多种方案解决IOS下uni.share分享分包页面报错Error: Framework inner error

项目场景&#xff1a; 有个需求是用uni.share从app分享微信小程序&#xff0c;发现在苹果手机真机调试的时候 跳转的目标页面会白屏、页面样式错乱、一些组件不出现等问题。并且报错 Error: Framework inner error 问题描述 uniapp开发在苹果手机下app分享微信小程序会出现白…