手写Maven插件实现MyBatis代码生成
官方mybatis generator使用案例:mybatis-generator-example
手写代码链接:codegen-maven-plugin
在使用MyBatis时,需要根据库表结构编写一些通用的Mapper interface、XML、Entity,这些重复操作可以通过代码生成器自动生成,大大提高开发效率。 目前,代码生成分为两种方式:
- 模版引擎:如
velocity
- 代码直接生成:如
javapoet
本案例使用 javapoet直接生成Java代码,使用jdom2生成XML文件。
Maven 插件
自定义Maven插件 可参考Plugin Developers Centre
Maven内置以下3个生命周期,每个生命周期(Lifecycle)有一个或多个阶段(Phase),每个阶段可以注册多个Plugin。
- default : handles your project deployment
- clean : handles project cleaning
- site : handles the creation of your project’s web site
其中,default生成周期常用,包含以下阶段,按顺序执行:
validate
- validate the project is correct and all necessary information is availablecompile
- compile the source code of the projecttest
- test the compiled source code using a suitable unit testing framework. These tests should not require the code be packaged or deployedpackage
- take the compiled code and package it in its distributable format, such as a JAR.verify
- run any checks on results of integration tests to ensure quality criteria are metinstall
- install the package into the local repository, for use as a dependency in other projects locallydeploy
- done in the build environment, copies the final package to the remote repository for sharing with other developers and projects.
如果单独执行某个Phase,会执行改Phase之前所有的Phases。
自定义插件套路如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 选定默认Phase,可在<build>中覆盖
@Mojo(name = "codegen", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
public class SQLTableGenMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", required = true, readonly = true)
private MavenProject project;
@Parameter(property = "skip", defaultValue = "false")
private boolean skip;
// 自定义xml项
@Parameter(property = "absoluteFilePath", required = true, readonly = true)
private String absoluteFilePath;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
if (skip) {
getLog().info("codegen is skipped!");
return;
}
if (StringUtils.isBlank(absoluteFilePath) || !Files.exists(Paths.get(absoluteFilePath))) {
// 异常
throw new MojoExecutionException("Failed to generate code: config file does not exist");
}
// 日志通过getLog()获取
getLog().info("codegen-maven-plugin for " + project.getName() + " starting!");
doExecute(project.getBasedir().getAbsolutePath(),absoluteFilePath);
}
public void doExecute(String baseAbsoluteDir,String filePath) throws MojoExecutionException {
// ...
}
}
javapoet生成Java源代码
javapoet通过Fluent api的方式构建代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
ClassName criterion = ClassName.get(configProperties.getMapperInterfaceGenPkg(),"Criterion");
TypeSpec criterion = TypeSpec.classBuilder("Criterion")
// 加annotation(也可以使用AnnotationSpec构建)
.addAnnotation(Data.class)
// List生成方式使用ParameterizedTypeName
// private List<Criterion> criteria
.addField(FieldSpec.builder(ParameterizedTypeName.get(ClassName.get(List.class),criterion),"criteria",Modifier.PRIVATE).build())
// private Object value;
.addField(FieldSpec.builder(Object.class, "value", Modifier.PRIVATE).build())
// private boolean noValue;
.addField(FieldSpec.builder(TypeName.BOOLEAN, "noValue", Modifier.PRIVATE).build())
// 添加构造器
.addMethod(MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(ParameterSpec.builder(String.class, "condition").build())
.addParameter(ParameterSpec.builder(Object.class, "value").build())
// addStatement最后不加分号
.addStatement("System.out.println(123);\n this(condition, value, null)")
.build()
)
// private void addCriterion(String property){...}
.addMethod(MethodSpec.methodBuilder("addCriterion")
.addModifiers(Modifier.PRIVATE)
.returns(TypeName.VOID)
.addParameter(String.class,"property")
// $T表示类型占位符
// $L for Literals
// $S for Strings
// $T for Types
// $N for Names
.addStatement(" if (value == null) {\n"
+ " throw new $T(\"Value for condition cannot be null \");\n"
+ "}\n"
+ "criteria.add(new $T(condition,value));",RuntimeException.class,criterion)
.build())
).build();
JDom2生成XML
1
2
3
4
5
6
7
8
9
10
11
12
13
Document xml = new Document();
DocType mybatisDocType = new DocType("mapper", "-//mybatis.org//DTD Mapper 3.0//EN",
"http://mybatis.org/dtd/mybatis-3-mapper.dtd");
xml.setDocType(mybatisDocType);
Comment comment = new Comment("Auto generated by codegen-maven-plugin @author:baotingyu " + LocalDateTime.now());
xml.addContent(comment);
Element mapper = new Element("mapper");
// <mapper namespace="xxx">
mapper.setAttribute("namespace", configProperties.getMapperInterfaceGenPkg()+"."+xmlName);
xml.addContent(mapper);
This post is licensed under CC BY 4.0 by the author.