赞
踩
我们在开发mybatis时,涉及到xml,和bean,mapper等的书写,copy改,花的时间多且会有Bug,考虑到这些代码都是机械式的,用生成的方式比较靠谱
mybatis官方推荐有了相应的生成工具org.mybatis.generator,以maven插件的形式生成,会生成很多的example类,也比较方便.
不过这篇要讲的是mybatis-plus的生成
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具
代码生成器是其其中一个核心功能,利用模板化,
快速入门
https://mp.baomidou.com/guide/generator.html#添加依赖
各配置的说明
https://mp.baomidou.com/config/generator-config.html#tableprefix
各种sample
https://github.com/baomidou/mybatis-plus-samples.git
mybatis-plus的集成说明
把sqlsessionFactory中的Configure 给替换了 ,入口为MybatisPlusAutoConfiguration
里面可不写xml,因为:org.apache.ibatis.session.Configuration中的 mappedStatements 方法addMappedStatement中把statement给加上了
为了使用mybatis-plus的生成代码,先要把其集成进来,这里是一个官方的例子.
https://github.com/baomidou/mybatis-plus-samples.git
查看其目录结构:
| pom.xml | +---src | +---main | | +---java | | | \---com | | | \---baomidou | | | \---mybatisplus | | | \---samples | | | \---quickstart | | | | QuickstartApplication.java | | | | | | | +---entity | | | | User.java | | | | | | | \---mapper | | | UserMapper.java | | | | | \---resources | | | application.yml | | | | | \---db | | data-h2.sql | | schema-h2.sql
这里和官网的有点不同,因为官网的采用了MAVEN的多模块,所以有继承,这边把继承相关的给copy过来
如parent官网的不同,
dependencyManagement,官网把其放在父pom.xml中,
copy过来后,就不会依赖父pom了,直接运行即可.
<?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"> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>mybatis-plus-sample-quickstart</artifactId> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <mybatisplus.version>3.0.8.4-SNAPSHOT</mybatisplus.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatisplus.version}</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>${mybatisplus.version}</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>${mybatisplus.version}</version> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
例子中没有用mysql,而是用了内存数据库org.h2.Driver中
下面建表和数据语句,即运行前会执行
data-h2.sql:
DELETE FROM user;
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');
schema-h2.sql:
DROP TABLE IF EXISTS user;
CREATE TABLE user
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
就只配置了db链接
# DataSource Config
spring:
datasource:
driver-class-name: org.h2.Driver
schema: classpath:db/schema-h2.sql
data: classpath:db/data-h2.sql
url: jdbc:h2:mem:test
username: root
password: test
User.java
原例子中用了lombok,这是简化java写法的插件,可用可不用,用的话要在ide中加上插件,比较麻烦,
所以我改成下下面:
public class User {
private Long id;
private String name;
private Integer age;
private String email;
...get/set
}
UserMapper.java
package com.baomidou.mybatisplus.samples.quickstart.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.samples.quickstart.entity.User;
public interface UserMapper extends BaseMapper<User> {
}
5,写好main和测试
QuickstartApplication.java
package com.baomidou.mybatisplus.samples.quickstart; import com.baomidou.mybatisplus.samples.quickstart.entity.User; import com.baomidou.mybatisplus.samples.quickstart.mapper.UserMapper; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import javax.annotation.Resource; import java.util.List; @SpringBootApplication @MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper") public class QuickstartApplication { @Resource private UserMapper userMapper; @Bean public void ss() { System.out.println(("----- selectAll method test ------")); List<User> userList = userMapper.selectList(null); userList.forEach(System.out::println); } public static void main(String[] args) { SpringApplication.run(QuickstartApplication.class, args); } }
6,执行
直接执行main即可,运行流程上,其会去加载 @Bean下的内容,所以会打印出内容
7,总结
所以,安装和使用非常的简单,上面中,只要配置了mapper的路径,其就能自动去扫,后面调用mapper类时,也就能直接使用了!!
官网也有生成代码的例子,不过过于简单,所以这里重新写一下.
实际上,这个生成工具是完全独立的,其不需要和spring结合,原理上来说,其是通过对已有表的查询,然后通过其 模板(beetl,velocity等) 渲染成对应的文
这个对应文件是一个文本文件.是.java,.html等后缀,其并不关心
系统自己有默认模板,我自己也写了一些模板,一些效果:
原文件目录:
| pom.xml | +---src | +---main | | +---java | | | \---com | | | \---example | | | \---demo | | | | DemoApplication.java | | | | | | | \---common | | | BaseController.java | | | BaseEntity.java | | | | | \---resources | | | application.yml | | | | | \---templates | | mapper.xml.btl | | page_info.js.btl | | | \---test | \---java | \---com | \---example | \---demo | CodeGeneratorTest.java
运行CodeGeneratorTest.java,生成后:
| pom.xml | +---src | +---main | | +---java | | | \---com | | | \---example | | | \---demo | | | | DemoApplication.java | | | | | | | +---common | | | | BaseController.java | | | | BaseEntity.java | | | | | | | \---mymodule | | | +---controller | | | | TUserController.java | | | | | | | +---entity | | | | TUser.java | | | | | | | +---mapper | | | | TUserMapper.java | | | | | | | \---service | | | | ITUserService.java | | | | | | | \---impl | | | TUserServiceImpl.java | | | | | \---resources | | | application.yml | | | | | +---mapper | | | \---mymodule | | | TUserMapper.xml | | | | | +---templates | | | mapper.xml.btl | | | page_info.js.btl | | | | | \---web | | \---mymodule | | TUser.js | | | \---test | \---java | \---com | \---example | \---demo | CodeGeneratorTest.java
说明,多了三个 mymodule文件夹
采用了mysql,所以要有一个mysql数据库
*pom.xml
<?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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.0</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> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.9</version> </dependency> <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-generator --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>com.ibeetl</groupId> <artifactId>beetl</artifactId> <version>2.9.3</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
BaseController和BaseEntity 是一个空的类,因为其默认生成的controller等需要用到的父类
btl是模板 page_info.js.btl(这是个自己写的模板,后面要指定的
/** * 初始化${cfg.bizChName}详情对话框 */ var ${cfg.bizEnBigName}InfoDlg = { ${cfg.bizEnName}InfoData : {} }; /** * 清除数据 */ ${cfg.bizEnBigName}InfoDlg.clearData = function() { this.${cfg.bizEnName}InfoData = {}; } $(function() { });
<?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="${package.Mapper}.${table.mapperName}"> <% if(enableCache){ %> <!-- 开启二级缓存 --> <cache type="org.mybatis.caches.ehcache.LoggingEhcache"/> <% } %> <% if(baseResultMap){ %> <!-- 通用查询映射结果 --> <resultMap id="BaseResultMap" type="${package.Entity}.${entity}"> <% for(field in table.fields){ %> <% /** 生成主键排在第一位 **/ %> <% if(field.keyFlag){ %> <id column="${field.name}" property="${field.propertyName}" /> <% } %> <% } %> <% for(field in table.commonFields){ %> <% /** 生成公共字段 **/ %> <result column="${field.name}" property="${field.propertyName}" /> <% } %> <% for(field in table.fields){ %> <% /** 生成普通字段 **/ %> <% if(!field.keyFlag){ %> <result column="${field.name}" property="${field.propertyName}" /> <% } %> <% } %> </resultMap> <% } %> <% if(baseColumnList){ %> <!-- 通用查询结果列 --> <sql id="Base_Column_List"> <% for(field in table.commonFields){ %> ${field.name}, <% } %> ${table.fieldNames} </sql> <% } %> </mapper>
*核心类CodeGeneratorTest
这个很重要
是在官方例子中改变过来的,加了模板,去除了不必要的内容.
package com.example.demo; 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.BeetlTemplateEngine; import java.util.*; // 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中 public class CodeGeneratorTest { public static void main(String[] args) { String author = "myname"; String dbUrl = "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false"; String dbDriver = "com.mysql.jdbc.Driver"; String uname = "root"; String pwd = "root"; String parentPackage = "com.example.demo"; String moduleName = "mymodule"; String tableName = "user"; // 代码生成器 AutoGenerator mpg = new AutoGenerator(); // 全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); gc.setOutputDir(projectPath + "/src/main/java"); //输出 文件路径 gc.setAuthor(author); gc.setOpen(false); mpg.setGlobalConfig(gc); // 数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl(dbUrl); // dsc.setSchemaName("public"); dsc.setDriverName(dbDriver); dsc.setUsername(uname); dsc.setPassword(pwd); mpg.setDataSource(dsc); // 包配置 PackageConfig pc = new PackageConfig(); pc.setModuleName(moduleName); pc.setParent(parentPackage); mpg.setPackageInfo(pc); // 自定义配置 InjectionConfig cfg = new InjectionConfig() { @Override public void initMap() { // to do nothing Map<String, Object> map = new HashMap<>(); map.put("bizChName", "用户"); map.put("bizEnBigName", "Tuser"); this.setMap(map); } }; // 如果模板引擎是 freemarker // String templatePath = "/templates/mapper.xml.ftl"; // 如果模板引擎是 velocity // String templatePath = "/templates/mapper.xml.vm"; // 如果模板引擎是 beetl String templatePath = "/templates/mapper.xml.btl"; // 自定义输出配置 List<FileOutConfig> focList = new ArrayList<>(); // 自定义配置会被优先输出 focList.add(new FileOutConfig(templatePath) { @Override public String outputFile(TableInfo tableInfo) { // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! return projectPath + "/src/main/resources/mapper/" + pc.getModuleName() + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; } }); focList.add(new FileOutConfig("/templates/page_info.js.btl") { @Override public String outputFile(TableInfo tableInfo) { // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! return projectPath + "/src/main/resources/web/" + pc.getModuleName() + "/" + tableInfo.getEntityName() + ".js"; } }); 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("com.example.demo.common.BaseEntity"); strategy.setEntityLombokModel(false); strategy.setEntityTableFieldAnnotationEnable(true); strategy.setRestControllerStyle(true); strategy.setSuperControllerClass("com.example.demo.common.BaseController"); strategy.setInclude(tableName); // 需要生成的表 strategy.setSuperEntityColumns("id"); strategy.setControllerMappingHyphenStyle(true); strategy.setTablePrefix(pc.getModuleName() + "_"); mpg.setStrategy(strategy); // mpg.setTemplateEngine(new FreemarkerTemplateEngine()); mpg.setTemplateEngine(new BeetlTemplateEngine()); mpg.execute(); } }
*生成逻辑是不依赖spring的,直接执行上面的main,即可生成
上面的是生成逻辑,生成了文件后,我们就可以启动spring来测试了,下面是一springboot的一些配置
*application.yml
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
dbcp2.pool-prepared-statements: true
dbcp2.max-open-prepared-statements: 20
*DemoApplication.java
下面 TUserMapper即是 上面生成的代码,所以可以先执行 上一步骤,再把这个文件加进来.
package com.example.demo; import com.example.demo.mymodule.entity.TUser; import com.example.demo.mymodule.mapper.TUserMapper; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import java.util.List; @SpringBootApplication @MapperScan(basePackages = {"com.example.demo.mymodule.mapper"}) public class DemoApplication { @Autowired private TUserMapper userMapper; @Bean public void testSelect() { System.out.println(("----- selectAll method test ------")); List<TUser> userList = userMapper.selectList(null); userList.forEach(System.out::println); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
默认entity,mapper,service,执行时可以直接生成,不过service,controller,html,js等最好都用template来处理!
讲一下模板,实际上就是实际要生成文件前的 模子
在模板里面,其变量可以直接使用,我这里是用了 beetl的模板引擎
其对象有 cfg,table等.直接使用即可,如:
/**
* 收集数据
*/
${cfg.bizEnBigName}InfoDlg.collectData = function() {
this
<% for(item in table.fields!){ %>
<% if(itemLP.last != true){ %>
.set('${item.propertyName}')
<% }else{ %>
.set('${item.propertyName}');
<% }} %>
}
ctf.bizEnBigName是自定义的,上面的 CodeGeneratorTest 里有初始化
table.fields 是生成器自己的 ,名字表达 很明白了,就是找到 表的字段.
生成器的内置对象具体请查看 https://mp.baomidou.com/config/generator-config.html#tableprefix
/** * 初始化${cfg.bizChName}详情对话框 */ var ${cfg.bizEnBigName}InfoDlg = { ${cfg.bizEnName}InfoData : {} }; /** * 清除数据 */ ${cfg.bizEnBigName}InfoDlg.clearData = function() { this.${cfg.bizEnName}InfoData = {}; } /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ ${cfg.bizEnBigName}InfoDlg.set = function(key, val) { this.${cfg.bizEnName}InfoData[key] = (typeof val == "undefined") ? $("#" + key).val() : val; return this; } /** * 设置对话框中的数据 * * @param key 数据的名称 * @param val 数据的具体值 */ ${cfg.bizEnBigName}InfoDlg.get = function(key) { return $("#" + key).val(); } /** * 关闭此对话框 */ ${cfg.bizEnBigName}InfoDlg.close = function() { parent.layer.close(window.parent.${cfg.bizEnBigName}.layerIndex); } /** * 收集数据 */ ${cfg.bizEnBigName}InfoDlg.collectData = function() { this <% for(item in table.fields!){ %> <% if(itemLP.last != true){ %> .set('${item.propertyName}') <% }else{ %> .set('${item.propertyName}'); <% }} %> } /** * 提交添加 */ ${cfg.bizEnBigName}InfoDlg.addSubmit = function() { this.clearData(); this.collectData(); //提交信息 var ajax = new $ax(Feng.ctxPath + "/${cfg.bizEnName}/add", function(data){ Feng.success("添加成功!"); window.parent.${cfg.bizEnBigName}.table.refresh(); ${cfg.bizEnBigName}InfoDlg.close(); },function(data){ Feng.error("添加失败!" + data.responseJSON.message + "!"); }); ajax.set(this.${cfg.bizEnName}InfoData); ajax.start(); } /** * 提交修改 */ ${cfg.bizEnBigName}InfoDlg.editSubmit = function() { this.clearData(); this.collectData(); //提交信息 var ajax = new $ax(Feng.ctxPath + "/${cfg.bizEnName}/update", function(data){ Feng.success("修改成功!"); window.parent.${cfg.bizEnBigName}.table.refresh(); ${cfg.bizEnBigName}InfoDlg.close(); },function(data){ Feng.error("修改失败!" + data.responseJSON.message + "!"); }); ajax.set(this.${cfg.bizEnName}InfoData); ajax.start(); } $(function() { });
生成代码在后台管理方面是运用非常广的,但有很多细节要处理
有些后台管理实现了 很复杂的生成功能,把代码生成做了可编辑的,但这样对扩展性限制 比较大,多生成几次,自己都不知道怎么开发了。所以我个人还是喜欢半自动化的,1是其它人好上手,同时好编辑,2是修改模板应该是常态,我们在做系统的过程中组件会一直增加,同时也会有重构的内容,
关于哪些模块更需要用到生成?mybatis-plus的生成功能明显是针对 mybatis的XML等去做的,但实际上java层面的生成交没有那么迫切,html和js等难以复用的模块才是要生成的主力模块。
生成的一个问题在于修改时怎么办,现在只能在初始时生成,后面如果修改了,则不敢去生成了,因为会覆盖后修改的代码,这也是关自动化的问题。不过金无足赤,总体来说还是OK的。
对于减少工作量来说,最好是复用,其次才是生成,同时文档说明全面,自己写了一堆的模板,哪些对哪些要搞清楚,
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。