首頁 > 軟體

mybatis plus自動生成器解析(及遇到的坑)

2022-03-12 13:00:16

mybatis plus自動生成器解析

使用這個可以超快速生成entity service controller層

1.加入依賴

模板引擎的依賴也要匯入,不然執行會報錯的

<dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.3.1.tmp</version>
    </dependency>
    <dependency>
        <groupId>org.freemarker</groupId>
        <artifactId>freemarker</artifactId>
        <version>2.3.30</version>
    </dependency>

2.寫一個類,作為自動生成器的入口

這裡複製程式碼的時候,一定一定要把import的包也複製,不然很容易出錯

package van.generator;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
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.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.FileOutConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.TemplateConfig;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
/**
 * @author Van
 * @date 2020/5/1 - 15:43
 */
public class CodeGenerator {
    /**
     * <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.isNotEmpty(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("請輸入正確的" + tip + "!");
    }
    /**
     * RUN THIS
     */
    public static void main(String[] args) {
        // 程式碼生成器
        AutoGenerator mpg = new AutoGenerator();
        // 全域性設定
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/ad-sponsor/src/main/java/van");
        gc.setAuthor("van");
        gc.setOpen(false);
        mpg.setGlobalConfig(gc);
        // 資料來源設定
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/ad?useUnicode=true&serverTimezone=GMT&useSSL=false&characterEncoding=utf8");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("321asd");
        mpg.setDataSource(dsc);
        // 包設定
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(scanner("模組名"));
        pc.setParent("com.baomidou.mybatisplus.samples.generator");
        mpg.setPackageInfo(pc);
        // 自定義設定
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };
        List<FileOutConfig> focList = new ArrayList<>();
        focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定義輸入檔名稱
                return projectPath + "/mybatis-plus-sample-generator/src/main/resources/mapper/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);
        mpg.setTemplate(new TemplateConfig().setXml(null));
        // 策略設定
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setSuperEntityClass("com.baomidou.mybatisplus.samples.generator.common.BaseEntity");
        strategy.setEntityLombokModel(true);
        strategy.setSuperControllerClass("com.baomidou.mybatisplus.samples.generator.common.BaseController");
        strategy.setInclude(scanner("表名"));
        strategy.setSuperEntityColumns("id");
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);
        // 選擇 freemarker 引擎需要指定如下加,注意 pom 依賴必須有!
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }
}

3.修改程式碼

1.全域性設定

2.資料來源設定

3.包設定

3.執行

講解一下

1.首先執行這裡

要求輸入的模組名是生成的entity等的上層包名,我這裡輸入的是sys如上圖所示

2.全域性設定這裡

是指定要生成的地方(模組名)

3.資料來源設定沒啥說的,填入正確資訊即可

4.包設定

mybatis plus程式碼生成器使用及注意事項

Mybatis-plus的程式碼生成器是mybatis-plus元件的,並不是mybatis的,注意不要看錯,這裡會介紹程式碼生成器的主要用法以及需要注意的事項。

1.新增maven依賴

Mybatis-plus的程式碼生成器在3.0.3之後就獨立出來,與mybatis-plus分開了,所以使用高版本的mybatis-plus的同學要注意,程式碼生成器要單獨引入,如果是使用低版本的,不需要額外參照,這裡使用的是按照官方檔案的3.1.2版本,

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>com.code.generator</groupId>
    <artifactId>code-generator</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
 
    <dependencies>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.1.2</version>
        </dependency>  
 
        <!-- velocity 模板引擎, 預設 -->
        <!-- https://mvnrepository.com/artifact/org.apache.velocity/velocity-engine-core -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.0</version>
        </dependency> 
 
        <!-- freemarker 模板引擎 -->
        <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.28</version>
        </dependency>
 
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>oracle.jdbc</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0.2.0</version>
        </dependency> 
 
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.20.RELEASE</version>
        </dependency> 
 
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
            <scope>compile</scope>
        </dependency> 
 
    </dependencies> 
</project>

