• MyBatis Generator 代码生成工具


     本文完整代码已上传Github仓库:https://github.com/ouyangyewei/mybatis-codegen

    1、MBG 的简介

     MyBatis Generator官网:MyBatis Generator Core – Introduction to MyBatis Generator

    借助MyBatis Generator(MBG)工具,可以基于数据库表结构自动生成Bean/Mapper/Mapper XML代码,简化了大量重复繁琐的开发步骤,相关信息如下:

    2、MGB 自定义插件

    MGB支持自定义插件,比如:自动生成代码时带上表/表字段注释、分页、Lombok

    2.1、注释插件

    1. package com.github.codegen;
    2. import org.apache.logging.log4j.util.Strings;
    3. import org.mybatis.generator.api.IntrospectedColumn;
    4. import org.mybatis.generator.api.IntrospectedTable;
    5. import org.mybatis.generator.api.dom.java.Field;
    6. import org.mybatis.generator.api.dom.java.TopLevelClass;
    7. import org.mybatis.generator.internal.DefaultCommentGenerator;
    8. import org.mybatis.generator.internal.util.StringUtility;
    9. import java.text.SimpleDateFormat;
    10. import java.util.Date;
    11. import java.util.Properties;
    12. /**
    13. * 注释生成器
    14. *

    15. * 根据元数据表的字段注释给JavaBean字段添加注释
    16. *

    17. * @author ouyangyewei
    18. * @date 2021-09-01
    19. **/
    20. public class CommentGenerator extends DefaultCommentGenerator {
    21. private boolean addRemarkComments = false;
    22. /**
    23. * 设置用户配置的参数
    24. * @param properties
    25. */
    26. @Override
    27. public void addConfigurationProperties(Properties properties) {
    28. super.addConfigurationProperties(properties);
    29. this.addRemarkComments = StringUtility.isTrue(properties.getProperty("addRemarkComments"));
    30. }
    31. /**
    32. * 根据表注释设置类文件注释
    33. * @param topLevelClass
    34. * @param introspectedTable
    35. */
    36. private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
    37. public void addModelClassComment(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
    38. if (this.addRemarkComments) {
    39. topLevelClass.addJavaDocLine("/**");
    40. topLevelClass.addJavaDocLine(" * " + introspectedTable.getRemarks());
    41. topLevelClass.addJavaDocLine(" * @author MyBatis Generator");
    42. topLevelClass.addJavaDocLine(" * @date " + DATE_FORMAT.format(new Date()));
    43. topLevelClass.addJavaDocLine(" */");
    44. }
    45. }
    46. /**
    47. * 给字段添加注释
    48. * @param field
    49. * @param introspectedTable
    50. * @param introspectedColumn
    51. */
    52. @Override
    53. public void addFieldComment(Field field, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) {
    54. String remarks = introspectedColumn.getRemarks();
    55. // 根据参数和备注信息判断是否添加备注信息
    56. if (this.addRemarkComments && StringUtility.stringHasValue(remarks)) {
    57. // 数据库中特殊字符需要转义
    58. if (remarks.contains("\"")) {
    59. remarks = remarks.replace("\"","'");
    60. }
    61. // model的字段添加注解
    62. field.addJavaDocLine("/**" + Strings.LINE_SEPARATOR + " * " + remarks + Strings.LINE_SEPARATOR + " */");
    63. }
    64. }
    65. }

    2.2、分页插件

    1. package com.github.codegen;
    2. import org.mybatis.generator.api.IntrospectedTable;
    3. import org.mybatis.generator.api.PluginAdapter;
    4. import org.mybatis.generator.api.dom.java.*;
    5. import org.mybatis.generator.api.dom.xml.Attribute;
    6. import org.mybatis.generator.api.dom.xml.TextElement;
    7. import org.mybatis.generator.api.dom.xml.XmlElement;
    8. import java.util.List;
    9. /**
    10. * Mybatis Page Limit Plugin
    11. *

    12. * 用于为每个Example类添加offset和limit属性方法
    13. *

    14. * @author ouyangyewei
    15. * @date 2021-08-31
    16. **/
    17. public class LimitPlugin extends PluginAdapter {
    18. @Override
    19. public boolean validate(List list) {
    20. return true;
    21. }
    22. /**
    23. * 为每个Example类添加offset和rows属性已经set、get方法
    24. * @param topLevelClass
    25. * @param introspectedTable
    26. * @return
    27. */
    28. @Override
    29. public boolean modelExampleClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
    30. PrimitiveTypeWrapper integerWrapper = FullyQualifiedJavaType.getIntInstance().getPrimitiveTypeWrapper();
    31. Field limit = new Field("limit", integerWrapper);
    32. limit.setVisibility(JavaVisibility.PRIVATE);
    33. topLevelClass.addField(limit);
    34. Method setLimit = new Method("setLimit");
    35. setLimit.setVisibility(JavaVisibility.PUBLIC);
    36. setLimit.addParameter(new Parameter(integerWrapper, "limit"));
    37. setLimit.addBodyLine("this.limit = limit;");
    38. topLevelClass.addMethod(setLimit);
    39. Method getLimit = new Method("getLimit");
    40. getLimit.setVisibility(JavaVisibility.PUBLIC);
    41. getLimit.setReturnType(integerWrapper);
    42. getLimit.addBodyLine("return limit;");
    43. topLevelClass.addMethod(getLimit);
    44. Field offset = new Field("offset", integerWrapper);
    45. offset.setVisibility(JavaVisibility.PRIVATE);
    46. topLevelClass.addField(offset);
    47. Method setOffset = new Method("setOffset");
    48. setOffset.setVisibility(JavaVisibility.PUBLIC);
    49. setOffset.addParameter(new Parameter(integerWrapper, "offset"));
    50. setOffset.addBodyLine("this.offset = offset;");
    51. topLevelClass.addMethod(setOffset);
    52. Method getOffset = new Method("getOffset");
    53. getOffset.setVisibility(JavaVisibility.PUBLIC);
    54. getOffset.setReturnType(integerWrapper);
    55. getOffset.addBodyLine("return offset;");
    56. topLevelClass.addMethod(getOffset);
    57. return true;
    58. }
    59. /**
    60. * 为Mapper.xml的selectByExample添加limit
    61. * @param element
    62. * @param introspectedTable
    63. * @return
    64. */
    65. @Override
    66. public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
    67. XmlElement ifLimitNotNullElement = new XmlElement("if");
    68. ifLimitNotNullElement.addAttribute(new Attribute("test", "limit != null"));
    69. XmlElement ifOffsetNotNullElement = new XmlElement("if");
    70. ifOffsetNotNullElement.addAttribute(new Attribute("test", "offset != null"));
    71. ifOffsetNotNullElement.addElement(new TextElement("limit ${offset}, ${limit}"));
    72. ifLimitNotNullElement.addElement(ifOffsetNotNullElement);
    73. XmlElement ifOffsetNullElement = new XmlElement("if");
    74. ifOffsetNullElement.addAttribute(new Attribute("test", "offset == null"));
    75. ifOffsetNullElement.addElement(new TextElement("limit ${limit}"));
    76. ifLimitNotNullElement.addElement(ifOffsetNullElement);
    77. element.addElement(ifLimitNotNullElement);
    78. return true;
    79. }
    80. }

    2.3、Lombok插件

    1. package com.github.codegen;
    2. import org.mybatis.generator.api.IntrospectedColumn;
    3. import org.mybatis.generator.api.IntrospectedTable;
    4. import org.mybatis.generator.api.PluginAdapter;
    5. import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
    6. import org.mybatis.generator.api.dom.java.Interface;
    7. import org.mybatis.generator.api.dom.java.Method;
    8. import org.mybatis.generator.api.dom.java.TopLevelClass;
    9. import java.util.*;
    10. /**
    11. * A MyBatis Generator plugin to use Lombok's annotations.
    12. * For example, use @Data annotation instead of getter ands setter.
    13. *
    14. * @See https://github.com/softwareloop/mybatis-generator-lombok-plugin
    15. * @author Paolo Predonzani (http://softwareloop.com/)
    16. */
    17. public class LombokPlugin extends PluginAdapter {
    18. private final Collection annotations;
    19. /**
    20. * LombokPlugin constructor
    21. */
    22. public LombokPlugin() {
    23. this.annotations = new LinkedHashSet<>(Annotations.values().length);
    24. }
    25. /**
    26. * @param warnings list of warnings
    27. * @return always true
    28. */
    29. @Override
    30. public boolean validate(List warnings) {
    31. return true;
    32. }
    33. /**
    34. * Intercepts base record class generation
    35. *
    36. * @param topLevelClass the generated base record class
    37. * @param introspectedTable The class containing information about the table as
    38. * introspected from the database
    39. * @return always true
    40. */
    41. @Override
    42. public boolean modelBaseRecordClassGenerated(
    43. TopLevelClass topLevelClass,
    44. IntrospectedTable introspectedTable) {
    45. this.addAnnotations(topLevelClass);
    46. return true;
    47. }
    48. /**
    49. * Intercepts primary key class generation
    50. *
    51. * @param topLevelClass the generated primary key class
    52. * @param introspectedTable The class containing information about the table as
    53. * introspected from the database
    54. * @return always true
    55. */
    56. @Override
    57. public boolean modelPrimaryKeyClassGenerated(
    58. TopLevelClass topLevelClass,
    59. IntrospectedTable introspectedTable) {
    60. this.addAnnotations(topLevelClass);
    61. return true;
    62. }
    63. /**
    64. * Intercepts "record with blob" class generation
    65. *
    66. * @param topLevelClass the generated record with BLOBs class
    67. * @param introspectedTable The class containing information about the table as
    68. * introspected from the database
    69. * @return always true
    70. */
    71. @Override
    72. public boolean modelRecordWithBLOBsClassGenerated(
    73. TopLevelClass topLevelClass,
    74. IntrospectedTable introspectedTable) {
    75. this.addAnnotations(topLevelClass);
    76. return true;
    77. }
    78. /**
    79. * Prevents all getters from being generated.
    80. * See SimpleModelGenerator
    81. *
    82. * @param method the getter, or accessor, method generated for the specified
    83. * column
    84. * @param topLevelClass the partially implemented model class
    85. * @param introspectedColumn The class containing information about the column related
    86. * to this field as introspected from the database
    87. * @param introspectedTable The class containing information about the table as
    88. * introspected from the database
    89. * @param modelClassType the type of class that the field is generated for
    90. */
    91. @Override
    92. public boolean modelGetterMethodGenerated(
    93. Method method,
    94. TopLevelClass topLevelClass,
    95. IntrospectedColumn introspectedColumn,
    96. IntrospectedTable introspectedTable,
    97. ModelClassType modelClassType) {
    98. return false;
    99. }
    100. /**
    101. * Prevents all setters from being generated
    102. * See SimpleModelGenerator
    103. *
    104. * @param method the setter, or mutator, method generated for the specified
    105. * column
    106. * @param topLevelClass the partially implemented model class
    107. * @param introspectedColumn The class containing information about the column related
    108. * to this field as introspected from the database
    109. * @param introspectedTable The class containing information about the table as
    110. * introspected from the database
    111. * @param modelClassType the type of class that the field is generated for
    112. * @return always false
    113. */
    114. @Override
    115. public boolean modelSetterMethodGenerated(
    116. Method method,
    117. TopLevelClass topLevelClass,
    118. IntrospectedColumn introspectedColumn,
    119. IntrospectedTable introspectedTable,
    120. ModelClassType modelClassType) {
    121. return false;
    122. }
    123. /**
    124. * Adds the lombok annotations' imports and annotations to the class
    125. *
    126. * @param topLevelClass the partially implemented model class
    127. */
    128. private void addAnnotations(TopLevelClass topLevelClass) {
    129. for (Annotations annotation : this.annotations) {
    130. topLevelClass.addImportedType(annotation.javaType);
    131. topLevelClass.addAnnotation(annotation.asAnnotation());
    132. }
    133. }
    134. @Override
    135. public void setProperties(Properties properties) {
    136. super.setProperties(properties);
    137. // @Data is default annotation
    138. this.annotations.add(Annotations.DATA);
    139. for (String annotationName : properties.stringPropertyNames()) {
    140. if (annotationName.contains(".")) {
    141. // Not an annotation name
    142. continue;
    143. }
    144. String value = properties.getProperty(annotationName);
    145. if (!Boolean.parseBoolean(value)) {
    146. // The annotation is disabled, skip it
    147. continue;
    148. }
    149. Annotations annotation = Annotations.getValueOf(annotationName);
    150. if (annotation == null) {
    151. continue;
    152. }
    153. String optionsPrefix = annotationName + ".";
    154. for (String propertyName : properties.stringPropertyNames()) {
    155. if (!propertyName.startsWith(optionsPrefix)) {
    156. // A property not related to this annotation
    157. continue;
    158. }
    159. String propertyValue = properties.getProperty(propertyName);
    160. annotation.appendOptions(propertyName, propertyValue);
    161. this.annotations.add(annotation);
    162. this.annotations.addAll(Annotations.getDependencies(annotation));
    163. }
    164. }
    165. }
    166. @Override
    167. public boolean clientGenerated(Interface interfaze, IntrospectedTable introspectedTable) {
    168. interfaze.addImportedType(new FullyQualifiedJavaType("org.springframework.stereotype.Service"));
    169. interfaze.addAnnotation("@Service");
    170. return true;
    171. }
    172. private enum Annotations {
    173. /**
    174. * Data
    175. */
    176. DATA("data", "@Data", "lombok.Data"),
    177. /**
    178. * Builder
    179. */
    180. BUILDER("builder", "@Builder", "lombok.Builder"),
    181. /**
    182. * AllArgsConstructor
    183. */
    184. ALL_ARGS_CONSTRUCTOR("allArgsConstructor", "@AllArgsConstructor", "lombok.AllArgsConstructor"),
    185. /**
    186. * NoArgsConstructor
    187. */
    188. NO_ARGS_CONSTRUCTOR("noArgsConstructor", "@NoArgsConstructor", "lombok.NoArgsConstructor"),
    189. /**
    190. * ToString
    191. */
    192. TO_STRING("toString", "@ToString", "lombok.ToString");
    193. private final String paramName;
    194. private final String name;
    195. private final FullyQualifiedJavaType javaType;
    196. private final List options;
    197. Annotations(String paramName, String name, String className) {
    198. this.paramName = paramName;
    199. this.name = name;
    200. this.javaType = new FullyQualifiedJavaType(className);
    201. this.options = new ArrayList<>();
    202. }
    203. private static Annotations getValueOf(String paramName) {
    204. for (Annotations annotation : Annotations.values()) {
    205. if (String.CASE_INSENSITIVE_ORDER.compare(paramName, annotation.paramName) == 0) {
    206. return annotation;
    207. }
    208. }
    209. return null;
    210. }
    211. private static Collection getDependencies(Annotations annotation) {
    212. if (annotation == ALL_ARGS_CONSTRUCTOR) {
    213. return Collections.singleton(NO_ARGS_CONSTRUCTOR);
    214. } else {
    215. return Collections.emptyList();
    216. }
    217. }
    218. /**
    219. * A trivial quoting.
    220. * Because Lombok annotation options type is almost String or boolean.
    221. * @param value
    222. * @return
    223. */
    224. private static String quote(String value) {
    225. if (Boolean.TRUE.toString().equals(value) || Boolean.FALSE.toString().equals(value)) {
    226. // case of boolean, not passed as an array.
    227. return value;
    228. }
    229. return value.replaceAll("[\\w]+", "\"$0\"");
    230. }
    231. private void appendOptions(String key, String value) {
    232. String keyPart = key.substring(key.indexOf(".") + 1);
    233. String valuePart = value.contains(",") ? String.format("{%s}", value) : value;
    234. this.options.add(String.format("%s=%s", keyPart, quote(valuePart)));
    235. }
    236. private String asAnnotation() {
    237. if (this.options.isEmpty()) {
    238. return this.name;
    239. }
    240. StringBuilder sb = new StringBuilder();
    241. sb.append(this.name);
    242. sb.append("(");
    243. boolean first = true;
    244. for (String option : this.options) {
    245. if (first) {
    246. first = false;
    247. } else {
    248. sb.append(", ");
    249. }
    250. sb.append(option);
    251. }
    252. sb.append(")");
    253. return sb.toString();
    254. }
    255. }
    256. }

    3、MBG 如何使用

    完整代码:https://github.com/ouyangyewei/mybatis-codegen

    Git地址:git clone https://github.com/ouyangyewei/mybatis-codegen

    步骤一:MySQL建表

    1. -- ----------------------------
    2. -- Table structure for t_project
    3. -- ----------------------------
    4. DROP TABLE IF EXISTS `t_project`;
    5. CREATE TABLE IF NOT EXISTS `t_project` (
    6. `id` int NOT NULL AUTO_INCREMENT COMMENT '项目Id',
    7. `name` varchar(100) DEFAULT NULL COMMENT '项目名称',
    8. `code` bigint NOT NULL COMMENT '项目编号',
    9. `description` varchar(200) DEFAULT NULL COMMENT '描述',
    10. `user_id` int DEFAULT NULL COMMENT '创建者ID',
    11. `flag` tinyint DEFAULT '1' COMMENT '0:不可用,1:可用',
    12. `create_time` datetime NOT NULL COMMENT '创建时间',
    13. `update_time` datetime DEFAULT NULL COMMENT '更新时间',
    14. PRIMARY KEY (`id`)
    15. ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8 COMMENT ='项目表';

    步骤二:运行MyBatisGenerator,自动生成代码

    生成的Java Bean对象自动添加@author、@date、表/字段注释、lombok注解

    生成的Java Mapper对象支持分页操作

    CURD的使用样例

    1. package com.github.codegen;
    2. import com.github.dao.domain.Project;
    3. import com.github.dao.domain.ProjectExample;
    4. import com.github.dao.mapper.ProjectMapper;
    5. import org.junit.jupiter.api.MethodOrderer;
    6. import org.junit.jupiter.api.Order;
    7. import org.junit.jupiter.api.Test;
    8. import org.junit.jupiter.api.TestMethodOrder;
    9. import org.springframework.beans.factory.annotation.Autowired;
    10. import org.springframework.boot.test.context.SpringBootTest;
    11. import org.springframework.util.Assert;
    12. import java.util.Date;
    13. import java.util.List;
    14. @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    15. @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    16. public class MBGTester {
    17. @Autowired
    18. private ProjectMapper projectMapper;
    19. private static int projectId;
    20. @Test
    21. @Order(1)
    22. public void testInsert() {
    23. // insert:插入完整记录,要求字段完整
    24. // insertSelective:插入记录,允许插入部分字段
    25. Project project = new Project();
    26. project.setCode(1L);
    27. project.setName("PROJ-1");
    28. project.setDescription("testing desc");
    29. project.setFlag((byte) 1);
    30. project.setCreateTime(new Date());
    31. project.setUpdateTime(new Date());
    32. int nEffectRows = this.projectMapper.insert(project);
    33. // 获取刚插入的记录的自增主键ID
    34. projectId = project.getId();
    35. Assert.isTrue(nEffectRows == 1);
    36. }
    37. /**
    38. * 条件查询
    39. */
    40. @Test
    41. @Order(2)
    42. public void testConditionQuery() {
    43. // selectByPrimaryKey:根据主键查询
    44. // selectByExample:根据条件查询
    45. ProjectExample example = new ProjectExample();
    46. example.createCriteria().andCodeIsNotNull();
    47. List projects = this.projectMapper.selectByExample(example);
    48. Assert.notEmpty(projects);
    49. }
    50. /**
    51. * 分页查询
    52. */
    53. @Test
    54. @Order(3)
    55. public void testPagingQuery() {
    56. ProjectExample example = new ProjectExample();
    57. example.createCriteria().andCodeIsNotNull();
    58. example.setOffset(0);
    59. example.setLimit(100);
    60. List projects = this.projectMapper.selectByExample(example);
    61. Assert.notEmpty(projects);
    62. }
    63. @Test
    64. @Order(4)
    65. public void testUpdate() {
    66. // updateByPrimaryKey:根据主键更新
    67. // updateByPrimaryKeySelective:根据主键更新部分字段
    68. // updateByExample:根据条件更新
    69. // updateByExampleSelective:根据条件更新部分字段
    70. Project project = new Project();
    71. project.setId(projectId);
    72. project.setCode(100L);
    73. project.setName("PROJ-1");
    74. project.setCreateTime(new Date());
    75. project.setUpdateTime(new Date());
    76. int nEffectRows = this.projectMapper.updateByPrimaryKey(project);
    77. Assert.isTrue(nEffectRows == 1);
    78. }
    79. @Test
    80. @Order(5)
    81. public void testDelete() {
    82. // deleteByPrimaryKey:根据主键删除
    83. // deleteByExample:根据条件删除
    84. int nEffectRows = this.projectMapper.deleteByPrimaryKey(projectId);
    85. Assert.isTrue(nEffectRows == 1);
    86. }
    87. }

     

     

     

  • 相关阅读:
    第142篇:原生js实现响应式原理
    景联文科技可为多模态语音翻译模型提供数据采集支持
    Java错题归纳day20
    基础(四)之java后端根据经纬度获取地址
    梭子鱼替换案例:国产防护甄选CACTER邮件安全网关
    Java通用转换地图坐标系离线算法,天地图和超图WGS84坐标系、高德GCJ-02坐标系和百度BD-09坐标系三个坐标系互相转换
    【Python】Python语言基础(中)
    【Css选择器】
    无法在 DLL“SQLite.Interop.dll”中找到名为”sIb4c632894b76cc1d“
    深度学习——(11)Knowledge distillation理论
  • 原文地址:https://blog.csdn.net/yeweiouyang/article/details/126321829