本文完整代码已上传Github仓库:https://github.com/ouyangyewei/mybatis-codegen
MyBatis Generator官网:MyBatis Generator Core – Introduction to MyBatis Generator
借助MyBatis Generator(MBG)工具,可以基于数据库表结构自动生成Bean/Mapper/Mapper XML代码,简化了大量重复繁琐的开发步骤,相关信息如下:
MGB支持自定义插件,比如:自动生成代码时带上表/表字段注释、分页、Lombok
- package com.github.codegen;
-
- import org.apache.logging.log4j.util.Strings;
- import org.mybatis.generator.api.IntrospectedColumn;
- import org.mybatis.generator.api.IntrospectedTable;
- import org.mybatis.generator.api.dom.java.Field;
- import org.mybatis.generator.api.dom.java.TopLevelClass;
- import org.mybatis.generator.internal.DefaultCommentGenerator;
- import org.mybatis.generator.internal.util.StringUtility;
-
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.Properties;
-
- /**
- * 注释生成器
- *
- * 根据元数据表的字段注释给JavaBean字段添加注释
- *
- * @author ouyangyewei
- * @date 2021-09-01
- **/
- public class CommentGenerator extends DefaultCommentGenerator {
- private boolean addRemarkComments = false;
-
- /**
- * 设置用户配置的参数
- * @param properties
- */
- @Override
- public void addConfigurationProperties(Properties properties) {
- super.addConfigurationProperties(properties);
- this.addRemarkComments = StringUtility.isTrue(properties.getProperty("addRemarkComments"));
- }
-
- /**
- * 根据表注释设置类文件注释
- * @param topLevelClass
- * @param introspectedTable
- */
- private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
- public void addModelClassComment(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
- if (this.addRemarkComments) {
- topLevelClass.addJavaDocLine("/**");
- topLevelClass.addJavaDocLine(" * " + introspectedTable.getRemarks());
- topLevelClass.addJavaDocLine(" * @author MyBatis Generator");
- topLevelClass.addJavaDocLine(" * @date " + DATE_FORMAT.format(new Date()));
- topLevelClass.addJavaDocLine(" */");
- }
- }
-
- /**
- * 给字段添加注释
- * @param field
- * @param introspectedTable
- * @param introspectedColumn
- */
- @Override
- public void addFieldComment(Field field, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) {
- String remarks = introspectedColumn.getRemarks();
- // 根据参数和备注信息判断是否添加备注信息
- if (this.addRemarkComments && StringUtility.stringHasValue(remarks)) {
- // 数据库中特殊字符需要转义
- if (remarks.contains("\"")) {
- remarks = remarks.replace("\"","'");
- }
- // model的字段添加注解
- field.addJavaDocLine("/**" + Strings.LINE_SEPARATOR + " * " + remarks + Strings.LINE_SEPARATOR + " */");
- }
- }
- }
- package com.github.codegen;
-
- import org.mybatis.generator.api.IntrospectedTable;
- import org.mybatis.generator.api.PluginAdapter;
- import org.mybatis.generator.api.dom.java.*;
- import org.mybatis.generator.api.dom.xml.Attribute;
- import org.mybatis.generator.api.dom.xml.TextElement;
- import org.mybatis.generator.api.dom.xml.XmlElement;
-
- import java.util.List;
-
- /**
- * Mybatis Page Limit Plugin
- *
- * 用于为每个Example类添加offset和limit属性方法
- *
- * @author ouyangyewei
- * @date 2021-08-31
- **/
- public class LimitPlugin extends PluginAdapter {
-
- @Override
- public boolean validate(List
list) { - return true;
- }
-
- /**
- * 为每个Example类添加offset和rows属性已经set、get方法
- * @param topLevelClass
- * @param introspectedTable
- * @return
- */
- @Override
- public boolean modelExampleClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
- PrimitiveTypeWrapper integerWrapper = FullyQualifiedJavaType.getIntInstance().getPrimitiveTypeWrapper();
-
- Field limit = new Field("limit", integerWrapper);
- limit.setVisibility(JavaVisibility.PRIVATE);
- topLevelClass.addField(limit);
-
- Method setLimit = new Method("setLimit");
- setLimit.setVisibility(JavaVisibility.PUBLIC);
- setLimit.addParameter(new Parameter(integerWrapper, "limit"));
- setLimit.addBodyLine("this.limit = limit;");
- topLevelClass.addMethod(setLimit);
-
- Method getLimit = new Method("getLimit");
- getLimit.setVisibility(JavaVisibility.PUBLIC);
- getLimit.setReturnType(integerWrapper);
- getLimit.addBodyLine("return limit;");
- topLevelClass.addMethod(getLimit);
-
- Field offset = new Field("offset", integerWrapper);
- offset.setVisibility(JavaVisibility.PRIVATE);
- topLevelClass.addField(offset);
-
- Method setOffset = new Method("setOffset");
- setOffset.setVisibility(JavaVisibility.PUBLIC);
- setOffset.addParameter(new Parameter(integerWrapper, "offset"));
- setOffset.addBodyLine("this.offset = offset;");
- topLevelClass.addMethod(setOffset);
-
- Method getOffset = new Method("getOffset");
- getOffset.setVisibility(JavaVisibility.PUBLIC);
- getOffset.setReturnType(integerWrapper);
- getOffset.addBodyLine("return offset;");
- topLevelClass.addMethod(getOffset);
- return true;
- }
-
- /**
- * 为Mapper.xml的selectByExample添加limit
- * @param element
- * @param introspectedTable
- * @return
- */
- @Override
- public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
- XmlElement ifLimitNotNullElement = new XmlElement("if");
- ifLimitNotNullElement.addAttribute(new Attribute("test", "limit != null"));
-
- XmlElement ifOffsetNotNullElement = new XmlElement("if");
- ifOffsetNotNullElement.addAttribute(new Attribute("test", "offset != null"));
- ifOffsetNotNullElement.addElement(new TextElement("limit ${offset}, ${limit}"));
- ifLimitNotNullElement.addElement(ifOffsetNotNullElement);
-
- XmlElement ifOffsetNullElement = new XmlElement("if");
- ifOffsetNullElement.addAttribute(new Attribute("test", "offset == null"));
- ifOffsetNullElement.addElement(new TextElement("limit ${limit}"));
- ifLimitNotNullElement.addElement(ifOffsetNullElement);
-
- element.addElement(ifLimitNotNullElement);
- return true;
- }
- }
- package com.github.codegen;
-
- import org.mybatis.generator.api.IntrospectedColumn;
- import org.mybatis.generator.api.IntrospectedTable;
- import org.mybatis.generator.api.PluginAdapter;
- import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
- import org.mybatis.generator.api.dom.java.Interface;
- import org.mybatis.generator.api.dom.java.Method;
- import org.mybatis.generator.api.dom.java.TopLevelClass;
-
- import java.util.*;
-
- /**
- * A MyBatis Generator plugin to use Lombok's annotations.
- * For example, use @Data annotation instead of getter ands setter.
- *
- * @See https://github.com/softwareloop/mybatis-generator-lombok-plugin
- * @author Paolo Predonzani (http://softwareloop.com/)
- */
- public class LombokPlugin extends PluginAdapter {
-
- private final Collection
annotations; -
- /**
- * LombokPlugin constructor
- */
- public LombokPlugin() {
- this.annotations = new LinkedHashSet<>(Annotations.values().length);
- }
-
- /**
- * @param warnings list of warnings
- * @return always true
- */
- @Override
- public boolean validate(List
warnings) { - return true;
- }
-
- /**
- * Intercepts base record class generation
- *
- * @param topLevelClass the generated base record class
- * @param introspectedTable The class containing information about the table as
- * introspected from the database
- * @return always true
- */
- @Override
- public boolean modelBaseRecordClassGenerated(
- TopLevelClass topLevelClass,
- IntrospectedTable introspectedTable) {
- this.addAnnotations(topLevelClass);
- return true;
- }
-
- /**
- * Intercepts primary key class generation
- *
- * @param topLevelClass the generated primary key class
- * @param introspectedTable The class containing information about the table as
- * introspected from the database
- * @return always true
- */
- @Override
- public boolean modelPrimaryKeyClassGenerated(
- TopLevelClass topLevelClass,
- IntrospectedTable introspectedTable) {
- this.addAnnotations(topLevelClass);
- return true;
- }
-
- /**
- * Intercepts "record with blob" class generation
- *
- * @param topLevelClass the generated record with BLOBs class
- * @param introspectedTable The class containing information about the table as
- * introspected from the database
- * @return always true
- */
- @Override
- public boolean modelRecordWithBLOBsClassGenerated(
- TopLevelClass topLevelClass,
- IntrospectedTable introspectedTable) {
- this.addAnnotations(topLevelClass);
- return true;
- }
-
- /**
- * Prevents all getters from being generated.
- * See SimpleModelGenerator
- *
- * @param method the getter, or accessor, method generated for the specified
- * column
- * @param topLevelClass the partially implemented model class
- * @param introspectedColumn The class containing information about the column related
- * to this field as introspected from the database
- * @param introspectedTable The class containing information about the table as
- * introspected from the database
- * @param modelClassType the type of class that the field is generated for
- */
- @Override
- public boolean modelGetterMethodGenerated(
- Method method,
- TopLevelClass topLevelClass,
- IntrospectedColumn introspectedColumn,
- IntrospectedTable introspectedTable,
- ModelClassType modelClassType) {
- return false;
- }
-
- /**
- * Prevents all setters from being generated
- * See SimpleModelGenerator
- *
- * @param method the setter, or mutator, method generated for the specified
- * column
- * @param topLevelClass the partially implemented model class
- * @param introspectedColumn The class containing information about the column related
- * to this field as introspected from the database
- * @param introspectedTable The class containing information about the table as
- * introspected from the database
- * @param modelClassType the type of class that the field is generated for
- * @return always false
- */
- @Override
- public boolean modelSetterMethodGenerated(
- Method method,
- TopLevelClass topLevelClass,
- IntrospectedColumn introspectedColumn,
- IntrospectedTable introspectedTable,
- ModelClassType modelClassType) {
- return false;
- }
-
- /**
- * Adds the lombok annotations' imports and annotations to the class
- *
- * @param topLevelClass the partially implemented model class
- */
- private void addAnnotations(TopLevelClass topLevelClass) {
- for (Annotations annotation : this.annotations) {
- topLevelClass.addImportedType(annotation.javaType);
- topLevelClass.addAnnotation(annotation.asAnnotation());
- }
- }
-
- @Override
- public void setProperties(Properties properties) {
- super.setProperties(properties);
-
- // @Data is default annotation
- this.annotations.add(Annotations.DATA);
-
- for (String annotationName : properties.stringPropertyNames()) {
- if (annotationName.contains(".")) {
- // Not an annotation name
- continue;
- }
- String value = properties.getProperty(annotationName);
- if (!Boolean.parseBoolean(value)) {
- // The annotation is disabled, skip it
- continue;
- }
- Annotations annotation = Annotations.getValueOf(annotationName);
- if (annotation == null) {
- continue;
- }
- String optionsPrefix = annotationName + ".";
- for (String propertyName : properties.stringPropertyNames()) {
- if (!propertyName.startsWith(optionsPrefix)) {
- // A property not related to this annotation
- continue;
- }
- String propertyValue = properties.getProperty(propertyName);
- annotation.appendOptions(propertyName, propertyValue);
- this.annotations.add(annotation);
- this.annotations.addAll(Annotations.getDependencies(annotation));
- }
- }
- }
-
- @Override
- public boolean clientGenerated(Interface interfaze, IntrospectedTable introspectedTable) {
- interfaze.addImportedType(new FullyQualifiedJavaType("org.springframework.stereotype.Service"));
- interfaze.addAnnotation("@Service");
- return true;
- }
-
- private enum Annotations {
- /**
- * Data
- */
- DATA("data", "@Data", "lombok.Data"),
- /**
- * Builder
- */
- BUILDER("builder", "@Builder", "lombok.Builder"),
- /**
- * AllArgsConstructor
- */
- ALL_ARGS_CONSTRUCTOR("allArgsConstructor", "@AllArgsConstructor", "lombok.AllArgsConstructor"),
- /**
- * NoArgsConstructor
- */
- NO_ARGS_CONSTRUCTOR("noArgsConstructor", "@NoArgsConstructor", "lombok.NoArgsConstructor"),
- /**
- * ToString
- */
- TO_STRING("toString", "@ToString", "lombok.ToString");
-
- private final String paramName;
- private final String name;
- private final FullyQualifiedJavaType javaType;
- private final List
options; -
- Annotations(String paramName, String name, String className) {
- this.paramName = paramName;
- this.name = name;
- this.javaType = new FullyQualifiedJavaType(className);
- this.options = new ArrayList<>();
- }
-
- private static Annotations getValueOf(String paramName) {
- for (Annotations annotation : Annotations.values()) {
- if (String.CASE_INSENSITIVE_ORDER.compare(paramName, annotation.paramName) == 0) {
- return annotation;
- }
- }
-
- return null;
- }
-
- private static Collection
getDependencies(Annotations annotation) { - if (annotation == ALL_ARGS_CONSTRUCTOR) {
- return Collections.singleton(NO_ARGS_CONSTRUCTOR);
- } else {
- return Collections.emptyList();
- }
- }
-
- /**
- * A trivial quoting.
- * Because Lombok annotation options type is almost String or boolean.
- * @param value
- * @return
- */
- private static String quote(String value) {
- if (Boolean.TRUE.toString().equals(value) || Boolean.FALSE.toString().equals(value)) {
- // case of boolean, not passed as an array.
- return value;
- }
- return value.replaceAll("[\\w]+", "\"$0\"");
- }
-
- private void appendOptions(String key, String value) {
- String keyPart = key.substring(key.indexOf(".") + 1);
- String valuePart = value.contains(",") ? String.format("{%s}", value) : value;
- this.options.add(String.format("%s=%s", keyPart, quote(valuePart)));
- }
-
- private String asAnnotation() {
- if (this.options.isEmpty()) {
- return this.name;
- }
- StringBuilder sb = new StringBuilder();
- sb.append(this.name);
- sb.append("(");
- boolean first = true;
- for (String option : this.options) {
- if (first) {
- first = false;
- } else {
- sb.append(", ");
- }
- sb.append(option);
- }
- sb.append(")");
- return sb.toString();
- }
- }
- }
完整代码:https://github.com/ouyangyewei/mybatis-codegen
Git地址:git clone https://github.com/ouyangyewei/mybatis-codegen
步骤一:MySQL建表
- -- ----------------------------
- -- Table structure for t_project
- -- ----------------------------
- DROP TABLE IF EXISTS `t_project`;
- CREATE TABLE IF NOT EXISTS `t_project` (
- `id` int NOT NULL AUTO_INCREMENT COMMENT '项目Id',
- `name` varchar(100) DEFAULT NULL COMMENT '项目名称',
- `code` bigint NOT NULL COMMENT '项目编号',
- `description` varchar(200) DEFAULT NULL COMMENT '描述',
- `user_id` int DEFAULT NULL COMMENT '创建者ID',
- `flag` tinyint DEFAULT '1' COMMENT '0:不可用,1:可用',
- `create_time` datetime NOT NULL COMMENT '创建时间',
- `update_time` datetime DEFAULT NULL COMMENT '更新时间',
- PRIMARY KEY (`id`)
- ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8 COMMENT ='项目表';
步骤二:运行MyBatisGenerator,自动生成代码
生成的Java Bean对象自动添加@author、@date、表/字段注释、lombok注解
生成的Java Mapper对象支持分页操作
CURD的使用样例
- package com.github.codegen;
-
- import com.github.dao.domain.Project;
- import com.github.dao.domain.ProjectExample;
- import com.github.dao.mapper.ProjectMapper;
- import org.junit.jupiter.api.MethodOrderer;
- import org.junit.jupiter.api.Order;
- import org.junit.jupiter.api.Test;
- import org.junit.jupiter.api.TestMethodOrder;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.boot.test.context.SpringBootTest;
- import org.springframework.util.Assert;
-
- import java.util.Date;
- import java.util.List;
-
- @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
- @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
- public class MBGTester {
-
- @Autowired
- private ProjectMapper projectMapper;
-
- private static int projectId;
-
- @Test
- @Order(1)
- public void testInsert() {
- // insert:插入完整记录,要求字段完整
- // insertSelective:插入记录,允许插入部分字段
-
- Project project = new Project();
- project.setCode(1L);
- project.setName("PROJ-1");
- project.setDescription("testing desc");
- project.setFlag((byte) 1);
- project.setCreateTime(new Date());
- project.setUpdateTime(new Date());
- int nEffectRows = this.projectMapper.insert(project);
-
- // 获取刚插入的记录的自增主键ID
- projectId = project.getId();
- Assert.isTrue(nEffectRows == 1);
- }
-
- /**
- * 条件查询
- */
- @Test
- @Order(2)
- public void testConditionQuery() {
- // selectByPrimaryKey:根据主键查询
- // selectByExample:根据条件查询
-
- ProjectExample example = new ProjectExample();
- example.createCriteria().andCodeIsNotNull();
- List
projects = this.projectMapper.selectByExample(example); - Assert.notEmpty(projects);
- }
-
- /**
- * 分页查询
- */
- @Test
- @Order(3)
- public void testPagingQuery() {
- ProjectExample example = new ProjectExample();
- example.createCriteria().andCodeIsNotNull();
- example.setOffset(0);
- example.setLimit(100);
- List
projects = this.projectMapper.selectByExample(example); - Assert.notEmpty(projects);
- }
-
- @Test
- @Order(4)
- public void testUpdate() {
- // updateByPrimaryKey:根据主键更新
- // updateByPrimaryKeySelective:根据主键更新部分字段
- // updateByExample:根据条件更新
- // updateByExampleSelective:根据条件更新部分字段
-
- Project project = new Project();
- project.setId(projectId);
- project.setCode(100L);
- project.setName("PROJ-1");
- project.setCreateTime(new Date());
- project.setUpdateTime(new Date());
-
- int nEffectRows = this.projectMapper.updateByPrimaryKey(project);
- Assert.isTrue(nEffectRows == 1);
- }
-
- @Test
- @Order(5)
- public void testDelete() {
- // deleteByPrimaryKey:根据主键删除
- // deleteByExample:根据条件删除
-
- int nEffectRows = this.projectMapper.deleteByPrimaryKey(projectId);
- Assert.isTrue(nEffectRows == 1);
- }
- }