前言
熟悉Spring框架的同学一定都知道MVC开发模式吧,控制器(Controller)、业务类(Service)、持久层(Repository)、数据库映射(Mapper)、各种DO类构成了我们服务端的代码。初学的时候,觉得新鲜手写这些东西不觉得有啥,但是写久了就会觉得很烦。好不容易在数据库中写完了一遍字段,在Java代码又要写一遍,不仅很枯燥,关键是还容易写错!!!
字段名称写错的痛,只有踩过这坑的人才能体会... ...
所以,如果有一个工具可以在我们创建完数据库表之后自动帮助我们生成各种类就好了。
这种工具不仅有,而且有很多,首先推荐一个最最简单的:
如果有用过bejson在线工具的小伙伴,应该很熟悉,不过这个工具有个缺点:
开发者需要自己手动创建代码目录和代码文件,然后一个一个将代码复制到文件中
所以为了让大家更省事一些,下面推荐的2种方法不仅可以生成代码,还可以直接创建好文件夹、代码类。这2种方法的效果差不多,自己看情况选用吧。
第一种:mybatis-plus-generator配合freemaker引擎
尝试一下🍓
SpringBoot项目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 https://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.7.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>SpringBoot-CodeGenerator</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringBoot-CodeGenerator</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.2.0</version>
</dependency>
<!-- freemaker引擎 -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.29</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</project>
resources目录
controller.java.ftl
package ${package.Controller};
import org.springframework.web.bind.annotation.RequestMapping;
<#if restControllerStyle>
import org.springframework.web.bind.annotation.RestController;
<#else>
import org.springframework.stereotype.Controller;
</#if>
<#if superControllerClassPackage??>
import ${superControllerClassPackage};
</#if>
/**
* @author ${author}
*/
<#if restControllerStyle>
@RestController
<#else>
@Controller
</#if>
@RequestMapping("<#if package.ModuleName??>/${package.ModuleName}</#if>/<#if controllerMappingHyphenStyle??>${controllerMappingHyphen}<#else>${table.entityPath}</#if>")
<#if kotlin>
class ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}()</#if>
<#else>
<#if superControllerClass??>
public class ${table.controllerName} extends ${superControllerClass} {
<#else>
public class ${table.controllerName} {
</#if>
}
</#if>
entity.java.ftl
package ${package.Entity};
<#list table.importPackages as pkg>
import ${pkg};
</#list>
<#if swagger2>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
</#if>
<#if entityLombokModel>
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
</#if>
/**
* ${table.comment!}
*
* @author ${author}
*/
<#if entityLombokModel>
@Data
<#if superEntityClass??>
@EqualsAndHashCode(callSuper = true)
<#else>
@EqualsAndHashCode(callSuper = false)
</#if>
@Accessors(chain = true)
</#if>
<#if table.convert>
@TableName("${table.name}")
</#if>
<#if swagger2>
@ApiModel(value="${entity}对象", description="${table.comment!}")
</#if>
<#if superEntityClass??>
public class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}></#if> {
<#elseif activeRecord>
public class ${entity} extends Model<${entity}> {
<#else>
public class ${entity} implements Serializable {
</#if>
private static final long serialVersionUID = 1L;
<#-- ---------- BEGIN 字段循环遍历 ---------->
<#list table.fields as field>
<#if field.keyFlag>
<#assign keyPropertyName="${field.propertyName}"/>
</#if>
<#if field.comment!?length gt 0>
<#if swagger2>
@ApiModelProperty(value = "${field.comment}")
<#else>
/**
* ${field.comment}
*/
</#if>
</#if>
<#if field.keyFlag>
<#-- 主键 -->
<#if field.keyIdentityFlag>
@TableId(value = "${field.name}", type = IdType.AUTO)
<#elseif idType??>
@TableId(value = "${field.name}", type = IdType.${idType})
<#elseif field.convert>
@TableId("${field.name}")
</#if>
<#-- 普通字段 -->
<#elseif field.fill??>
<#-- ----- 存在字段填充设置 ----->
<#if field.convert>
@TableField(value = "${field.name}", fill = FieldFill.${field.fill})
<#else>
@TableField(fill = FieldFill.${field.fill})
</#if>
<#elseif field.convert>
@TableField("${field.name}")
</#if>
<#-- 乐观锁注解 -->
<#if (versionFieldName!"") == field.name>
@Version
</#if>
<#-- 逻辑删除注解 -->
<#if (logicDeleteFieldName!"") == field.name>
@TableLogic
</#if>
private ${field.propertyType} ${field.propertyName};
</#list>
<#------------ END 字段循环遍历 ---------->
<#if !entityLombokModel>
<#list table.fields as field>
<#if field.propertyType == "boolean">
<#assign getprefix="is"/>
<#else>
<#assign getprefix="get"/>
</#if>
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}) {
</#if>
this.${field.propertyName} = ${field.propertyName};
<#if entityBuilderModel>
return this;
</#if>
}
</#list>
</#if>
<#if entityColumnConstant>
<#list table.fields as field>
public static final String ${field.name?upper_case} = "${field.name}";
</#list>
</#if>
<#if activeRecord>
@Override
protected Serializable pkVal() {
<#if keyPropertyName??>
return this.${keyPropertyName};
<#else>
return null;
</#if>
}
</#if>
<#if !entityLombokModel>
@Override
public String toString() {
return "${entity}{" +
<#list table.fields as field>
<#if field_index==0>
"${field.propertyName}=" + ${field.propertyName} +
<#else>
", ${field.propertyName}=" + ${field.propertyName} +
</#if>
</#list>
"}";
}
</#if>
}
mapper.java.ftl
package ${package.Mapper};
import ${package.Entity}.${entity};
import ${superMapperClassPackage};
/**
* @author ${author}
*/
<#if kotlin>
interface ${table.mapperName} : ${superMapperClass}<${entity}>
<#else>
public interface ${table.mapperName} extends ${superMapperClass}<${entity}> {
}
</#if>
mapper.xml.ftl
<?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>
<#if baseResultMap>
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="${package.Entity}.${entity}">
<#list table.fields as field>
<#if field.keyFlag><#--生成主键排在第一位-->
<id column="${field.name}" property="${field.propertyName}" />
</#if>
</#list>
<#list table.commonFields as field><#--生成公共字段 -->
<result column="${field.name}" property="${field.propertyName}" />
</#list>
<#list table.fields as field>
<#if !field.keyFlag><#--生成普通字段 -->
<result column="${field.name}" property="${field.propertyName}" />
</#if>
</#list>
</resultMap>
</#if>
<#if baseColumnList>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
<#list table.commonFields as field>
${field.name},
</#list>
${table.fieldNames}
</sql>
</#if>
</mapper>
service.java.ftl
package ${package.Service};
import ${package.Entity}.${entity};
import ${superServiceClassPackage};
/**
* @author ${author}
*/
<#if kotlin>
interface ${table.serviceName} : ${superServiceClass}<${entity}>
<#else>
public interface ${table.serviceName} extends ${superServiceClass}<${entity}> {
}
</#if>
serviceImpl.java.ftl
package ${package.ServiceImpl};
import ${package.Entity}.${entity};
import ${package.Mapper}.${table.mapperName};
import ${package.Service}.${table.serviceName};
import ${superServiceImplClassPackage};
import org.springframework.stereotype.Service;
/**
* @author ${author}
*/
@Service
<#if kotlin>
open class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>(), ${table.serviceName} {
}
<#else>
public class ${table.serviceImplName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}> implements ${table.serviceName} {
}
</#if>
上面这些文件直接创建在resources/generator1/templates
的目录下,把内容复制进去就行了。这些文件就是你代码生成的格式模板,当然自己也可以修改。
生成代码
package com.example.springbootcodegenerator.util;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Scanner;
/**
* mybatis plus 提供的代码生成器
* 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码
*
* @link https://mp.baomidou.com/guide/generator.html
*/
public class CodeGenerator {
// 数据库 URL
private static final String URL = "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC";
// 数据库驱动
private static final String DRIVER_NAME = "com.mysql.cj.jdbc.Driver";
// 数据库用户名
private static final String USERNAME = "root";
// 数据库密码
private static final String PASSWORD = "123456";
// @author 值
private static final String AUTHOR = "sun墨";
// 包的基础路径
private static final String BASE_PACKAGE_URL = "com.example.springbootcodegenerator";
// xml文件路径
private static final String XML_PACKAGE_URL = "/src/main/resources/mapper/";
// xml 文件模板
private static final String XML_MAPPER_TEMPLATE_PATH = "generator1/templates/mapper.xml";
// mapper 文件模板
private static final String MAPPER_TEMPLATE_PATH = "generator1/templates/mapper.java";
// entity 文件模板
private static final String ENTITY_TEMPLATE_PATH = "generator1/templates/entity.java";
// service 文件模板
private static final String SERVICE_TEMPLATE_PATH = "generator1/templates/service.java";
// serviceImpl 文件模板
private static final String SERVICE_IMPL_TEMPLATE_PATH = "generator1/templates/serviceImpl.java";
// controller 文件模板
private static final String CONTROLLER_TEMPLATE_PATH = "generator1/templates/controller.java";
public static void main(String[] args) {
AutoGenerator generator = new AutoGenerator();
// 全局配置
GlobalConfig globalConfig = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
globalConfig.setOutputDir(projectPath + "/src/main/java");
globalConfig.setAuthor(AUTHOR);
globalConfig.setOpen(false);
globalConfig.setFileOverride(false);
generator.setGlobalConfig(globalConfig);
// 数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setUrl(URL);
dataSourceConfig.setDriverName(DRIVER_NAME);
dataSourceConfig.setUsername(USERNAME);
dataSourceConfig.setPassword(PASSWORD);
generator.setDataSource(dataSourceConfig);
// 包配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setModuleName("gen");
packageConfig.setParent(BASE_PACKAGE_URL);
generator.setPackageInfo(packageConfig);
// 配置自定义代码模板
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setXml(XML_MAPPER_TEMPLATE_PATH);
templateConfig.setMapper(MAPPER_TEMPLATE_PATH);
templateConfig.setEntity(ENTITY_TEMPLATE_PATH);
templateConfig.setService(SERVICE_TEMPLATE_PATH);
templateConfig.setServiceImpl(SERVICE_IMPL_TEMPLATE_PATH);
templateConfig.setController(CONTROLLER_TEMPLATE_PATH);
generator.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
strategy.setInclude(scanner("表名"));
strategy.setSuperEntityColumns("id");
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(packageConfig.getModuleName() + "_");
generator.setStrategy(strategy);
generator.setTemplateEngine(new FreemarkerTemplateEngine());
generator.execute();
}
private static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
System.out.println(("请输入" + tip + ":"));
if (scanner.hasNext()) {
String ipt = scanner.next();
if (null != ipt && !"".equals(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
}
效果展示
运行这个类之后,输入表名
代码目录如下:
第二种:tk.mybatis配合maven插件
尝试一下🍓
SpringBoot项目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 https://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.7.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>SpringBoot-CodeGenerator</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringBoot-CodeGenerator</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.2.0</version>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.6</version>
<configuration>
<configurationFile>${basedir}/src/main/resources/generator2/generatorConfiguration.xml
</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
<dependencies>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.1.2</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
resources目录
config.properties
## 驱动
jdbc.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&useSSL=false
## 登录名
jdbc.user=root
## 登录密码
jdbc.password=123456
## 表名
table.name=t_test
## DO
entity.name=TTestDO
## Mapper
mapper.name=TTestMapper
generatorConfiguration.xml
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<properties resource="generator2/config.properties"/>
<classPathEntry location="/Users/wzhi/Desktop/tools/mysql-connector-java-8.0.11.jar"/>
<context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>
<plugin type="tk.mybatis.mapper.generator.MapperPlugin">
<property name="mappers" value="com.baomidou.mybatisplus.core.mapper.BaseMapper"/>
<!-- 插件默认的通用mapper <property name="mappers" itemValue="tk.mybatis.mapper.common.Mapper"/>-->
<property name="caseSensitive" value="true"/>
<property name="lombok" value="Getter,Setter,Builder,NoArgsConstructor,AllArgsConstructor"/>
</plugin>
<!-- 加载驱动 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="${jdbc.url}"
userId="${jdbc.daUser}" password="${jdbc.password}"/>
<javaModelGenerator targetPackage="com.example.springbootcodegenerator.entity"
targetProject="src/main/java"/>
<sqlMapGenerator targetPackage="mybatis/mapper"
targetProject="src/main/resources/">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<javaClientGenerator targetPackage="com.example.springbootcodegenerator.mapper"
targetProject="src/main/java"
type="XMLMAPPER">
</javaClientGenerator>
<table tableName="${table.name}" domainObjectName="${entity.name}"
mapperName="${mapper.name}">
<generatedKey column="id" sqlStatement="Mysql" identity="true"/>
</table>
</context>
</generatorConfiguration>
上面两个文件配置好之后,如果使用的是idea,且装有maven插件,在maven 的Plugins中会有一个mybatis-generator选项,点击mybatis-generator :generate
即可运行
没有这个插件也不要紧,运行下面这个命令也可以
mvn mybatis-generator:generate
效果展示
总结一下
总体来说,这两种方式的效果都是差不多的。第一种方式可以自己创建任意文件,第二种方式专注于配置。个人喜欢第二种方式,不想写代码而已🤤
问题总结
有些同学在使用的时候遇到一些问题,有的是只创建了文件夹,有的是生成的entity不对,如下图:
这里我统一回复一下:
问题1、生成的是空目录
有两种可能,第一种是你们的配置不对,可以详细对照一下我的配置;第二种就是输出路径不对,生成的文件没法输出。
问题2、第一个方法的模版有问题
我看了下entity.java.ftl的内容确实不对,估计是当时复制错了,已改正,谢谢提醒。
文末小彩蛋,自己花一个星期做的小网站,放出来给大家看看,网址如下:
http://47.120.49.119:8080