SpringBoot2.4.x整合Mybatis-Plus3.4.x(詳細教程)


Mybatis和MybatisPlus的區別與聯系

Mybatis-Plus是一個Mybatis的增強工具,只是在Mybatis的基礎上做了增強卻不做改變,MyBatis-Plus支持所有Mybatis原生的特性,所以引入Mybatis-Plus不會對現有的Mybatis構架產生任何影。Mybatis-Plus又簡稱(MP)是為簡化開發,提高開發效率而生正如官網所說的

image.png
點擊這里進入學官網學習

快速與SpringBoot整合基礎入門

導入必須依賴

  1. MybatisPlus整合SpringBoot的場景啟動器jar
 <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.3</version>
  </dependency>
  1. 連接mysql的驅動jar
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

注意這里沒有指定mysql-connector-javajar包版本時SpringBoot會默認為我們指定一個版本

配置數據源

#---------------數據庫連接配置--------------
# 用戶名
spring.datasource.username=root
# 密碼
spring.datasource.password=root
# 連接url
spring.datasource.url=jdbc:mysql://localhost:3306/school?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&autoReconnect=true
# 驅動名稱
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

簡單CRUD

編寫實體類與數據庫映射

實體類Student與數據庫表student對應

package cn.soboys.springbootmybatisplus.bean;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/25 10:08
 */

@TableName("student")
public class Student extends Model {
    @TableId(value = "student_id",type = IdType.AUTO)
    private Long studentId;
    @TableField("student_name")
    private String studentName;
    @TableField("age")
    private int age;
    @TableField("phone")
    private String phone;
    @TableField("addr")
    private String addr;


    public Long getStudentId() {
        return studentId;
    }

    public void setStudentId(Long studentId) {
        this.studentId = studentId;
    }

    public String getStudentName() {
        return studentName;
    }

    public void setStudentName(String studentName) {
        this.studentName = studentName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }
}

編寫mapper(dao)與數據庫交互

接口StudentMapper具體實現由mybatis代理實現

package cn.soboys.springbootmybatisplus.mapper;

import cn.soboys.springbootmybatisplus.bean.Student;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/25 10:53
 */
@Mapper
public interface StudentMapper extends BaseMapper<Student> {


}

編寫service實現具體業務

  1. 接口IStudentService
package cn.soboys.springbootmybatisplus.service;

import cn.soboys.springbootmybatisplus.bean.Student;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.stereotype.Service;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/25 10:59
 */

public interface IStudentService extends IService<Student> {
}

  1. 實現類StudentServiceImpl
package cn.soboys.springbootmybatisplus.service.impl;

import cn.soboys.springbootmybatisplus.bean.Student;
import cn.soboys.springbootmybatisplus.mapper.StudentMapper;
import cn.soboys.springbootmybatisplus.service.IStudentService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/25 13:35
 */
@Service
public class StudentServiceImpl extends ServiceImpl<StudentMapper,Student> implements IStudentService {
}

編寫controller主程序

package cn.soboys.springbootmybatisplus.controller;

import cn.soboys.springbootmybatisplus.bean.Student;

import cn.soboys.springbootmybatisplus.service.IStudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/25 11:09
 */
@RestController
@RequestMapping("/student")
public class StudentController {

    @Autowired
    private IStudentService studentService;

    /**
     * 添加學生
     *
     * @param student
     * @return
     */
    @PostMapping("/add")
    public boolean addStudent(@RequestBody Student student) {
        boolean flag = studentService.save(student);
        return flag;
    }

    /**
     * 根據id更新學生信息
     *
     * @param student
     * @return
     */
    @PutMapping("/update")
    public boolean updateStudent(@RequestBody Student student) {
        //根據學生id更新學生
        boolean flag = studentService.updateById(student);
        return flag;
    }


    /**
     * 查找所有學生信息
     *
     * @return
     */
    @GetMapping("/list")
    public List<Student> list() {
        return studentService.list();
    }

    /**
     * 根據id刪除學生信息
     *
     * @param studentId
     * @return
     */
    @DeleteMapping("/del/{studentId}")
    public boolean del(@PathVariable long studentId) {
        boolean flag = studentService.removeById(studentId);
        return flag;
    }

    /**
     * 根據id獲取學生信息
     * @param studentId
     * @return
     */
    @GetMapping("{studentId}")
    public Student getStudentInfo(@PathVariable long studentId){
        return studentService.getById(studentId);
    }


}