2.編寫程式碼生成器的類

依賴好了之後就可以開始編寫我們的程式碼生成器,程式碼生成器主要有幾部分的設定,具體設定的說明參考程式碼註釋

(1)全域性設定

(2)包名設定

(3)資料來源設定

(4)輸出模板

(5)自定義設定

(6)生成策略

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
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 java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; 
 
public class CodeGenerator {
    public static void main(String[] args) {
        AutoGenerator mpg = new AutoGenerator();
 
        /**
         * 全域性設定,常用
         * 1.組態檔輸出的路徑OutputDir
         * 2.設定是否每次覆蓋檔案FileOverride
         * 3.mybatis的xml設定,如二級快取,是否生成resultMap對映,是否生成columnlist的sql,這裡沒有使用xml,所以註釋掉
         * 4.設定作者、是否使用swagger2等全域性變數,其他變數參考官方檔案
         * 5.自定義檔案命名,%s是預留位置,會將實體名替換進去
         */
        final GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir("E://output");//輸出檔案路徑
        gc.setFileOverride(true);
        gc.setActiveRecord(false);// 不需要ActiveRecord特性的請改為false
        //gc.setEnableCache(false);// XML 二級快取
        //gc.setBaseResultMap(true);// XML ResultMap
        //gc.setBaseColumnList(false);// XML columList
        gc.setAuthor("simple");// 作者
		gc.setSwagger2(true);
 
        // 自定義檔案命名,注意 %s 會自動填充表實體屬性!
        gc.setControllerName("%sController");
        gc.setServiceName("%sService");
        gc.setServiceImplName("%sServiceImpl");
        gc.setMapperName("%sMapper");
        mpg.setGlobalConfig(gc);
 
        /**
         * 包的設定,主要設定每一層的包名
         */
        final PackageConfig pc = new PackageConfig();
        //設定包名
        pc.setParent("com.code.generator");
        pc.setController("system.web");
        pc.setService("system.service");
        pc.setServiceImpl("system.service.impl");
        pc.setMapper("system.mapper");
        pc.setEntity("domain.po");
        mpg.setPackageInfo(pc);
 
        /**
         * 資料來源設定,mybatis支援的資料庫這裡都支援
         */
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setDbType(DbType.ORACLE);
        dsc.setDriverName("oracle.jdbc.driver.OracleDriver");
		//TODO
        dsc.setUsername("user");
		//TODO
        dsc.setPassword("password");
		//TODO
        dsc.setUrl("jdbc:oracle:thin:@ip:host:db");
        mpg.setDataSource(dsc);
 
        /**
         * 輸出模板,如果按照官方原生的可以不設定,也可以設定自定義的模板
         */
        TemplateConfig templateConfig = new TemplateConfig();
 
        // 設定自定義輸出模板
        //指定自定義模板路徑,注意不要帶上.ftl/.vm, 會根據使用的模板引擎自動識別,預設vm,xml不輸出
        templateConfig.setEntity("myTemplates/entity.java");
        templateConfig.setService("myTemplates/service.java");
        templateConfig.setServiceImpl("myTemplates/serviceImpl.java");
        templateConfig.setController("myTemplates/controller.java");
        templateConfig.setMapper("myTemplates/mapper.java");
        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);
 
