mybatis-flex/docs/zh/others/codegen.md
2024-01-04 16:47:23 +08:00

688 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# MyBatis-Flex 代码生成器
在 mybatis-flex 的模块 `mybatis-flex-codegen` 中,提供了可以通过数据库表,生成 Entity 类和 Mapper 类的功能。当我们把数据库表设计完成
后可以使用其快速生成 Entity 和 Mapper 的 java 类。
在使用前先添加 `mybatis-flex-codegen` 的 Maven 依赖:
```xml
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-codegen</artifactId>
<version>1.7.7</version>
</dependency>
```
同时需要添加数据源的 Maven 依赖和 jdbc 驱动依赖:
```xml
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.32</version>
</dependency>
```
然后,编写一个任意带有 main 方法的类,如下所示:
```java
public class Codegen {
public static void main(String[] args) {
//配置数据源
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/your-database?characterEncoding=utf-8");
dataSource.setUsername("root");
dataSource.setPassword("******");
//创建配置内容,两种风格都可以。
GlobalConfig globalConfig = createGlobalConfigUseStyle1();
//GlobalConfig globalConfig = createGlobalConfigUseStyle2();
//通过 datasource 和 globalConfig 创建代码生成器
Generator generator = new Generator(dataSource, globalConfig);
//生成代码
generator.generate();
}
public static GlobalConfig createGlobalConfigUseStyle1() {
//创建配置内容
GlobalConfig globalConfig = new GlobalConfig();
//设置根包
globalConfig.setBasePackage("com.test");
//设置表前缀和只生成哪些表
globalConfig.setTablePrefix("tb_");
globalConfig.setGenerateTable("tb_account", "tb_account_session");
//设置生成 entity 并启用 Lombok
globalConfig.setEntityGenerateEnable(true);
globalConfig.setEntityWithLombok(true);
//设置生成 mapper
globalConfig.setMapperGenerateEnable(true);
//可以单独配置某个列
ColumnConfig columnConfig = new ColumnConfig();
columnConfig.setColumnName("tenant_id");
columnConfig.setLarge(true);
columnConfig.setVersion(true);
globalConfig.setColumnConfig("tb_account", columnConfig);
return globalConfig;
}
public static GlobalConfig createGlobalConfigUseStyle2() {
//创建配置内容
GlobalConfig globalConfig = new GlobalConfig();
//设置根包
globalConfig.getPackageConfig()
.setBasePackage("com.test");
//设置表前缀和只生成哪些表setGenerateTable 未配置时,生成所有表
globalConfig.getStrategyConfig()
.setTablePrefix("tb_")
.setGenerateTable("tb_account", "tb_account_session");
//设置生成 entity 并启用 Lombok
globalConfig.enableEntity()
.setWithLombok(true);
//设置生成 mapper
globalConfig.enableMapper();
//可以单独配置某个列
ColumnConfig columnConfig = new ColumnConfig();
columnConfig.setColumnName("tenant_id");
columnConfig.setLarge(true);
columnConfig.setVersion(true);
globalConfig.getStrategyConfig()
.setColumnConfig("tb_account", columnConfig);
return globalConfig;
}
}
```
注意:由于 MyBatis-Flex 的 APT 功能会自动帮我们生成了 Mapper 的 Java 类,如果我们在代码生成器中选择生成 Mapper
则建议把 APT 的 Mapper 生成功能给关闭掉,否则系统中会存在两份一样功能的 Mapper。
关闭 APT 的 Mapper 类文件生成,请参考:[APT 设置章节](../others/apt.md)
## 使用介绍
在 Mybatis-Flex 的代码生成器中,支持如下 8 种类型的的产物生成:
- Entity 实体类
- Mapper 映射类
- TableDef 表定义辅助类
- Service 服务类
- ServiceImpl 服务实现类
- Controller 控制类
- MapperXml 文件
- package-info.java 文件
启用或关闭某种类型产物的生成,代码如下:
```java
// 开启 Entity 的生成
globalConfig.enableEntity();
// 关闭 Entity 的生成
globalConfig.disableEntity();
```
所有方法均支持链式调用配置,代码如下:
```java
// 设置生成 Entity 并启用 Lombok、设置父类
globalConfig.enableEntity()
.setWithLombok(true)
.setSuperClass(BaseEntity.class);
```
## 全局配置 `GlobalConfig`
> 可以像先前一样直接使用 `setXxx()` 进行配置,也可以使用 `getXxxConfig().setXxx()` 进行分类配置。
| 获取配置 | 描述 |
|------------------------|------------------|
| getJavadocConfig() | 注释配置 |
| getPackageConfig() | 包配置 |
| getStrategyConfig() | 策略配置 |
| getTemplateConfig() | 模板配置 |
| getEntityConfig() | Entity 生成配置 |
| getMapperConfig() | Mapper 生成配置 |
| getServiceConfig() | Service 生成配置 |
| getServiceImplConfig() | ServiceImpl 生成配置 |
| getControllerConfig() | Controller 生成配置 |
| getTableDefConfig() | TableDef 生成配置 |
| getMapperXmlConfig() | MapperXml 生成配置 |
```java
globalConfig.getPackageConfig()
.setSourceDir("D://files/java")
.setBasePackage("com.your.domain");
```
| 启用生成 | 描述 |
|---------------------|-------------------|
| enableEntity() | 启用 Entity 生成 |
| enableMapper() | 启用 Mapper 生成 |
| enableService() | 启用 Service 生成 |
| enableServiceImpl() | 启用 ServiceImpl 生成 |
| enableController() | 启用 Controller 生成 |
| enableTableDef() | 启用 TableDef 生成 |
| enableMapperXml() | 启用 MapperXml 生成 |
启用生成之后可以继续链式进行配置,例如:
```java
// 设置生成 Entity 并启用 Lombok、设置父类
globalConfig.enableEntity()
.setWithLombok(true)
.setSuperClass(BaseEntity.class);
```
## 注释配置 `JavadocConfig`
| 配置 | 描述 | 默认值 |
|----------------------------------|-----------------|---------------------------------|
| setAuthor(String) | 作者 | System.getProperty("user.name") |
| setSince(String) | 自 | 日期yyyy-MM-dd |
| setTableCommentFormat(Function) | 表注释格式化 | 原表注释 |
| setColumnCommentFormat(Function) | 字段注释格式化 | 原字段注释 |
| setEntityPackage(String) | Entity 包注释 | "实体类层Entity软件包。" |
| setMapperPackage(String) | Mapper 包注释 | "映射层Mapper软件包。" |
| setServicePackage(String) | Service 包注释 | "服务层Service软件包。" |
| setServiceImplPackage(String) | ServiceImpl 包注释 | "服务层实现ServiceImpl软件包。" |
| setControllerPackage(String) | Controller 包注释 | "控制层Controller软件包。" |
| setTableDefPackage(String) | TableDef 包注释 | "表定义层TableDef软件包。" |
```java
globalConfig.getJavadocConfig()
.setAuthor("Your Name")
.setSince("1.0.1");
```
## 包配置 `PackageConfig`
| 配置 | 描述 | 默认值 |
|-------------------------------|----------------|---------------------------------------------------------------|
| setSourceDir(String) | 文件输出目录 | System.getProperty("user.dir") + "/src/main/java" |
| setBasePackage(String) | 根包名 | "com.mybatisflex" |
| setEntityPackage(String) | Entity 包名 | getBasePackage() + ".entity" | |
| setMapperPackage(String) | Mapper 包名 | getBasePackage() + ".mapper" | |
| setServicePackage(String) | Service 包名 | getBasePackage() + ".service" | |
| setServiceImplPackage(String) | ServiceImpl 包名 | getBasePackage() + ".service.impl" | |
| setControllerPackage(String) | Controller 包名 | getBasePackage() + ".controller" | |
| setTableDefPackage(String) | TableDef 包名 | getEntityPackage() + ".tables" | |
| setMapperXmlPath(String) | MapperXml 路径 | System.getProperty("user.dir") + "/src/main/resources/mapper" | |
```java
globalConfig.getPackageConfig()
.setSourceDir("D://files/java")
.setBasePackage("com.your.domain");
```
## 策略配置 `StrategyConfig`
| 配置 | 描述 | 默认值 |
|--------------------------------|------------------------|-------|
| setTablePrefix(String) | 数据库表前缀,多个前缀用英文逗号(, 隔开 | null |
| setLogicDeleteColumn(String) | 逻辑删除的默认字段名称 | null |
| setVersionColumn(String) | 乐观锁的字段名称 | null |
| setGenerateForView(boolean) | 是否生成视图映射 | false |
| setTableConfig(TableConfig) | 单独为某张表添加独立的配置 | null |
| setColumnConfig(ColumnConfig) | 设置某个列的全局配置 | null |
| setGenerateSchema(String) | 生成哪个schema下的表 | null |
| setGenerateTables(String...) | 生成哪些表,白名单 | null |
| setUnGenerateTables(String...) | 不生成哪些表,黑名单 | null |
| setIgnoreColumns(String...) | 需要忽略的列,父类定义的字段 | null |
```java
globalConfig.getStrategyConfig()
.setGenerateSchema("schema")
.setTablePrefix("sys_")
.setGenerateTables("sys_user","sys_dept");
```
> `setGenerateTables` 和 `setUnGenerateTables` 未配置时,生成所有表。
## 模板配置 `TemplateConfig`
| 配置 | 描述 | 默认值 |
|------------------------|------------------|------------------------------------|
| setTemplate(ITemplate) | | |
| setEntity(String) | Entity 模板路径 | "/templates/enjoy/entity.tpl" |
| setMapper(String) | Mapper 模板路径 | "/templates/enjoy/mapper.tpl" |
| setService(String) | Service 模板路径 | "/templates/enjoy/service.tpl" |
| setServiceImpl(String) | ServiceImpl 模板路径 | "/templates/enjoy/serviceImpl.tpl" |
| setController(String) | Controller 模板路径 | "/templates/enjoy/controller.tpl" |
| setTableDef(String) | TableDef 模板路径 | "/templates/enjoy/tableDef.tpl" |
| setMapperXml(String) | MapperXml 模板路径 | "/templates/enjoy/mapperXml.tpl" |
```java
globalConfig.getTemplateConfig()
.setTemplate(new FreeMarkerTemplate())
.setEntity("D:\your-template-file\my-entity.tpl");
```
## Entity 生成配置 `EntityConfig`
| 配置 | 描述 | 默认值 |
|------------------------------------------------|----------------------------------|--------------------|
| setClassPrefix(String) | Entity 类的前缀 | "" |
| setClassSuffix(String) | Entity 类的后缀 | "" |
| setSuperClass(Class) | Entity 类的父类,可以自定义一些 BaseEntity 类 | null |
| setOverwriteEnable(boolean) | 是否覆盖之前生成的文件 | false |
| setImplInterfaces(Class[]) | Entity 默认实现的接口 | Serializable.class |
| setWithLombok(boolean) | Entity 是否使用 Lombok 注解 | false |
| setWithSwagger(boolean) | Entity 是否使用 Swagger 注解 | false |
| setSwaggerVersion(EntityConfig.SwaggerVersion) | Swagger 注解版本 | SwaggerVersion.FOX |
| setWithActiveRecord(boolean) | 是否生成 Active Record 模式的 Entity | false |
| setDataSource(String) | 统一使用的数据源 | null |
```java
globalConfig.getEntityConfig()
.setWithLombok(true)
.setClassPrefix("My")
.setClassSuffix("Entity")
.setSuperClass(BaseEntity.class);
```
## Mapper 生成配置 `MapperConfig`
| 配置 | 描述 | 默认值 |
|------------------------------|-----------------|------------------|
| setClassPrefix(String) | Mapper 类的前缀 | "" |
| setClassSuffix(String) | Mapper 类的后缀 | "Mapper" |
| setSuperClass(Class) | Mapper 类的父类 | BaseMapper.class |
| setOverwriteEnable(boolean) | 是否覆盖之前生成的文件 | false |
| setMapperAnnotation(boolean) | 是否生成 @Mapper 注解 | false |
```java
globalConfig.getMapperConfig()
.setClassPrefix("My")
.setClassSuffix("Mapper")
.setSuperClass(BaseMapper.class);
```
## Service 生成配置 `ServiceConfig`
| 配置 | 描述 | 默认值 |
|-----------------------------|--------------|----------------|
| setClassPrefix(String) | Service 类的前缀 | "" |
| setClassSuffix(String) | Service 类的后缀 | "Service" |
| setSuperClass(Class) | Service 类的父类 | IService.class |
| setOverwriteEnable(boolean) | 是否覆盖之前生成的文件 | false |
```java
globalConfig.getServiceConfig()
.setClassPrefix("My")
.setClassSuffix("Service")
.setSuperClass(IService.class);
```
## ServiceImpl 生成配置 `ServiceImplConfig`
| 配置 | 描述 | 默认值 |
|-----------------------------|------------------|-------------------|
| setClassPrefix(String) | ServiceImpl 类的前缀 | "" |
| setClassSuffix(String) | ServiceImpl 类的后缀 | "ServiceImpl" |
| setSuperClass(Class) | ServiceImpl 类的父类 | ServiceImpl.class |
| setOverwriteEnable(boolean) | 是否覆盖之前生成的文件 | false |
| setCacheExample(boolean) | 是否添加缓存示例代码 | false |
```java
globalConfig.getServiceImplConfig()
.setClassPrefix("My")
.setClassSuffix("ServiceImpl")
.setSuperClass(ServiceImpl.class);
```
## Controller 生成配置 `ControllerConfig`
| 配置 | 描述 | 默认值 |
|-----------------------------|---------------------|--------------|
| setClassPrefix(String) | Controller 类的前缀 | "" |
| setClassSuffix(String) | Controller 类的后缀 | "Controller" |
| setSuperClass(Class) | Controller 类的父类 | null |
| setOverwriteEnable(boolean) | 是否覆盖之前生成的文件 | false |
| setRestStyle(boolean) | REST 风格的 Controller | true |
```java
globalConfig.getControllerConfig()
.setClassPrefix("My")
.setClassSuffix("Controller")
.setSuperClass(BaseController.class);
```
## TableDef 生成配置 `TableDefConfig`
| 配置 | 描述 | 默认值 |
|--------------------------------------------------|----------------|-------------------------------------------|
| setClassPrefix(String) | TableDef 类的前缀 | "" |
| setClassSuffix(String) | TableDef 类的后缀 | "TableDef" |
| setOverwriteEnable(boolean) | 是否覆盖之前生成的文件 | false |
| setPropertiesNameStyle(TableDefConfig.NameStyle) | 生成辅助类的字段风格 | TableDefConfig.NameStyle.LOWER_CAMEL_CASE |
| setInstanceSuffix(String) | 生成辅助类常量对应的变量后缀 | "" |
```java
globalConfig.getTableDefConfig()
.setClassPrefix("My")
.setClassSuffix("Def");
```
## MapperXml 生成配置 `MapperXmlConfig`
| 配置 | 描述 | 默认值 |
|-----------------------------|-----------------|----------|
| setFilePrefix(String) | MapperXml 文件的前缀 | "" |
| setFileSuffix(String) | MapperXml 文件的后缀 | "Mapper" |
| setOverwriteEnable(boolean) | 是否覆盖之前生成的文件 | false |
```java
globalConfig.getMapperXmlConfig()
.setFilePrefix("My")
.setFileSuffix("Mapper");
```
## 表配置 `TableConfig`
TableConfig 支持的配置如下:
```java
public class TableConfig {
/**
* 表名。
*/
private String tableName;
/**
* 数据库的 schema模式
*/
private String schema;
/**
* 默认为 驼峰属性 转换为 下划线字段。
*/
private Boolean camelToUnderline;
/**
* 监听 entity 的 insert 行为。
*/
private Class<? extends InsertListener> insertListenerClass;
/**
* 监听 entity 的 update 行为。
*/
private Class<? extends UpdateListener> updateListenerClass;
/**
* 监听 entity 的查询数据的 set 行为。
*/
private Class<? extends SetListener> setListenerClass;
/**
* 对应列的配置。
*/
private Map<String, ColumnConfig> columnConfigMap;
/**
* 是否开启 Mapper 生成。
*/
private Boolean mapperGenerateEnable = Boolean.TRUE;
}
```
## 列配置 `ColumnConfig`
ColumnConfig 支持的配置如下:
```java
public class ColumnConfig implements Serializable {
/**
* 字段名称。
*/
private String columnName;
/**
* insert 的时候默认值,这个值会直接被拼接到 sql 而不通过参数设置。
*/
private String onInsertValue;
/**
* update 的时候自动赋值,这个值会直接被拼接到 sql 而不通过参数设置。
*/
private String onUpdateValue;
/**
* 是否是大字段,大字段 APT 不会生成到 DEFAULT_COLUMNS 里。
*/
private Boolean isLarge;
/**
* 是否是逻辑删除字段,一张表中只能存在 1 一个逻辑删除字段。
*/
private Boolean isLogicDelete;
/**
* 是否为乐观锁字段。
*/
private Boolean version;
/**
* 配置的 jdbcType。
*/
private JdbcType jdbcType;
/**
* <p>属性的类型。
*
* <p>原始类型直接写类型名称例如int/long/float/double/boolean<br/>
* 对象类型请写对应类的全限定名例如java.lang.String/com.example.enums.Gender
*/
private String propertyType;
/**
* 属性的默认值例如long 类型默认值0L枚举类型默认值Gender.MALE。
*/
private String propertyDefaultValue;
/**
* 自定义 TypeHandler。
*/
private Class<? extends TypeHandler> typeHandler;
/**
* 脱敏方式。
*/
private String mask;
/**
* 字段是否为主键。
*/
private boolean isPrimaryKey = false;
/**
* ID 生成策略。
*/
private KeyType keyType;
/**
* ID 生成器值。
*/
private String keyValue;
/**
* sequence 序列执行顺序。
*/
private Boolean keyBefore;
/**
* 是否是租户 ID。
*/
private Boolean tenantId;
}
```
## 自定义属性类型
MyBatis-Flex 内置了一个名为:`JdbcTypeMapping` 的 java 类,我们可以用其配置映射 Jdbc 驱动的数据类型为自定义的
数据类型,在开始生成代码之前,可以先调用其进行配置,例如:
```java
JdbcTypeMapping.registerMapping(LocalDateTime.class, Date.class);
```
那么,当我们生成代码的时候,发现 JDBC 驱动的数据类型为 `LocalDateTime`,则 Entity 对应的属性类型为 `Date`
## 自定义代码模板
通过 `GlobalConfig`(全局配置)的 `setTemplateEngine()` 方法,可以配置自己的模板引擎以及模板,以下是内置的 `EnjoyTemplate`
的代码示例:
```java
public class EnjoyTemplate implements ITemplate {
private Engine engine;
public EnjoyTemplate() {
Engine engine = Engine.use(engineName);
if (engine == null) {
engine = Engine.create(engineName, e -> {
e.addSharedStaticMethod(StringUtil.class);
e.setSourceFactory(new FileAndClassPathSourceFactory());
});
}
this.engine = engine;
// 以下配置将支持 user.girl 表达式去调用 user 对象的 boolean isGirl() 方法
Engine.addFieldGetterToFirst(new FieldGetters.IsMethodFieldGetter());
}
@Override
public void generate(Map<String, Object> params, String templateFilePath, File generateFile) {
if (!generateFile.getParentFile().exists() && !generateFile.getParentFile().mkdirs()) {
throw new IllegalStateException("Can not mkdirs by dir: " + generateFile.getParentFile());
}
// 开始生成文件
try (FileOutputStream fileOutputStream = new FileOutputStream(generateFile)) {
engine.getTemplate(templateFilePath).render(params, fileOutputStream);
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
## 自定义数据方言
在 MyBatis-Flex 的代码生成器中,已经内置了 4 种方言,他们分别是:
- 默认方言
- MySQL 方言
- Oracle 方言
- SQLite 方言
方言可以通过如下的方式进行使用:
```java 3
Generator generator = new Generator(dataSource
, globalConfig
, IDialect.ORACLE); //使用哪个方言
generator.generate();
```
> 不传入方言的情况下,使用默认方言。
针对不同的数据库,我们也可以通过自定义方言来实现代码生成,例如:
MyDialect.java
```java
class MyDialect implements IDialect {
//重写相关构建方法
}
```
开始使用 MyDialect
```java 3
Generator generator = new Generator(dataSource
, globalConfig
, new MyDialect()); //使用哪个方言
generator.generate();
```
## 添加其他产物的生成
通过实现 `IGenerator` 来实现,比如 Entity 实体类的代码如下:
```java
public class EntityGenerator implements IGenerator {
private String templatePath = "/templates/enjoy/entity.tpl";
@Override
public void generate(Table table, GlobalConfig globalConfig) {
if (!globalConfig.isEntityGenerateEnable()) {
return;
}
PackageConfig packageConfig = globalConfig.getPackageConfig();
EntityConfig entityConfig = globalConfig.getEntityConfig();
String entityPackagePath = packageConfig.getEntityPackage().replace(".", "/");
File entityJavaFile = new File(packageConfig.getSourceDir(), entityPackagePath + "/" +
table.buildEntityClassName() + ".java");
if (entityJavaFile.exists() && !entityConfig.isOverwriteEnable()) {
return;
}
Map<String, Object> params = new HashMap<>(4);
params.put("table", table);
params.put("entityConfig", entityConfig);
params.put("packageConfig", packageConfig);
params.put("javadocConfig", globalConfig.getJavadocConfig());
globalConfig.getTemplateConfig().getTemplate().generate(params, templatePath, entityJavaFile);
}
}
```
如果我们想生成其他产物,比如 `html` ,可以通过编写自己的类,来实现 IGenerator 接口,例如:
```java
public class HtmlGenerator implements IGenerator {
@Override
public void generate(Table table, GlobalConfig globalConfig) {
//在这里生成 html 代码
}
}
```
最后,通过 `GeneratorFactory` 来注册 `HtmlGenerator` 即可:
```java
GeneratorFactory.registerGenerator("html",new HtmlGenerator());
```