SpringBoot2.x學習-MyBatis-Plus使用

示例環境:JDK1.8+Spring Boot2+MP+Druid+Oracle

<java.version>1.8</java.version>
<lombok.version>1.18.6</lombok.version>
<druid.version>1.1.13</druid.version>
<jodd.version>5.0.10</jodd.version>
<mybatis-plus.version>3.3.1.tmp</mybatis-plus.version>

一、插入主鍵策略

1.主鍵類型爲NUMBER

當表的主鍵類型爲NUMBER時,插入主鍵採用Oracle序列方式
創建序列如下:

create sequence emp_sequence
minvalue 1
maxvalue 999999999999999999999999
start with 1
increment by 1;

在實體類中採用@KeySequence註解,並且在註解中指定序列名稱,主鍵生成策略必須使用INPUT,如下:

@Setter
@Getter
@ToString
@TableName("EMP")
@KeySequence(value = "EMP_SEQUENCE",clazz = Long.class)
public class Emp {
    /**
     * 員工編號
     */
    @TableId(type = IdType.INPUT)
    private Long empno;
    /**
     * 員工姓名
     */
    @TableField("ename")
    private String empname;
    ........

同時還要 配置Oracle序列生成器

@Configuration
public class MybatisPlusConfig {

    @Bean
    public IdentifierGenerator idGenerator() {
        //自定義ID生成器
        return new CustomIdGenerator();
    }

    @Bean
    public IKeyGenerator keyGenerator(){
    //
        return new OracleKeyGenerator();
    }
}

這樣在保存的時候,會先執行查詢序列:
SELECT EMP_SEQUENCE.NEXTVAL FROM DUAL
然後再執行插入:INSERT INTO EMP ( empno, ename, job, mgr, hiredate, sal, comm, deptno ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ? )

2.主鍵類型爲VARCHAR2

(1)配置全局ID生成策略
mybatis-plus.global-config.db-config.id-type=assign_uuid
(2) 實現自定義的ID生成器

@Slf4j
public class CustomIdGenerator implements IdentifierGenerator {

    @Override
    public Number nextId(Object entity) {
        return null;
    }

    @Override
    public String nextUUID(Object entity) {
        log.info("調用了UUID 生成!");
        return PlatStringUtil.randomUUID();
    }

}

(3) MybatisPlusConfig中 註冊UUID生成器

 /**
     * 註冊UUID生成器
     * @return
     */
    @Bean
    public IdentifierGenerator idGenerator() {
        //自定義ID生成器
        return new CustomIdGenerator();
    }

二、@TableField

這個註解在MP中是字段註解(非主鍵) 主要用途如下

  • 對象中的屬性名和表字段名稱不一致的時候(非駝峯)使用
  • 對象中的屬性名稱在表中不存在的時候使用
  • 某個字段查詢的時候不返回值

示例如下:假設表EMP中字段有:員工姓名-ENAME ;薪水-SAL等字段,對象Emp中有屬性empAddress 不在表中,則可以使用該註解如下

@Setter
@Getter
@ToString
@TableName("EMP")
public class Emp {
    /**
     * 員工編號
     */
    @TableId(type = IdType.ASSIGN_ID)
    private Long empno;
    /**
     * 員工姓名
     */
    @TableField("ename")
    private String empname;
    /**
     * 工作崗位
     */
    private String job;
    /**
     * 領導者編號
     */
    private int mgr;
    /**
     * 入職時間
     * 自定義輸出格式
     */
    @JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    private Timestamp hiredate;
    /**
     * 薪水  查詢的時候不返回這個字段值
     */
    @TableField(select = false)
    private double sal;
    /**
     * 獎金
     */
    private double comm;
    /**
     * 所在部門編號
     */
    private int deptno;
    /**
     * 地址 不是數據庫表字段
     */
    @TableField(exist = false)
    private String empAddress;

}

三、更新操作

一種是根據ID更新,還有一種是根據條件更新