        /**
         * 自定義設定,可以自定義引數在模板中使用,還可以已定義輸出的檔案,
         * 如果除了上面的幾個模板之外還有其他的檔案需要輸出可以在這裡設定
         */
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("tableComment", "系統使用者");
                map.put("Handler", pc.getParent()+".system.handler");
                map.put("SuperHandler", "com.code.generator.core.base.BaseHandler");
                map.put("SuperHandlerName", "BaseHandler");
                map.put("vo", pc.getParent()+".domain.vo");
                this.setMap(map);
            }
        };
 
        // 自定義輸出設定
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定義handler
        focList.add(new FileOutConfig("myTemplates/handler.java.vm") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return gc.getOutputDir() + "/com/code/generator/handler/"
                        + tableInfo.getEntityName()+"Handler" + StringPool.DOT_JAVA;
            }
        });
        // 自定義provider
        focList.add(new FileOutConfig("myTemplates/provider.java.vm") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return gc.getOutputDir() + "/com/code/generator/mapper/provider/"
                        + tableInfo.getEntityName()+"Provider" + StringPool.DOT_JAVA;
            }
        });
 
        // 自定義vo
        focList.add(new FileOutConfig("myTemplates/vo.java.vm") {
            @Override
            public String outputFile(TableInfo tableInfo) {
                return gc.getOutputDir() + "/com/code/generator/domain/vo/"
                        + tableInfo.getEntityName()+"Vo" + StringPool.DOT_JAVA;
            }
        });
 
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);  
 
        /**
         * 生成策略設定,常用
         * 1.指定生成的表名
         * 2.表名字首過濾
         * 3.實體名、欄位名的命名方式
         * 4.指定繼承的父類別、父欄位
         */
        StrategyConfig strategy = new StrategyConfig();
        //過濾表字首
        strategy.setTablePrefix(new String[] { "T_" });
        //類名生成策略:駝峰命名
        strategy.setNaming(NamingStrategy.underline_to_camel);
        //欄位名生成方式:駝峰命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        //需要生成的表
        strategy.setInclude(new String[] { "T_SYS_USER" });
        strategy.setCapitalMode(true);
        //controller是否restful風格
        strategy.setRestControllerStyle(true);
 
        //設定繼承的父類別
        strategy.setSuperEntityClass("com.code.generator.po.BaseDomain");
        strategy.setSuperEntityColumns("ID","ISVALID","CREATORID","CREATEDTIME","MODIFYID","MODIFYTIME","PACKAGEID");
        strategy.setSuperControllerClass("com.code.generator.core.base.BaseController");
        strategy.setSuperServiceClass("com.code.generator.core.base.BaseService");
        strategy.setSuperServiceImplClass("com.code.generator.impl.core.base.BaseServiceImpl");
        strategy.setSuperMapperClass("com.code.generator.impl.core.base.BaseMapper");
        mpg.setStrategy(strategy);  
        // 執行生成
        mpg.execute(); 
    } 
}
 

3.自定義輸出模板

mybatis-plus-generator的jar包裡面有原生的模板檔案,在jar包的templates目錄下,btl對應的是beetl模板引擎,ftl對應freemarker模板引擎,vm對應的是velocity模板引擎,預設是vm,如果想更改使用的引擎可以通過setTemplateEngine方法設定,模板中的全域性變數可以參考下面方法裡面的變數com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine#getObjectMap

   

如果不想使用原生的模板,可以編寫自己的模板,基於原生模板修改,新的模板要放在resources下面,注意寫的時候不要加上模板檔案的字尾,會根據引擎自動識別,如果不想輸出對應的檔案,需要設定為null,否則會拿jar包下面原生的模板生成。

//指定自定義模板路徑,注意不要帶上.ftl/.vm, 會根據使用的模板引擎自動識別,預設vm,xml不輸出
templateConfig.setEntity("myTemplates/entity.java");
templateConfig.setXml(null);

4.自定義引數模板

(1)自定義引數

程式碼生成器中提供了一些模板引數,如果需要客製化化自己的模板可能需要一些自定義的引數可以在這裡新增,在模板中可以通過${cfg.key}來使用

(2)自定義模板

原生提供的有controller、service、serviceImpl、dao、mapper、entity六種生成模板,如果我們還需要輸出其他的一些模板檔案,可以通過以下方式來輸出,與TemplateConfig設定不一樣,這裡模板的路徑需要加上字尾,還需要指定輸出路徑。

到這裡,程式碼生成器就可以工作了,但是還有一些小缺陷在其他的文章中沒有提到,下面再提兩點

5.關於紀錄檔輸出

在程式碼生成器中實際上是有生成過程的紀錄檔輸出的,如果設定不正確也會有對應的提示,但是在很多的教學裡面都沒有提到這一點,控制檯都提示log4j異常了。