向數據庫里添加一個學生

image.png
我們看到返回結果是true 代表添加成功

根據學生id修改剛剛添加學生信息

image.png
我們看到也修改成功返回true,注意這里修改時候多傳一個studentId 參數,就是通過學生id找到對應的學生在進行修改。

查詢所有的學生信息

image.png

根據學生id刪除學生信息

image.png
我們看到返回true代表刪除成功

根據id獲取某個學生信息

image.png

到這里單張表最基本的crud功能都可以正常使用

SpringBoot整合進階使用

我們看到上面完成了最基本整合使用很多地方還可以進一步優化

簡化實體bean

我們看到上面整合方式Student實體類包含很多getter,setter方法,和一些不非必要的映射注解可以適當的簡化

  1. 導入jar包lombok 這里沒有寫版本默認用springboot給我們指定的一個版本
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

lombok可以通過注解@Data幫我們自動生成getter,setter 方法我們只需要在對應實體類上添加上這個注解就可以去掉代碼中冗余getter,setter 方法。

簡化實體bean與數據庫映射注解

我們看到上面整合方式Student實體類包含@TableName 注解和很多@TableField注解其實遵守MybatisPlus中java實體類與數據庫映射規則可以適當簡化默認MybatisPlus會把大駝峰命名法(帕斯卡命名法)轉換為數據庫對應下划線命名方法

例如實體類名為:OwnUser,會給你對應到數據庫中的own_user表
字段StudentId ,會給你對應數據庫表中student_id 的字段

所以最終實體類可以簡化成如下代碼

package cn.soboys.springbootmybatisplus.bean;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/25 10:08
 */

@TableName
@Data
public class Student extends Model {
    @TableId(type = IdType.AUTO)
    private Long studentId;
    private String studentName;
    private int age;
    private String phone;
    private String addr;
}

簡化mapper掃描

上面整合方式會在每個mapper接口類中添加@Mapper注解進行掃描這樣會很麻煩造成冗余,我們可以直接在SpringBoot啟動類上添加@MapperScan批量掃描mapper 包,當然我們也可以在其他任意配置類上添加@MapperScan批量掃描mapper 包,但一般會在SpringBoot啟動類上添加(本質SpringBoot啟動類也是配置類),
這樣配置比較集中,有意義,也不需要額外去寫一個無意的配置類

package cn.soboys.springbootmybatisplus;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
@MapperScan({"cn.soboys.springbootmybatisplus.mapper"})
public class SpringbootMybatisplusApplication {

    private static ApplicationContext applicationContext;

    public static void main(String[] args) {
        applicationContext = SpringApplication.run(SpringbootMybatisplusApplication.class, args);
        //displayAllBeans();
    }

    /**
     * 打印所以裝載的bean
     */
    public static void displayAllBeans() {
        String[] allBeanNames = applicationContext.getBeanDefinitionNames();
        for (String beanName : allBeanNames) {
            System.out.println(beanName);
        }
    }
}

這樣就可以不用單獨在每個mapper接口類添加@Mapper注解進行掃描

數據庫連接池配置

上面我們只是進行了簡單的數據庫連接配置,但是在真正實際應用中都會使用數據庫連接池提高數據連接效率,減少不必要數據庫資源開銷 具體配置如下

#---------------數據庫連接配置--------------
#數據源類型
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
# 用戶名
spring.datasource.username=root
# 密碼
spring.datasource.password=root
# 連接url
spring.datasource.url=jdbc:mysql://localhost:3306/school?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&autoReconnect=true
# 驅動名稱
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#---------------數據庫連接池HikariCP配置--------------
#最小空閑連接,默認值10,小於0或大於maximum-pool-size,都會重置為maximum-pool-size
spring.datasource.hikari.minimum-idle=10
#最大連接數,小於等於0會被重置為默認值10;大於零小於1會被重置為minimum-idle的值
spring.datasource.hikari.maximum-pool-size=20
#空閑連接超時時間,默認值600000單位毫秒(10分鍾),
# 大於等於max-lifetime且max-lifetime>0,會被重置為0;不等於0且小於10秒,會被重置為10秒。
spring.datasource.hikari.idle-timeout=500000
#連接最大存活時間,不等於0且小於30秒,會被重置為默認值30分鍾.設置應該比mysql設置的超時時間短
spring.datasource.hikari.max-lifetime=540000
#連接超時時間:毫秒,小於250毫秒,否則被重置為默認值30秒
spring.datasource.hikari.connection-timeout=60000
#用於測試連接是否可用的查詢語句
spring.datasource.hikari.connection-test-query=SELECT 1

