MyBatis Generator使用总结
- 介绍
- 具体使用
- 数据准备
- 插件引入
- 配置
- 条件构建讲解
- demo地址
介绍
MyBatis Generator (MBG) 是 MyBatis 的代码生成器。它能够根据数据库表,自动生成 java 实体类、dao 层接口(mapper 接口)及mapper.xml文件。
具体使用
数据准备
创建数据库(8.0版本)mybatis,并添加一张表rbac_user,用于测试
CREATE TABLE `rbac_user` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`name` varchar(100) DEFAULT NULL COMMENT '用户名',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`nick_name` varchar(100) DEFAULT NULL COMMENT '昵称',
`remark` varchar(100) DEFAULT NULL COMMENT '备注',
`create_time` date DEFAULT NULL COMMENT '创建时间',
`status` char(1) DEFAULT NULL COMMENT '状态',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表';
如图所示:
插件引入
创建一个SpringBoot项目这里使用的spring boot版本是2.1.3.RELEASE,将mybatis 插件引入
<!--SpringBoot通用依赖模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--SpringBoot整合MyBatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!--MyBatis分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.5</version>
</dependency>
<!--集成druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.9</version>
</dependency>
<!-- MyBatis 生成器 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.4.1</version>
</dependency>
<!--Mysql数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
<!--springfox swagger官方Starter-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
配置
1、对项目的application.yml进行配置:
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: root
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER
mybatis:
mapper-locations:
- classpath:dao/*.xml
configuration:
# 下划线自动转驼峰
map-underscore-to-camel-case: true
logging:
level:
root: info
com.sheep: debug
2、创建配置文件为generator.properties,添加数据库配置信息,用于代码生成器配置使用:
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.connectionURL=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
jdbc.userId=root
jdbc.password=root
3、创建代码生成器配置文件generatorConfig.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!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="generator.properties"/>
<context id="MySqlContext" targetRuntime="MyBatis3" defaultModelType="flat">
<!-- 配置SQL语句中的前置分隔符 -->
<property name="beginningDelimiter" value="`"/>
<!-- 配置SQL语句中的后置分隔符 -->
<property name="endingDelimiter" value="`"/>
<!-- 配置生成Java文件的编码 -->
<property name="javaFileEncoding" value="UTF-8"/>
<!-- 为模型生成序列化方法 -->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
<!-- 为生成的Java模型创建一个toString方法 -->
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
<!-- 生成mapper.xml时覆盖原文件 -->
<plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />
<commentGenerator type="com.macro.mall.CommentGenerator">
<!-- 是否阻止生成的注释 -->
<property name="suppressAllComments" value="true"/>
<!-- 是否阻止生成的注释包含时间戳 -->
<property name="suppressDate" value="true"/>
<!-- 是否添加数据库表的备注信息 -->
<property name="addRemarkComments" value="true"/>
</commentGenerator>
<!--配置数据库连接-->
<jdbcConnection driverClass="${jdbc.driverClass}"
connectionURL="${jdbc.connectionURL}"
userId="${jdbc.userId}"
password="${jdbc.password}">
<!--解决mysql驱动升级到8.0后不生成指定数据库代码的问题-->
<property name="nullCatalogMeansCurrent" value="true"/>
</jdbcConnection>
<!--指定生成model的路径-->
<javaModelGenerator targetPackage="com.sheep.mbg.model" targetProject="learn-mybatis\src\main\java"/>
<!--指定生成mapper.xml的路径-->
<sqlMapGenerator targetPackage="com.sheep..mbg.mapper" targetProject="learn-mybatis\src\main\resources"/>
<!--指定生成mapper接口的的路径-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.sheep.mbg.mapper" targetProject="learn-mybatis\src\main\java"/>
<!--配置需要生成的表,生成全部表tableName设为% 还可以指定统一前缀的表 如 rbac_% 表示所有rbac_开始的所有表-->
<table tableName="rbac_%">
<generatedKey column="id" sqlStatement="MySql" identity="true"/>
</table>
</context>
</generatorConfiguration>
4、实际开发中,有的项目是需要接入swagger-ui的,所以字段注释就得自定义。建立一个注释自定义处理类CommentGenerator:
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.CompilationUnit;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.internal.DefaultCommentGenerator;
import org.mybatis.generator.internal.util.StringUtility;
import java.util.Properties;
/**
* 自定义注释生成器
*/
public class CommentGenerator extends DefaultCommentGenerator {
private boolean addRemarkComments = false;
private static final String EXAMPLE_SUFFIX = "Example";
private static final String MAPPER_SUFFIX = "Mapper";
private static final String API_MODEL_PROPERTY_FULL_CLASS_NAME = "io.swagger.annotations.ApiModelProperty";
/**
* 设置用户配置的参数
*/
@Override
public void addConfigurationProperties(Properties properties) {
super.addConfigurationProperties(properties);
this.addRemarkComments = StringUtility.isTrue(properties.getProperty("addRemarkComments"));
}
/**
* 给字段添加注释
*/
@Override
public void addFieldComment(Field field, IntrospectedTable introspectedTable,
IntrospectedColumn introspectedColumn) {
String remarks = introspectedColumn.getRemarks();
//根据参数和备注信息判断是否添加swagger注解信息
if (addRemarkComments && StringUtility.stringHasValue(remarks)) {
// addFieldJavaDoc(field, remarks);
//数据库中特殊字符需要转义
if (remarks.contains("\"")) {
remarks = remarks.replace("\"", "'");
}
//给model的字段添加swagger注解
field.addJavaDocLine("@ApiModelProperty(value = \"" + remarks + "\")");
}
}
/**
* 给model的字段添加注释
*/
private void addFieldJavaDoc(Field field, String remarks) {
//文档注释开始
field.addJavaDocLine("/**");
//获取数据库字段的备注信息
String[] remarkLines = remarks.split(System.getProperty("line.separator"));
for (String remarkLine : remarkLines) {
field.addJavaDocLine(" * " + remarkLine);
}
addJavadocTag(field, false);
field.addJavaDocLine(" */");
}
@Override
public void addJavaFileComment(CompilationUnit compilationUnit) {
super.addJavaFileComment(compilationUnit);
//只在model中添加swagger注解类的导入
if (!compilationUnit.getType().getFullyQualifiedName().contains(MAPPER_SUFFIX) && !compilationUnit.getType().getFullyQualifiedName().contains(EXAMPLE_SUFFIX)) {
compilationUnit.addImportedType(new FullyQualifiedJavaType(API_MODEL_PROPERTY_FULL_CLASS_NAME));
}
}
}
5、添加运行类Generator:
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class Generator {
public static void main(String[] args) throws Exception {
//MBG 执行过程中的警告信息
List<String> warnings = new ArrayList<String>();
//当生成的代码重复时,覆盖原代码
boolean overwrite = true;
//读取我们的 MBG 配置文件
InputStream is = Generator.class.getResourceAsStream("/generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(is);
is.close();
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
//创建 MBG
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
//执行生成代码
myBatisGenerator.generate(null);
//输出警告信息
for (String warning : warnings) {
System.out.println(warning);
}
}
}
6、运行后看结果:图中的路径是与配置文件相对应的。若不存在也会自动创建目录
7、可以看到model对象中的各个字段的注释。
8、还有另个自动生成的对象类:此对象是为了配合增删改查的条件构建用的。
9、看下生成的Mapper接口,这里生成了基本增删改查操作的接口,查询参数也应用了删改你的条件构建对象。
10、构建RbacUserService,RbacUserController。具体讲解下这个条件构建器如何使用:
public interface RbacUserService {
List<RbacUser> listAllUser();
int createUser(RbacUser user);
int updateUser(Integer id, RbacUser user);
int deleteUser(Integer id);
List<RbacUser> listUser(int pageNum, int pageSize);
RbacUser getUser(Integer id);
}
@Service
public class RbacUserServiceImpl implements RbacUserService {
@Autowired
private RbacUserMapper userMapper;
@Override
public List<RbacUser> listAllUser() {
return userMapper.selectByExample(new RbacUserExample());
}
@Override
public int createUser(RbacUser user) {
return userMapper.insert(user);
}
@Override
public int updateUser(Integer id, RbacUser user) {
user.setId(id);
return userMapper.updateByPrimaryKeySelective(user);
}
@Override
public int deleteUser(Integer id) {
return userMapper.deleteByPrimaryKey(id);
}
@Override
public List<RbacUser> listUser(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
return userMapper.selectByExample(new RbacUserExample());
}
@Override
public RbacUser getUser(Integer id) {
return userMapper.selectByPrimaryKey(id);
}
}
@Api("用户管理")
@Controller
@RequestMapping("/user")
public class RbacUserController {
private static final Logger LOGGER = LoggerFactory.getLogger(RbacUserController.class);
@Autowired
private RbacUserService userService;
@ApiOperation("获取所有用户列表")
@RequestMapping(value = "listAll", method = RequestMethod.GET)
@ResponseBody
public CommonResult<List<RbacUser>> getUserList() {
return CommonResult.success(userService.listAllUser());
}
@ApiOperation("添加用户")
@RequestMapping(value = "/create", method = RequestMethod.POST)
@ResponseBody
public CommonResult createUser(@RequestBody RbacUser user) {
CommonResult commonResult;
int count = userService.createUser(user);
if (count == 1) {
commonResult = CommonResult.success(user);
LOGGER.debug("createUser success:{}", user);
} else {
commonResult = CommonResult.failed("操作失败");
LOGGER.debug("createUser failed:{}", user);
}
return commonResult;
}
@ApiOperation("更新指定id用户信息")
@RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
@ResponseBody
public CommonResult updateUser(@PathVariable("id") Integer id, @RequestBody RbacUser user, BindingResult result) {
CommonResult commonResult;
int count = userService.updateUser(id, user);
if (count == 1) {
commonResult = CommonResult.success(user);
LOGGER.debug("updateUser success:{}", user);
} else {
commonResult = CommonResult.failed("操作失败");
LOGGER.debug("updateUser failed:{}", user);
}
return commonResult;
}
@ApiOperation("删除指定id的用户")
@RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
@ResponseBody
public CommonResult deleteUser(@PathVariable("id") Integer id) {
int count = userService.deleteUser(id);
if (count == 1) {
LOGGER.debug("deleteUser success :id={}", id);
return CommonResult.success(null);
} else {
LOGGER.debug("deleteUser failed :id={}", id);
return CommonResult.failed("操作失败");
}
}
@ApiOperation("分页查询用户列表")
@RequestMapping(value = "/list", method = RequestMethod.GET)
@ResponseBody
public CommonResult<CommonPage<RbacUser>> listUser(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum, @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
List<RbacUser> brandList = userService.listUser(pageNum, pageSize);
return CommonResult.success(CommonPage.restPage(brandList));
}
@ApiOperation("获取指定id的品牌详情")
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@ResponseBody
public CommonResult<RbacUser> user(@PathVariable("id") Integer id) {
return CommonResult.success(userService.getUser(id));
}
}
11、controller中的返回结果进行了统一,代码如下,可以自行修改:
public interface IErrorCode {
long getCode();
String getMessage();
}
public enum ResultCode implements IErrorCode {
SUCCESS(200, "操作成功"),
FAILED(500, "操作失败"),
VALIDATE_FAILED(404, "参数检验失败"),
UNAUTHORIZED(401, "暂未登录或token已经过期"),
FORBIDDEN(403, "没有相关权限");
private long code;
private String message;
private ResultCode(long code, String message) {
this.code = code;
this.message = message;
}
public long getCode() {
return code;
}
public String getMessage() {
return message;
}
}
public class CommonPage<T> {
private Integer pageNum;
private Integer pageSize;
private Integer totalPage;
private Long total;
private List<T> list;
/**
* 将PageHelper分页后的list转为分页信息
*/
public static <T> CommonPage<T> restPage(List<T> list) {
CommonPage<T> result = new CommonPage<T>();
PageInfo<T> pageInfo = new PageInfo<T>(list);
result.setTotalPage(pageInfo.getPages());
result.setPageNum(pageInfo.getPageNum());
result.setPageSize(pageInfo.getPageSize());
result.setTotal(pageInfo.getTotal());
result.setList(pageInfo.getList());
return result;
}
public Integer getPageNum() {
return pageNum;
}
public void setPageNum(Integer pageNum) {
this.pageNum = pageNum;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getTotalPage() {
return totalPage;
}
public void setTotalPage(Integer totalPage) {
this.totalPage = totalPage;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
public Long getTotal() {
return total;
}
public void setTotal(Long total) {
this.total = total;
}
}
public class CommonResult<T> {
private long code;
private String message;
private T data;
protected CommonResult() {
}
protected CommonResult(long code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
/**
* 成功返回结果
*
* @param data 获取的数据
*/
public static <T> CommonResult<T> success(T data) {
return new CommonResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
}
/**
* 成功返回结果
*
* @param data 获取的数据
* @param message 提示信息
*/
public static <T> CommonResult<T> success(T data, String message) {
return new CommonResult<T>(ResultCode.SUCCESS.getCode(), message, data);
}
/**
* 失败返回结果
*
* @param errorCode 错误码
*/
public static <T> CommonResult<T> failed(IErrorCode errorCode) {
return new CommonResult<T>(errorCode.getCode(), errorCode.getMessage(), null);
}
/**
* 失败返回结果
*
* @param message 提示信息
*/
public static <T> CommonResult<T> failed(String message) {
return new CommonResult<T>(ResultCode.FAILED.getCode(), message, null);
}
/**
* 失败返回结果
*/
public static <T> CommonResult<T> failed() {
return failed(ResultCode.FAILED);
}
/**
* 参数验证失败返回结果
*/
public static <T> CommonResult<T> validateFailed() {
return failed(ResultCode.VALIDATE_FAILED);
}
/**
* 参数验证失败返回结果
*
* @param message 提示信息
*/
public static <T> CommonResult<T> validateFailed(String message) {
return new CommonResult<T>(ResultCode.VALIDATE_FAILED.getCode(), message, null);
}
/**
* 未登录返回结果
*/
public static <T> CommonResult<T> unauthorized(T data) {
return new CommonResult<T>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data);
}
/**
* 未授权返回结果
*/
public static <T> CommonResult<T> forbidden(T data) {
return new CommonResult<T>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data);
}
public long getCode() {
return code;
}
public void setCode(long code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//为当前包下controller生成API文档
.apis(RequestHandlerSelectors.basePackage("com.sheep.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("SwaggerUI演示")
.description("learn-mybatis")
.contact(new Contact("sheep", "", "123@qq.com"))
.version("1.0")
.build();
}
}
12、运行服务:
13、访问 http://localhost:8080/swagger-ui.html
成功启动。
条件构建讲解
从上面可以看到MyBatis Generator (MBG) 为每个数据库表生成了一个对应的 Example 类。它主要用于生成动态 SQL 语句。
Example 类允许你构建复杂的 WHERE 子句,而无需直接在 mapper 文件中硬编码 SQL,它包含了很多用于构建 WHERE 子句的方法。每个方法都对应一个 SQL 比较运算符,比如 “=”, “<>”, “>”, “<”, “LIKE” 等。你可以动态地添加、修改或删除查询条件。使用 Example 类,你可以构建几乎任何类型的查询,包括带有 AND 和 OR 子句的查询,带有子查询的查询等。
在使用 Example 类时,你需要创建一个 Example 对象,然后通过调用该对象的 createCriteria() 方法创建一个 Criteria 对象。然后你可以在 Criteria 对象上调用各种方法来添加查询条件。
上面的案例基本上都没用到,下面我们就开始使用这个条件构建器:
上面的案例是查询的所有用户,我们基于这个进行条件查询。
比如 对名字进行模糊查询:
Criteria 对象创建后,可以调用里面的各个接口进行构建参数,这个里面会对每个参数生成 “=”, “<>”, “>”, “<”, “LIKE” 的查询接口。如图所示是上面的接口详情:可以看到默认的情况,不会加%的
再看addCriterion,可以看到是往全局变量list中插入不同的条件构建对象。
看下查询语句拼接效果:
若我们查询有多个条件的话,直接继续添加即可,例如我可以根据name模糊查询,还要查询小于指定时间创建的用户
上面的是不同条件进行and拼接的,若使用or进行拼接不同条件的话,需要换个方式:
每个条件都需要通过or创建Criteria 对象,然后再赋值,看下or接口就明白了:
比如 我们查询的话还需要根据指定的字段进行排序:
可以看到在mapper中的条件构建判断逻辑:
这个构建器只能用来生成简单的基于单表的 CRUD 操作的代码。对于更复杂的 SQL 操作,比如子查询、Group 查询和 Join 查询,MBG 并不直接支持。这些复杂查询需要你自己在 Mapper 的 xml 文件或基于注解编写相应的 SQL。
demo地址
项目地址: https://gitee.com/yang1112/learn-project.git
我额外还写了一个根据模板生成代码,包括service、dao、mapper,有兴趣的可以看看:
项目地址: https://gitee.com/yang1112/code-generator.git