   @RequestMapping("/updateByid")
    public String updateById(HttpServletRequest request){
        Emp emp = new Emp();
        emp.setEmpno(1L);
        emp.setHiredate(new Timestamp(System.currentTimeMillis()));
        emp.setSal(100);
        boolean result = empService.updateById(emp);
        logger.info("the exec result is {}",result);
        return "success";
    }

根據ID更新的語句是: UPDATE EMP SET mgr=?, hiredate=?, sal=?, comm=?, deptno=? WHERE empno=?


    @RequestMapping("/update")
    public String update(HttpServletRequest request){
        Emp emp = new Emp();
        emp.setHiredate(new Timestamp(System.currentTimeMillis()));
        emp.setSal(100);

        /**
         * 根據條件更新需要使用 條件構造器  QueryWrapper或者 UpdateWrapper
         */
        QueryWrapper<Emp> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("ename","程序員");
        queryWrapper.ge("sal",200);

        boolean result = empService.update(emp, queryWrapper);

        logger.info("the exec result is {}",result+"");
        return "success";
    }

根據條件更新語句爲: UPDATE EMP SET mgr=?, hiredate=?, sal=?, comm=?, deptno=? WHERE (ename = ? AND sal >= ?)
根據條件更新也可以寫成這樣

         /**
         * 或者
         */
        UpdateWrapper<Emp> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("sal", 9999).set("hiredate", new Timestamp(System.currentTimeMillis()))
                .eq("ename", "fire2").ge("sal", 200);

        boolean result = empService.update(updateWrapper);

四、分頁查詢

1.配置分頁插件

/**
 * MybatisPlus配置
 *
 * @author David Lin
 * @version: 1.0
 * @date 2020-02-22 23:15
 */
@Configuration
public class MybatisPlusConfig {
    
    @Bean
    public PaginationInterceptor paginationInterceptor(){
        return new PaginationInterceptor();
    }

2.自定義分頁查詢

mapper接口定義如下:

/**
     * 分頁查詢
     * @param page
     * @param queryWrapper
     * @return
     */
    IPage<Emp> selectPageInfo(Page<Emp> page,  @Param(Constants.WRAPPER) QueryWrapper queryWrapper);

自定義SQL的同時也使用Wrapper,方法參數必須添加@Param(Constants.WRAPPER) ,這樣在XML中的${ew.customSqlSegment}纔會生效,更多用法看官方DEMO
XML定義如下:

<select id="selectPageInfo" resultType="Emp">
          select  e.*,d.dname  from emp e
           left join dept d on e.deptno = d.deptno
           ${ew.customSqlSegment}
 </select>

service定義如下:

public IPage<Emp> selectPage() {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("d.deptno","20");

        Page<Emp> page = new Page<>(1,5);
        IPage<Emp> resultPage = empMapper.selectPageInfo(page, queryWrapper);

        return resultPage;
    }

最終分頁執行的Sql如下:

SELECT *
  FROM (SELECT TMP.*, ROWNUM ROW_ID
          FROM (select e.*, d.dname
                  from emp e
                  left join dept d
                    on e.deptno = d.deptno
                 WHERE (d.deptno = ?)) TMP
         WHERE ROWNUM <= ?)
 WHERE ROW_ID > ?

五、常用配置

application.properties 配置如下

#指定MyBatis-Plus配置文件  複雜配置可以單獨寫在 一個xml
#全局配置不能和  mybatis-plus.configuration.xx.xx 配置共同使用
mybatis-plus.config-location=classpath:mybatis/mybatis-config.xml
#如果接口方法對應的XML放在resources目錄下 需要告訴MyBatis-Plus哪裏掃描Mapper
mybatis-plus.mapper-locations=classpath:mapper/**/*.xml
#定義別名掃描的包
mybatis-plus.type-aliases-package=com.soft.fire.platform.*.model
#全局ID生成策略  設置後 可以省略實體對象中的  @TableId(type = IdType.INPUT) 配置
mybatis-plus.global-config.db-config.id-type=assign_uuid
#是否開啓自動駝峯命名規則(camel case)映射
#此屬性在 MyBatis 中原默認值爲 false,在 MyBatis-Plus 中默認開啓
#mybatis-plus.configuration.map-underscore-to-camel-case=true
#MP 全局地開啓或關閉配置文件中的所有映射器已經配置的任何緩存,默認爲 true。
#mybatis-plus.configuration.cache-enabled=true

六、條件構造器

1.allEq

全部eq(或個別isNull)

