基于mybatis-plus的代码生成
基于mybatis-plus的代码生成
前言
随着敏捷开发模式的推广,伴着日益增长的需求,日常工作中我们越来越注重效率和便捷性。今天我们就来探讨下如何自动生成代码,准确地说是如何依赖数据库生成我们的entity、mapper、mybatis xml、service、serviceImpl、controller,搭建我们的项目模板,提高我们的开发效率。这里我们的实现方式是基于mybatis-plus来实现的,废话少说,直接开始吧。
创建项目
这里创建的maven项目,核心依赖如下:
<properties>
<mybatis-plus.version>2.1.9</mybatis-plus.version>
<mybatis-plus-generator.version>2.1.9</mybatis-plus-generator.version>
</properties>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generate</artifactId>
<version>${mybatis-plus-generator.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
这里需要还需要引入slf4j-log4j12和slf4j-api的依赖,否则会报找不到org/slf4j/LoggerFactory的错误:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.1</version>
</dependency>
因为要通过数据库自动构建代码,所以数据库驱动必不可少,这里演示用的是mysql数据库:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
项目创建完后,下面我们要编写代码自动生成器。
导入代码模板
在编写自动生成器之前,更重要的工作是导入代码模板,模板决定了后续你代码生成后的样式,如果第一次接触,先不要考虑如何自己构建模板,我们先照猫画虎改一个呗:
entity.java的模板:
文件名及路径:
- mybatis-templates/entity.java.vm
package ${package.Entity};
#if(${activeRecord})
import java.io.Serializable;
#end
#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
#if(${entityLombokModel})
import com.baomidou.mybatisplus.annotations.Version;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
#end
/**********************************************
* 描述:$!{table.comment}
*
* @author ${author}
* @version 1.0
* @date: ${date}
*********************************************/
#if(${entityLombokModel})
@Data
@Accessors(chain = true)
#end
#if(${table.convert})
@TableName("${table.name}")
#end
#if(${superEntityClass})
public class ${entity} implements Serializable {
#elseif(${activeRecord})
public class ${entity} extends Model<${entity}> {
#else
public class ${entity} implements Serializable {
#end
private static final long serialVersionUID = 1L;
## ---------- BEGIN 字段循环遍历 ----------
#foreach($field in ${table.fields})
#if(${field.keyFlag})
#set($keyPropertyName=${field.propertyName})
#end
#if("$!field.comment" != "")
/**
* ${field.comment}
*/
#end
#if(${field.keyFlag})
## 主键
#if(${field.keyIdentityFlag})
@TableId(value="${field.name}", type= IdType.AUTO)
#elseif(${field.convert})
@TableId("${field.name}")
#end
## 普通字段
#elseif(${field.fill})
## ----- 存在字段填充设置 -----
#if(${field.convert})
@TableField(value = "${field.name}", fill = FieldFill.${field.fill})
#else
@TableField(fill = FieldFill.${field.fill})
#end
#elseif(${field.convert})
@TableField("${field.name}")
#end
## 乐观锁注解
#if(${versionFieldName}==${field.name})
@Version
#end
## 逻辑删除注解
#if(${logicDeleteFieldName}==${field.name})
@TableLogic
#end
private ${field.propertyType} ${field.propertyName};
#end
## ---------- END 字段循环遍历 ----------
#if(!${entityLombokModel})
#foreach($field in ${table.fields})
#if(${field.propertyType.equals("boolean")})
#set($getprefix="is")
#else
#set($getprefix="get")
#end
public ${field.propertyType} ${getprefix}${field.capitalName}() {
return ${field.propertyName};
}
#if(${entityBuilderModel})
public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
#else
public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
#end
this.${field.propertyName} = ${field.propertyName};
#if(${entityBuilderModel})
return this;
#end
}
#end
#end
#if(${entityColumnConstant})
#foreach($field in ${table.fields})
public static final String ${field.name.toUpperCase()} = "${field.name}";
#end
#end
#if(!${entityLombokModel})
@Override
public String toString() {
return "${entity}{" +
#foreach($field in ${table.fields})
#if($!{velocityCount}==1)
"${field.propertyName}=" + ${field.propertyName} +
#else
", ${field.propertyName}=" + ${field.propertyName} +
#end
#end
"}";
}
#end
}
mapper.java的模板:
文件名及路径:
- mybatis-templates/mapper.java.vm
package ${package.Mapper};
/**********************************************
* 描述:$!{table.comment}
*
* @author ${author}
* @version 1.0
* @date: ${date}
*********************************************/
public interface ${table.mapperName} {
}
service.java的模板:
文件名及路径:
- mybatis-templates/service.java.vm
package ${package.Service};
import ${package.Entity}.${entity};
/**********************************************
* 描述:$!{table.comment}
*
* @author ${author}
* @version 1.0
* @date: ${date}
*********************************************/
#if(${kotlin})
interface ${table.serviceName}
#else
public interface ${table.serviceName} {
}
#end
servicempl.java的模板:
文件名及路径:
- mybatis-templates/serviceImpl.java.vm
package ${package.ServiceImpl};
import ${package.Entity}.${entity};
import ${package.Mapper}.${table.mapperName};
import ${package.Service}.${table.serviceName};
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**********************************************
* 描述:$!{table.comment}
*
* @author ${author}
* @version 1.0
* @date: ${date}
*********************************************/
@Service("")
public class ${table.serviceImplName} implements ${table.serviceName} {
private static Logger _log = LoggerFactory.getLogger(${table.serviceImplName}.class);
}
controller.java的模板:
文件名及路径:
- mybatis-templates/controller.java.vm
package ${package.Controller};
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.beans.factory.annotation.Autowired;
import ${package.Service}.${table.serviceName};
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
#if(${restControllerStyle})
import org.springframework.web.bind.annotation.RestController;
#else
import org.springframework.stereotype.Controller;
#end
#if(${superControllerClassPackage})
import ${superControllerClassPackage};
#end
/**********************************************
* 描述:$!{table.comment}
*
* @author ${author}
* @version 1.0
* @date: ${date}
*********************************************/
#if(${restControllerStyle})
@RestController
#else
@Controller
#end
@Api(value = "$!{table.comment}", description = "$!{table.comment}")
@RequestMapping("#if(${package.ModuleName})/${package.ModuleName}#end/#if(${controllerMappingHyphenStyle})${controllerMappingHyphen}#else${table.entityPath}#end")
#if(${superControllerClass})
public class ${table.controllerName} {
#else
public class ${table.controllerName} {
#end
private static Logger _log = LoggerFactory.getLogger(${table.controllerName}.class);
@Autowired
private ${table.serviceName} service;
}
肯定有小伙伴在疑惑,上面的代码都是神马东西,我在了解mybatis-plus自动生成代码之前,第一次看到如上代码也是一脸懵逼。其实上面这些模板都是基于java的模板引擎 Velocity 编写的, Velocity是一个基于java的模板引擎(template engine)。它允许任何人仅仅简单的使用模板语言(template language)来引用由java代码定义的对象。 这里就不过多说明了,因为我也不是特别熟悉,有兴趣的小伙伴可以自行研究。
如果你不知道该如何改,那就先别动,等我们写完了自动生成器,你自然就知道咋改了,就算不知道,你也可以改着试呀。
编写代码自动生成器
我们的传统是先写代码,后解释,代码如下:
package io.github.syske.mybatisplus;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.DbType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import io.github.syske.mybatisplus.util.PropertiesFileUtil;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
/**
* @program: mybatisPlus
* @description: 代码生成器
* @author: liu yan
* @create: 2019-11-30 12:13
*/
public class MybatisPlusGenerator {
private static String JDBC_DRIVER = PropertiesFileUtil.getInstance("generator").get("generator.jdbc.driver");
private static String JDBC_URL = PropertiesFileUtil.getInstance("generator").get("generator.jdbc.url");
private static String JDBC_USERNAME = PropertiesFileUtil.getInstance("generator").get("generator.jdbc.username");
private static String JDBC_PASSWORD = PropertiesFileUtil.getInstance("generator").get("generator.jdbc.password");
private static String PROEJCT_NAME = "mybatisplus";//项目文件夹名称,我的项目名称就是mybatisplus
private static String BASE_PACKAGE_NAME="io.github.syske"; // 包前缀,也就是你的代码要生成在那个包底下
public static void main(String[] args) {
String[] table = {"user"}; // 表名称,也就是你要生成那些表的代码,我这里配置了一个表
String modelPath = "./"; // 模块路径,因为我就一个模块所以路径就是./,如果你的项目有多个模块,那就要指定你的模块名称
for(int i = 0 ; i<table.length;i++) {
shell(modelPath, table[i]);
}
}
private static void shell(String modelPath, String tabName){
File file = new File(modelPath);
String path = file.getAbsolutePath();
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
gc.setOutputDir(path+"/src/main/java");
gc.setOpen(false);
gc.setFileOverride(true);
gc.setActiveRecord(true);
gc.setEnableCache(true);// XML 二级缓存
gc.setBaseResultMap(true);// XML ResultMap
gc.setBaseColumnList(true);// XML columList
gc.setAuthor("syske");
// 自定义文件命名,注意 %s 会自动填充表实体属性!
gc.setMapperName("%sMapper");
gc.setXmlName("%sMapper");
gc.setServiceName("%sService");
gc.setServiceImplName("%sServiceImpl");
gc.setControllerName("%sController");
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
// 数据源类型orcale和mysql是不一样的
dsc.setDbType(DbType.MYSQL);
dsc.setDriverName(JDBC_DRIVER);
dsc.setUsername(JDBC_USERNAME);
dsc.setPassword(JDBC_PASSWORD);
dsc.setUrl(JDBC_URL);
mpg.setDataSource(dsc);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略
strategy.setInclude(new String[] {tabName}); // 需要生成的表
mpg.setStrategy(strategy);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setParent(BASE_PACKAGE_NAME);
pc.setController("controller");
pc.setEntity("dao.model");
pc.setMapper("dao.mapper");
pc.setService("service");
pc.setServiceImpl("service.impl");
pc.setXml("dao.mapper");
pc.setModuleName(PROEJCT_NAME);
mpg.setPackageInfo(pc);
// 注入自定义配置,可以在 VM 中使用 cfg.abc 【可无】
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
this.setMap(map);
}
};
mpg.setCfg(cfg);
//多模块
TemplateConfig tc = getTemplateConfig(gc,pc,modelPath,tabName, false);
if (tc.getMapper() == null && tc.getXml() == null && tc.getService() == null &&
tc.getServiceImpl() == null && tc.getController() == null && tc.getEntity(false) == null) {
return;
}
// 关闭默认 xml 生成,调整生成 至 根目录(单模块)
mpg.setTemplate(tc);
// 自定义模板配置,可以 copy 源码 mybatis-plus/src/main/resources/template 下面内容修改,
// 放置自己项目的 src/main/resources/template 目录下, 默认名称一下可以不配置,也可以自定义模板名称
// 如上任何一个模块如果设置 空 OR Null 将不生成该模块。
// 执行生成
mpg.execute();
// 打印注入设置【可无】
System.err.println(mpg.getCfg().getMap().get("abc"));
}
/**
* 控制包生成的路径与是否覆盖生成
* @param gc // 全局配置
* @param pc 包配置
* @param model model名
* @param tabName 表名
* @param isCover 是否覆盖生成代码
* @return TemplateConfig
*/
private static TemplateConfig getTemplateConfig(GlobalConfig gc, PackageConfig pc, String model, String tabName, boolean isCover) {
TemplateConfig tc = new TemplateConfig();
String entity = getName(tabName,"_");
String path = model + "/src/main/java/" +replace( pc.getParent());
if (!isCover) {
if (model.endsWith("dao")) {
String mapperPath =path + "/" + replace(pc.getMapper()) + "/" + gc.getMapperName().replace("%s",entity) + ".java";
if (isExists(mapperPath)) {
tc.setMapper(null);
System.out.println(gc.getMapperName().replace("%s",entity) + ".java 文件已存在");
}
String modelPath = path + "/" + replace(pc.getEntity()) + "/" + entity + ".java";
if (isExists(modelPath)) {
tc.setEntity(null);
System.out.println(entity + ".java 文件已存在");
}
tc.setController(null);
tc.setXml(null);
tc.setService(null);
tc.setServiceImpl(null);
} else if (model.endsWith("api")) {
String servicePath = path + "/" +replace(pc.getService()) + "/" + gc.getServiceName().replace("%s",entity) + ".java";
if (isExists(servicePath)) {
tc.setService(null);
System.out.println(gc.getServiceName().replace("%s",entity) + ".java 文件已存在");
}
tc.setController(null);
tc.setEntity(null);
tc.setServiceImpl(null);
tc.setMapper(null);
tc.setXml(null);
} else if (model.endsWith("service")) {
String serviceImplPath = path + "/" +replace(pc.getServiceImpl()) + "/" + gc.getServiceImplName().replace("%s",entity) + ".java";
if (isExists(serviceImplPath)) {
tc.setServiceImpl(null);
System.out.println(gc.getServiceImplName().replace("%s",entity) + ".java 文件已存在");
}
String mapperXmlPath =path + "/" + replace(pc.getXml()) + "/" + gc.getXmlName().replace("%s",entity) + ".xml";
if (isExists(mapperXmlPath)) {
tc.setXml(null);
System.out.println(gc.getXmlName().replace("%s",entity) + ".xml 文件已存在");
}
tc.setController(null);
tc.setService(null);
tc.setMapper(null);
tc.setEntity(null);
}else if (model.endsWith("web")) {
String controllerPath = path + "/" +replace(pc.getController()) + "/" + gc.getControllerName().replace("%s",entity) + ".java";;
if (isExists(controllerPath)) {
tc.setController(null);
System.out.println(gc.getControllerName().replace("%s",entity) + ".java 文件已存在");
}
tc.setMapper(null);
tc.setXml(null);
tc.setService(null);
tc.setServiceImpl(null);
tc.setEntity(null);
}
} else {
if (model.endsWith("dao")) {
tc.setController(null);
tc.setService(null);
tc.setXml(null);
tc.setServiceImpl(null);
} else if (model.endsWith("api")) {
tc.setController(null);
tc.setEntity(null);
tc.setServiceImpl(null);
tc.setMapper(null);
tc.setXml(null);
} else if (model.endsWith("service")) {
tc.setController(null);
tc.setService(null);
tc.setMapper(null);
tc.setEntity(null);
} else if (model.endsWith("web")) {
tc.setMapper(null);
tc.setXml(null);
tc.setService(null);
tc.setServiceImpl(null);
tc.setEntity(null);
}
}
return tc;
}
/**
* 将点替换为斜杠
* @param name
* @return
*/
private static String replace(String name) {
return name.replace(".","/");
}
/**
* 判断文件是否存在
* @param path 路径
* @return
*/
private static boolean isExists(String path) {
File file = new File(path);
return file.exists();
}
/**
* 根据驼峰命名,首字母大写
* @param tabName 原名
* @return 返回生成后的名字
* 例如:user_info 返回 UserInfo
*/
public static String getName(String tabName, String reChar) {
String[] arr = tabName.split(reChar);
String str = "";
for (int i = 0; i < arr.length; i++ ) {
String startChar = arr[i].substring(0,1).toUpperCase();
String lastChar = arr[i].substring(1, arr[i].length());
String newStr = startChar + lastChar;
str += newStr;
}
return str;
}
}
解释
最前面的代码是获取数据库的配置,我是写在properties文件中的,当然你也可以直接写在代码中:
generator.jdbc.driver=com.mysql.cj.jdbc.Driver
generator.jdbc.url=jdbc:mysql://127.0.0.1:3307/test?characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=Asia/Shanghai
generator.jdbc.username=root
generator.jdbc.password=root
以上注释已经够详细了,我就不做过多说明,下来我说下我遇到的问题:
第一个问题
错误信息
java.sql.SQLException: The server time zone value '?й???????' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
刚开始我以为是编码问题,改了成utf-8,发现不行,然后上网查资料,发现是时区的问题,解决方法是在mysql url中加入如下参数:
zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=Asia/Shanghai
第二个问题
错误信息
java.sql.SQLSyntaxErrorException: Unknown error 1146
找了很多资料没有找到解决方法,然后在代码中找到如下设置:
dsc.setDbType(DbType.ORACLE);
因为我是mysql数据库,所以要改成:
dsc.setDbType(DbType.MYSQL);
如果你用的是orcale,记得改成orcale
结语
如果没有报错,那你的代码模板已经完成了,如果你还想进一步定制完善,好好去研究 Velocity ,编写更符合自己需求的代码模板吧!
不知道各位小伙伴感觉怎么样,但我是真实地感受到用mybatis-plus构建项目的便利性,而且如果的模板够完善,生成基于单表的增删改查基本接口是没有什么压力的。好了,今天就到这里吧,我要去学习了
基于mybatis-plus的代码生成的更多相关文章
- 基于mybatis的java代码生成存储过程
问题: 项目中目前使用mybatis操作数据库,使用插件(mybatis-generator)自动生成代码,对于增改查,使用存储过程实现了一版本,方便使用. insert代码生成器用法: insert ...
- MyBatis学习总结(四)——MyBatis缓存与代码生成
一.MyBatis缓存 缓存可以提高系统性能,可以加快访问速度,减轻服务器压力,带来更好的用户体验.缓存用空间换时间,好的缓存是缓存命中率高的且数据量小的.缓存是一种非常重要的技术. 1.0.再次封装 ...
- 基于Mybatis的Dao层的开发
基于Mybatis的Dao层开发 SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要SqlSessionFa ...
- MyBatis中mybatis-generator代码生成的一般过程
MyBatis框架的使用,可以参考我的文章: https://blog.csdn.net/JayInnn/article/details/81746571(基于Mybatis实现一个查库的接口) ht ...
- 分表需要解决的问题 & 基于MyBatis 的轻量分表落地方案
分表:垂直拆分.水平拆分 垂直拆分:根据业务将一个表拆分为多个表. 如:将经常和不常访问的字段拆分至不同的表中.由于与业务关系密切,目前的分库分表产品均使用水平拆分方式. 水平拆分:根据分片算法将一个 ...
- 基于Mybatis分页插件PageHelper
基于Mybatis分页插件PageHelper 1.分页插件使用 1.POM依赖 PageHelper的依赖如下.需要新的版本可以去maven上自行选择 <!-- PageHelper 插件分页 ...
- 基于Mybatis的Dao层开发
转自:https://www.cnblogs.com/rodge-run/p/6528398.html 基于Mybatis的Dao层开发 SqlSessionFactoryBuilder用于创建 Sq ...
- 5.7 Liquibase:与具体数据库独立的追踪、管理和应用数据库Scheme变化的工具。-mybatis-generator将数据库表反向生成对应的实体类及基于mybatis的mapper接口和xml映射文件(类似代码生成器)
一. liquibase 使用说明 功能概述:通过xml文件规范化维护数据库表结构及初始化数据. 1.配置不同环境下的数据库信息 (1)创建不同环境的数据库. (2)在resource/liquiba ...
- 基于mybatis的CRUD
u 基于Mybatis的CRUD u 掌握MyBatis的结果类型-resultMap和resultType u 掌握MyBatis的参数类型 u 掌握#和$两种语法 1 基于myb ...
- 基于mybatis的BaseDao及BaseService深度结合(转)
原文地址:http://zhaoshijie.iteye.com/blog/2003209 关键字:Mybatis通用DAO设计封装(mybatis) 说明: mybatis默认分页机制为逻辑分页,所 ...
随机推荐
- Qt对话框部分学习
一.对话框部分常用内容 颜色对话框.文件对话框.字体对话框.输入对话框.消息对话框.进度对话框.错误对话框.向导对话框. 二.代码部分 //widget.h #ifndef MYWIDGET_H ...
- axios拦截设置和错误处理
目前想出的处理接口请求进行全局错误提示 的最佳方案,axios整体配置如下 1.基于axiso.interceptors进行响应拦截: 主要负责全局提示错误 axios.interceptors.re ...
- 【已转移】【Java架构:基础技术】一篇文章搞掂:Java 8
本文篇幅较长,建议合理利用右上角目录进行查看(如果没有目录请刷新). 一.Java的历史与演变 目的:为完全了解Java,需要理解Java的诞生原因.成型动力以及他继承的思想. 计算机语言的创新与发展 ...
- Javascript 数组 数字 字符串 时间等使用
1.Javascript 数组API 1. //定义数组 2. var pageIds = new Array(); 3. pageIds.push('A'); 5. 数组长度 6. pageIds. ...
- hive内部表&外部表介绍
未被external修饰的是内部表(managed table),被external修饰的为外部表(external table):区别:内部表数据由Hive自身管理,外部表数据由HDFS管理:内部表 ...
- linux卸载
centos平台 yum remove xxxrpm包,rpm -e xxxtar包直接删除该文件或者make uninstall xxx
- SecureCRT key登录linux ssh设置
一.首先用secureCrt创建密钥 1.使用SecureCRT创建私钥和公钥. SecureCRT quick Connect-> Authentiation -> Public Key ...
- webstorm 分屏
- @RequestParam 引发的编译问题
在使用SpringMVC绑定基本类型(如String,Integer等)参数时,应通过@RequestParam注解指定具体的参数名称,否则,当源代码在非debug模式下编译后,运行时会引发Handl ...
- Oracle查看有锁进程,删除锁
查看锁表进程SQL语句1: select sess.sid, sess.serial#, lo.oracle_username, lo.os_user_name, ao ...