關於紀錄檔,實際上在模板引擎中就有log4j的api包引入了,但是log4j還需要有對應的實現才能輸出紀錄檔,所以會提示上圖的異常,因此在上面我的依賴中還加入了slf4j-log4j12的實現依賴

這時候我們再來執行,發現還會報錯,這是因為缺少log4j的properties檔案,下面我們建立一個log4j.properties檔案,注意檔名不要改,就叫這個名字,設定內容參考下面的,也可以用你常用的,具體設定的屬性自行查詢吧

log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

上面該補的補完之後我們就可以看到程式碼生成器的控制檯紀錄檔了

6.關於欄位型別轉換

程式碼生成器在生成欄位資訊的時候會從資料庫獲取表欄位資訊,根據一定的規則來生成欄位,如果不想使用原生的欄位資訊,可以自定義自己的規則,在這裡我使用的是Oracle資料庫,在設定資料來源的時候有兩個方法setDbQuery和setTypeConvert,前者是獲取資料庫欄位資訊的sql,後者是資料庫欄位型別與java欄位型別的對映規則,下面我們看一下原始碼的實現看看是如何轉換的

(1)獲取欄位資訊

在這裡可以看到對於Oracle在處理Number型別欄位的時候是做了處理的,不像mysql,很多時候我們會直接使用number來代表各種數位型別,如double、decimal等,這裡會根據number的欄位長度、小數位來進行組裝然後提供給後面欄位對映來進行處理,所以我們在設計的時候要注意,該定義長度就定義長度,該定義小數位就定義小數位,儘量不要直接用number或者number(*,0)這樣的方式來定義,否則在後面對映的時候回當成Double處理,但實際上我們想要的是Integer,不然生成之後你就只能手工修改,或者自定義自己的轉換規則來適配

SELECT
	A.COLUMN_NAME,
CASE
	
	WHEN A.DATA_TYPE = 'NUMBER' THEN
	(
CASE
	
	WHEN A.DATA_PRECISION IS NULL THEN
	A.DATA_TYPE 
	WHEN NVL( A.DATA_SCALE, 0 ) > 0 THEN
	A.DATA_TYPE || '(' || A.DATA_PRECISION || ',' || A.DATA_SCALE || ')' ELSE A.DATA_TYPE || '(' || A.DATA_PRECISION || ')' 
END 
	) ELSE A.DATA_TYPE 
	END DATA_TYPE,
	B.COMMENTS,
	DECODE( C.POSITION, '1', 'PRI' ) KEY 
FROM
	ALL_TAB_COLUMNS A
	INNER JOIN ALL_COL_COMMENTS B ON A.TABLE_NAME = B.TABLE_NAME 
	AND A.COLUMN_NAME = B.COLUMN_NAME 
	AND B.OWNER = 'USER'--schema
	LEFT JOIN ALL_CONSTRAINTS D ON D.TABLE_NAME = A.TABLE_NAME 
	AND D.CONSTRAINT_TYPE = 'P' 
	AND D.OWNER = 'USER'--schema
	LEFT JOIN ALL_CONS_COLUMNS C ON C.CONSTRAINT_NAME = D.CONSTRAINT_NAME 
	AND C.COLUMN_NAME = A.COLUMN_NAME 
	AND C.OWNER = 'USER' --schema
WHERE
	A.OWNER = 'USER' --schema
	AND A.TABLE_NAME = 'T_SYS_USER' 
ORDER BY
	A.COLUMN_ID

(2)欄位型別對映

欄位型別對映我們關注點還是對於number的處理,其他型別都是直接對映過去的,在這裡可以看到是使用了正規表示式對number的欄位長度進行匹配來區分Integer、Long、Double,如果沒有小數位,長度小於10的則為Integer,否則為Long,否則一律按Double處理,所以在定義Number的時候就注意按實際來吧,不要貪圖方便或者偷懶,否則生成之後Entity、xml都要做相應的修改。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com