 @RequestMapping("list")
    public List<Emp> listEmp(HttpServletRequest request) {
        Map<String, Object> params = new HashMap<>(8);
        params.put("deptno", 20);
        params.put("comm", null);
        QueryWrapper<Emp> queryWrapper = new QueryWrapper();
        queryWrapper.allEq(params);

        return empService.list(queryWrapper);
    }

執行的Sql如下:
SELECT empno, ename AS empname, job, mgr, hiredate, comm, deptno
FROM EMP
WHERE (comm IS NULL AND deptno = ?)

2.likeLeft

QueryWrapper<Emp> queryWrapper = new QueryWrapper();
     queryWrapper.likeLeft("job","MAN");

   return empService.list(queryWrapper);

執行的Sql如下:
SELECT empno, ename AS empname, job, mgr, hiredate, comm, deptno
FROM EMP
WHERE (job LIKE ?)
參數爲: %MAN

3.in

QueryWrapper<Emp> queryWrapper = new QueryWrapper();
queryWrapper.in("deptno",10,20);

執行的Sql如下:
SELECT empno, ename AS empname, job, mgr, hiredate, comm, deptno
FROM EMP
WHERE (deptno IN (?, ?))

4.or

QueryWrapper<Emp> queryWrapper = new QueryWrapper();  
 queryWrapper.eq("ename", "smith").or().eq("deptno", 10);

執行的sql如下:
SELECT empno, ename AS empname, job, mgr, hiredate, comm, deptno
FROM EMP
WHERE (ename = ? OR deptno = ?)

5.exists

QueryWrapper<Emp> queryWrapper = new QueryWrapper();
  queryWrapper.exists("select 1  from dept d  where d.deptno =deptno ");

執行的sql如下:
SELECT empno, ename AS empname, job, mgr, hiredate, comm, deptno
FROM EMP
WHERE (EXISTS (select 1 from dept d where d.deptno = deptno))

6.select

設置查詢字段

 QueryWrapper<Emp> queryWrapper = new QueryWrapper();
 queryWrapper.select("empno,ename,job,deptno");

執行的Sql如下:
SELECT empno,ename,job,deptno FROM EMP

七、代碼生成器

(1) 添加依賴

  <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>${mybatis-plus.version}</version>
  </dependency>

(2)設置模板
這裏底層採用Freemarke模板引擎生成代碼,從GitHub下載這些模板如下:
在這裏插入圖片描述

(3)編寫代碼
這個代碼來自MP官方的DEMO
稍加修改如下:

**
 * 代碼生成器 示例
 * @since 202012/29
 */
public class CodeGeneratorWithTemplateTest {

    /**
     * 是否強制帶上註解
     */
    private boolean enableTableFieldAnnotation = false;
    /**
     * 生成的註解帶上IdType類型
     */
    private IdType tableIdType = null;
    /**
     * 是否去掉生成實體的屬性名前綴
     */
    private String[] fieldPrefix = null;
    /**
     * 生成的Service 接口類名是否以I開頭
     * <p>默認是以I開頭</p>
     * <p>user表 -> IUserService, UserServiceImpl</p>
     */
    private boolean serviceClassNameStartWithI = true;

    //運行這個方法 自動代碼生成
    @Test
    public void generateCode() {
        String packageName = "com.soft.fire.platform";
        enableTableFieldAnnotation = true;
        tableIdType = null;
        generateByTables(packageName , "PLAT_SYSTEM_SYSUSER");

    }

