基于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的代码生成的更多相关文章

  1. 基于mybatis的java代码生成存储过程

    问题: 项目中目前使用mybatis操作数据库,使用插件(mybatis-generator)自动生成代码,对于增改查,使用存储过程实现了一版本,方便使用. insert代码生成器用法: insert ...

  2. MyBatis学习总结(四)——MyBatis缓存与代码生成

    一.MyBatis缓存 缓存可以提高系统性能,可以加快访问速度,减轻服务器压力,带来更好的用户体验.缓存用空间换时间,好的缓存是缓存命中率高的且数据量小的.缓存是一种非常重要的技术. 1.0.再次封装 ...

  3. 基于Mybatis的Dao层的开发

    基于Mybatis的Dao层开发 SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要SqlSessionFa ...

  4. MyBatis中mybatis-generator代码生成的一般过程

    MyBatis框架的使用,可以参考我的文章: https://blog.csdn.net/JayInnn/article/details/81746571(基于Mybatis实现一个查库的接口) ht ...

  5. 分表需要解决的问题 & 基于MyBatis 的轻量分表落地方案

    分表:垂直拆分.水平拆分 垂直拆分:根据业务将一个表拆分为多个表. 如:将经常和不常访问的字段拆分至不同的表中.由于与业务关系密切,目前的分库分表产品均使用水平拆分方式. 水平拆分:根据分片算法将一个 ...

  6. 基于Mybatis分页插件PageHelper

    基于Mybatis分页插件PageHelper 1.分页插件使用 1.POM依赖 PageHelper的依赖如下.需要新的版本可以去maven上自行选择 <!-- PageHelper 插件分页 ...

  7. 基于Mybatis的Dao层开发

    转自:https://www.cnblogs.com/rodge-run/p/6528398.html 基于Mybatis的Dao层开发 SqlSessionFactoryBuilder用于创建 Sq ...

  8. 5.7 Liquibase:与具体数据库独立的追踪、管理和应用数据库Scheme变化的工具。-mybatis-generator将数据库表反向生成对应的实体类及基于mybatis的mapper接口和xml映射文件(类似代码生成器)

    一. liquibase 使用说明 功能概述:通过xml文件规范化维护数据库表结构及初始化数据. 1.配置不同环境下的数据库信息 (1)创建不同环境的数据库. (2)在resource/liquiba ...

  9. 基于mybatis的CRUD

    u  基于Mybatis的CRUD u  掌握MyBatis的结果类型-resultMap和resultType u  掌握MyBatis的参数类型 u  掌握#和$两种语法 1      基于myb ...

  10. 基于mybatis的BaseDao及BaseService深度结合(转)

    原文地址:http://zhaoshijie.iteye.com/blog/2003209 关键字:Mybatis通用DAO设计封装(mybatis) 说明: mybatis默认分页机制为逻辑分页,所 ...

随机推荐

  1. ajax中json格式数据如何朝后端发送数据

  2. ubuntu下共享wifi 使用kde5-nm-connection-editor

    1.首先按照正常的建立方法把wifi建立好,然后在软件中心搜索 network ,点击安装 kde5-nm-connection-editor. 2.在终端里输入kde5-nm-connection- ...

  3. sql server 基础语法4 实践练习+子查询

    drop table class create table class ( classId ) primary key not null, cName ) ) insert into class ', ...

  4. 2018icpc沈阳/gym101955 J How Much Memory Your Code Is Using? 签到

    题意: 给你定义一堆变量,计算一下这些变量共占了多少k内存. 题解: 按题意模拟即可,善用ceil() // // Created by melon on 2019/10/22. // #includ ...

  5. 在使用 Eclisp 生成 实体(sql Server) 出现错误 :Unable to locate JAR/zip in file system as specified by the driver definition: sqljdbc.jar.

    错误: 解决方法: 第一步:点击 JAR List 第二步:  点击  Remove  JAR/ZIP 第三步: 再添加一下 sqljdbc.jar

  6. C++——右值引用

    1.绑定到一个将要销毁的右值的引用——移动 2.左值表达式——对象的身份:右值表达式——对象的值 ; int& r=i; int&& rr=i;//×,左值不能绑定到右值引用 ...

  7. expand gcc case variable length

    daniel@daniel-mint ~/vex $ bash gen.sh 0x10 0x1F case 10: case 11: case 12: case 13: case 14: case 1 ...

  8. JavaScript实现10大算法可视化

    参考博客: https://www.cnblogs.com/Unknw/p/6346681.html#4195503 十大经典算法 一张图概括: 名词解释: n:数据规模 k:“桶”的个数 In-pl ...

  9. python学习笔记:网络请求——requests模块

    上面讲过的urllib模块太麻烦了,还有一个比较方便的模块,就是requests模块,好用到你怀疑人生·^_^,一定要会哦 需要安装,pip install requests即可,下面是request ...

  10. activiti7完成当前任务

    package com.zcc.acvitivi; import org.activiti.engine.ProcessEngine;import org.activiti.engine.Proces ...