MybatisPlus配置

我們看到上面整合只是簡單crud,單張表的操作,我們的service,mapper 沒有寫任何方法只是繼承了MybatisPlus 的通用 Mapper,BaseMapper,通用service接口IService以及實現ServiceImpl就具備了基礎的crud方法,但是當遇到多表復雜條件查詢時候,就需要單獨寫sql,這時候就需要單獨配置了mapper.xml 文件了

#--------------------mybatisPlus配置------------------
#mapper.xml 文件位置
mybatis-plus.mapper-locations=classpath:mapper/*.xml
#別名包掃描路徑,通過該屬性可以給包中的類注冊別名
mybatis-plus.type-aliases-package=cn.soboys.springbootmybatisplus.bean
#控制台打印mybatisPlus LOGO
mybatis-plus.global-config.banner=true

MybatisPlus更多詳細配置

分頁插件使用

在MybatisPlus中也為我們分頁做了相關處理我們要做相關配置才能正常使用

SpringBoot配置類

package cn.soboys.springbootmybatisplus.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/28 10:19
 */
@SpringBootConfiguration
public class MyBatisCfg {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

StudentMapper類

/**
     * 分頁查詢每個班級學生信息
     * @param page 分頁對象,xml中可以從里面進行取值,傳遞參數 Page 即自動分頁,必須放在第一位(你可以繼承Page實現自己的分頁對象)
     * @param gradeId 班級id
     * @return 分頁對象
     */
    IPage<Student> findStudentPage(Page<?> page, long gradeId);

studentMapper.xml

<!-- 等同於編寫一個普通 list 查詢,mybatis-plus 自動替你分頁-->
        <select id="findStudentPage" resultType="Student">
            select * from student ,grade g where g.grade_id=#{gradeId}
        </select>

IStudentService 接口

/**
     * 分頁查詢
     * @param page
     * @param gradeId 班級id
     * @return
     */
    IPage<Student> getStudentPage(Page<Student> page,long gradeId);

StudentServiceImpl 實現類

@Override
    public IPage<Student> getStudentPage(Page<Student> page,long gradeId) {
        // 不進行 count sql 優化,解決 MP 無法自動優化 SQL 問題,這時候你需要自己查詢 count 部分
        // page.setOptimizeCountSql(false);
        // 當 total 為小於 0 或者設置 setSearchCount(false) 分頁插件不會進行 count 查詢
        // 要點!! 分頁返回的對象與傳入的對象是同一個

        return this.baseMapper.findStudentPage(page,gradeId);
    }

StudentController 主程序調用

 /**
     * 分頁獲取學生詳細信息
     *
     * @return
     */
    @GetMapping("listDetailPage")
    public IPage<Student> getStudentDetailPage(PageRequest request) {
        Page<Student> page = new Page<>();
        //設置每頁顯示幾條
        page.setSize(request.getPageSize());
        //設置第幾頁
        page.setCurrent(request.getPageNum());
        return studentService.getStudentPage(page, 1);
    }

這里需要傳遞分頁等相關信息,可以自己封裝一個分頁查詢通用對象PageRequest

package cn.soboys.springbootmybatisplus;

import lombok.Data;

import java.io.Serializable;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/28 10:41
 * 分頁查詢
 */
@Data
public class PageRequest implements Serializable {
    private static final long serialVersionUID = -4869594085374385813L;

    /**
     * 當前頁面數據量
     */
    private int pageSize = 10;

    /**
     * 當前頁碼
     */
    private int pageNum = 1;

    /**
     * 排序字段
     */
    private String field;

    /**
     * 排序規則,asc升序,desc降序
     */
    private String order;
}

image.png
我們看到正常分頁查詢每頁顯示2條第1頁

image.png
第2頁

代碼生成器

我們知道Mybatis可以通過配置生成基礎的實體映射bean,簡化我們開發時間,不必要寫繁瑣的映射bean包括一堆屬性,MybatisPlus也有自己的代碼生成器AutoGenerator,通過簡單配置,我們可以快速生成完整的modelservice,mapper,不需要自己去寫然后繼承通用mapper,service官網原話如下

image.png

添加依賴