    private void generateByTables(String packageName, String... tableNames) {

        // 代碼生成器
        AutoGenerator autoGenerator = new AutoGenerator();

        // 全局配置
        GlobalConfig globalConfig = getGlobalConfig();

        // 數據源配置
        String dbUrl = "jdbc:oracle:thin:@127.0.0.1:1521:ORCL";
        DataSourceConfig dataSourceConfig = getDataSourceConfig(dbUrl);

        // 策略配置
        StrategyConfig strategyConfig = getStrategyConfig(tableNames);


        //配置模板
        TemplateConfig templateConfig = getTemplateConfig();
        //包配置
        PackageConfig packageConfig = getPackageConfig(packageName);

        // 自定義配置
        InjectionConfig injectionConfig = getInjectionConfig(packageConfig);

        autoGenerator.setGlobalConfig(globalConfig);
        autoGenerator.setDataSource(dataSourceConfig);
        autoGenerator.setStrategy(strategyConfig);
        //配置自定義模板
        autoGenerator.setTemplate(templateConfig);
        //配置自定義屬性注入
        autoGenerator.setCfg(injectionConfig);
        autoGenerator.setPackageInfo(packageConfig);

        //使用 Freemarker模版引擎
        autoGenerator.setTemplateEngine(new FreemarkerTemplateEngine());
        autoGenerator.execute();
    }

    /**
     * 包配置
     *
     * @param packageName
     * @return
     */
    private PackageConfig getPackageConfig(String packageName) {
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("system");
        pc.setParent(packageName);
        pc.setController("controller");
        pc.setEntity("model");
        pc.setService("service");
        pc.setMapper("mapper");

        return pc;
    }

    /**
     * 自定義配置
     *
     * @return
     */
    private InjectionConfig getInjectionConfig(PackageConfig pc) {
        InjectionConfig injectionConfig = new InjectionConfig() {
            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<>();
                map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
                this.setMap(map);
            }
        };
        // 自定義輸出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定義配置優先於默認配置生效
        focList.add(new FileOutConfig("/templates/code/mapper.xml.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定義mapper xml輸出目錄
                return System.getProperty("user.dir") + "/src/main/resources/mapper/" + pc.getModuleName() +
                        "/" + tableInfo.getMapperName() + StringPool.DOT_XML;
            }
        });

        injectionConfig.setFileOutConfigList(focList);

        return injectionConfig;
    }

    /**
     * 指定自定義模板路徑
     *
     * @return
     */
    private TemplateConfig getTemplateConfig() {
        //指定自定義模板路徑,注意不要帶上.ftl/.vm, 會根據使用的模板引擎自動識別
        TemplateConfig templateConfig = new TemplateConfig();

        templateConfig.setEntity("templates/code/entity.java");
        templateConfig.setController("templates/code/controller.java");
        templateConfig.setMapper("templates/code/mapper.java");
        //關閉默認的mapper xml生成
        templateConfig.setXml(null);
        templateConfig.setService("templates/code/service.java");
        templateConfig.setServiceImpl("templates/code/serviceImpl.java");

        return templateConfig;
    }

    /**
     * 策略配置
     *
     * @return
     */
    private StrategyConfig getStrategyConfig(String... tableNames) {
        StrategyConfig strategyConfig = new StrategyConfig();

        strategyConfig.setCapitalMode(true);
        strategyConfig.setRestControllerStyle(true);
        //設置數據庫表映射到實體的命名策略
        strategyConfig.setNaming(NamingStrategy.underline_to_camel);
        //設置數據庫表字段映射到實體的命名策略, 未指定按照 naming 執行
        strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
        //是否生成實體時,生成字段註解
        strategyConfig.setEntityTableFieldAnnotationEnable(enableTableFieldAnnotation);
        //是否啓用 Lombok
        strategyConfig.setEntityLombokModel(true);
        //表前綴,配置後 生成的的代碼都會把前綴去掉
        strategyConfig.setTablePrefix("PLAT_SYSTEM");
        //test_id -> id, test_type -> type
        strategyConfig.setFieldPrefix(fieldPrefix);
        //修改替換成你需要的表名,多個表名傳數組
        strategyConfig.setInclude(tableNames);

        return strategyConfig;
    }

    /**
     * 數據源配置
     *
     * @param dbUrl
     * @return
     */
    private DataSourceConfig getDataSourceConfig(String dbUrl) {
        DataSourceConfig dataSourceConfig = new DataSourceConfig();

        dataSourceConfig.setDbType(DbType.ORACLE);
        dataSourceConfig.setUrl(dbUrl);
        dataSourceConfig.setUsername("scott");
        dataSourceConfig.setPassword("tiger");
        dataSourceConfig.setDriverName(Driver.class.getName());

        return dataSourceConfig;
    }

    private GlobalConfig getGlobalConfig() {
        // 全局配置
        GlobalConfig globalConfig = new GlobalConfig();

        globalConfig.setActiveRecord(false);
        globalConfig.setAuthor("David");
        //生成文件的輸出目錄
        globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
        //是否覆蓋已有文件
        globalConfig.setFileOverride(true);
        //開啓 BaseResultMap
        globalConfig.setBaseResultMap(true);
        //開啓 baseColumnList
        globalConfig.setBaseColumnList(true);
        //是否生成完成後打開資源管理器
        globalConfig.setOpen(true);
        //日期類型的字段使用哪個類型,默認是 java8的 日期類型,此處改爲 java.util.date
        globalConfig.setDateType(DateType.ONLY_DATE);

        //生成的Service 接口類名是否以I開頭
        if (!serviceClassNameStartWithI) {
            globalConfig.setServiceName("%sService");
        }

        return globalConfig;
    }

八、ActiveRecord模式

實體對象繼承Model就可以開啓AR模式

@Setter
@Getter
@ToString
@TableName("EMP")
@KeySequence("EMP_SEQUENCE")
public class Emp extends Model<Emp> {
    /**
     * 員工編號
     */
    @TableId(type = IdType.INPUT)
    private Long empno;
 @RequestMapping("list")
    public List<Emp> listEmp(HttpServletRequest request) {

        QueryWrapper<Emp> queryWrapper = new QueryWrapper();
        queryWrapper.eq("deptno",20);
        queryWrapper.select("empno,ename,job,deptno");

        Emp emp = new Emp();

        return emp.selectList(queryWrapper);
    }

執行的SQL如下:
SELECT empno,ename,job,deptno FROM EMP WHERE (deptno = ?)
由於底層也是調用Mapper的方法,所以EmpMapper不能刪除掉

九、自動填充

當插入的時候自動填充CREATE_TIME 字段值,當更新的時候 自動填充UPDATE_TIME 字段值

1.指定要自動填充的字段

/**
     * 創建時間
     */
    @TableField(value = "CREATE_TIME" ,fill = FieldFill.INSERT,jdbcType = JdbcType.TIMESTAMP)
    private Timestamp createTime;
    /**
     * 更新時間
     */
    @TableField(value = "UPDATE_TIME",fill = FieldFill.UPDATE,jdbcType = JdbcType.TIMESTAMP)
    private Timestamp updateTime;

如果日期類型使用LocalDateTime ,如會報:Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. Cause: java.sql.SQLException: 無效的列類型: 1111, ,日期類型改成 Timestamp類型

2.自定義填充器實現類

**
 * 自定義填充器
 *
 * @author David Lin
 * @version: 1.0
 * @date 2020-03-15 17:28
 */
public class MyMetaObjectHandler implements MetaObjectHandler {

    /**
     * 插入時填充
     *
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        Object createTime = getFieldValByName("createTime", metaObject);
        //如果爲空 才進行填充
        if (null == createTime) {
            this.strictInsertFill(metaObject, "createTime", Timestamp.class, new Timestamp(System.currentTimeMillis()));
        }
    }

    /**
     * 更新時填充
     *
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        Object createTime = getFieldValByName("updateTime", metaObject);
        //如果爲空 才進行填充
        if (null == createTime) {
            this.strictUpdateFill(metaObject, "updateTime", Timestamp.class, new Timestamp(System.currentTimeMillis()));
        }
    }
}

3.注入Spring BOOT

 */
@Configuration
public class MybatisPlusConfig {

    /**
     * 注入 自定義填充器
     * @return
     */
    @Bean
    public MetaObjectHandler metaObjectHandler(){
        return new MyMetaObjectHandler();
    }

這樣在插入操作/更新操作的時候 如果沒有顯示設置時間值, 則會自動填充,插入的sql如下:
INSERT INTO EMP ( empno, ename, job, mgr, hiredate, sal, comm, deptno, CREATE_TIME ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? )
自動填充只對操作實體對象時纔有效果

十、MybatisX

MybatisX 是一款基於 IDEA 的快速開發插件,爲效率而生。

1.Java代碼與XML之間跳轉

2.Mapper方法自動生成XML

具體使用參考官網

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章