 <!--生成器依賴-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.1</version>
</dependency>
  <!--MyBatis-Plus 從 3.0.3 之后移除了代碼生成器與模板引擎的默認依賴,需要手動添加相關依賴:-->
<!--添加 模板引擎 依賴,MyBatis-Plus 支持 Velocity(默認)、
Freemarker、Beetl,用戶可以選擇自己熟悉的模板引擎,
如果都不滿足您的要求,可以采用自定義模板引擎。-->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.31</version>
</dependency>

自定義生成器代碼

package cn.soboys.springbootmybatisplus;


import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * @author kenx
 * @version 1.0
 * @date 2021/6/28 11:31
 * 自動代碼生成器
 */
public class MyBatisGeneratorCode {
    /**
     * <p>
     * 讀取控制台內容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("請輸入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("請輸入正確的" + tip + "!");
    }

    public static void main(String[] args) {
        // 代碼生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("kenx");
        gc.setOpen(false);
        // gc.setSwagger2(true); 實體屬性 Swagger2 注解
        mpg.setGlobalConfig(gc);

        // 數據源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/school?useUnicode=true&useSSL=false&characterEncoding=utf8");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(scanner("模塊名"));
        pc.setParent("cn.soboys.springbootmybatisplus.generator");
        mpg.setPackageInfo(pc);

        // 自定義配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        // 如果模板引擎是 freemarker
        String templatePath = "/templates/mapper.xml.ftl";
        // 如果模板引擎是 velocity
        // String templatePath = "/templates/mapper.xml.vm";

        // 自定義輸出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定義配置會被優先輸出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定義輸出文件名 , 如果你 Entity 設置了前后綴、此處注意 xml 的名稱會跟着發生變化!!
                return projectPath + "/src/main/resources/mapper/generator/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        /*
        cfg.setFileCreate(new IFileCreate() {
            @Override
            public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
                // 判斷自定義文件夾是否需要創建
                checkDir("調用默認方法創建的目錄,自定義目錄用");
                if (fileType == FileType.MAPPER) {
                    // 已經生成 mapper 文件判斷存在,不想重新生成返回 false
                    return !new File(filePath).exists();
                }
                // 允許生成模板文件
                return true;
            }
        });
        */
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();

        // 配置自定義輸出模板
        //指定自定義模板路徑,注意不要帶上.ftl/.vm, 會根據使用的模板引擎自動識別
        // templateConfig.setEntity("templates/entity2.java");
        // templateConfig.setService();
        // templateConfig.setController();

        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        //strategy.setSuperEntityClass("你自己的父類實體,沒有就不用設置!");
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        // 公共父類
        //strategy.setSuperControllerClass("你自己的父類控制器,沒有就不用設置!");
        // 寫於父類中的公共字段
        strategy.setSuperEntityColumns("id");
        strategy.setInclude(scanner("表名,多個英文逗號分割").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }
}

運行后的結果

image.png
我們看到運行成功已經正常生成我們需要的目錄結構

image.png

image.png
這個模版是通用模版只需要,稍微改一下自己需要生成數據庫目錄就可以使用了

更多內容參考官網

SpringBoot整合高階使用

調試打印應sql

在開發中我們常常需要調試代碼,需要看看生成sql是否正確,這個時候就需要在控制台打印sql

導入依賴

<!-- https://mvnrepository.com/artifact/p6spy/p6spy -->
<dependency>
    <groupId>p6spy</groupId>
    <artifactId>p6spy</artifactId>
    <version>3.9.0</version>
</dependency>

配置文件配置

#url 改為p6spy開頭的連接url
spring.datasource.url=jdbc:p6spy:mysql://localhost:3306/school?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&autoReconnect=true
# 驅動名稱
# 需要調試攔截打印sql 驅動改為p6spy 攔截
spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver

spy配置文件

#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定義日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志輸出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系統記錄 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 設置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前綴
useprefix=true
# 配置記錄 Log 例外,可去掉的結果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 實際驅動可多個
#driverlist=org.h2.Driver
# 是否開啟慢SQL記錄
outagedetection=true
# 慢SQL記錄標准 2 秒
outagedetectioninterval=2

我們看到通過簡單配置控制台成功打印出運行sql

image.png

GitHub項目源碼

掃碼關注公眾號猿人生了解更多好文


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM