This commit is contained in:
farukonfly 2023-12-04 21:46:06 +08:00
commit aa8755bd4b
8 changed files with 168 additions and 100 deletions

View File

@ -65,6 +65,31 @@ in alimaven (http://maven.aliyun.com/nexus/content/groups/public/)
</mirror>
```
## SpringBoot 3.2 项目,启动报错 Invalid value type for attribute 'factoryBeanObjectType': java.lang.String
这个是 `mybatis-spring` 依赖版本过低造成的,需要将内置的低版本 `mybatis-spring` 换为最新版。
```xml {6-9,17}
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-spring-boot-starter</artifactId>
<version>${mybatis-flex.version}</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 添加已适配 springboot 3.2 的 mybatis-spring 依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.3</version>
</dependency>
```
## SpringBoot 项目,启动报错 java.lang.ClassNotFoundException: org.springframework.transaction.TransactionManager
这个应该是 Spring Boot 版本的问题,`org.springframework.transaction.TransactionManager` 这个类是 Spring Framework 5.2

View File

@ -295,7 +295,7 @@ public class QueryCondition implements CloneSupport<QueryCondition> {
return nextContainsTable(tables);
}
for (String table : tables) {
String tableName = StringUtil.getTableNameWithAlisa(table)[0];
String tableName = StringUtil.getTableNameWithAlias(table)[0];
if (column.table != null && tableName.equals(column.table.name)) {
return true;
}

View File

@ -43,9 +43,9 @@ public class RawQueryCondition extends QueryCondition {
@Override
boolean containsTable(String... tables) {
for (String table : tables) {
String[] tableNameWithAlisa = StringUtil.getTableNameWithAlisa(table);
if (content.contains(tableNameWithAlisa[0])
|| (tableNameWithAlisa[1] != null && content.contains(tableNameWithAlisa[1]))) {
String[] tableNameWithAlias = StringUtil.getTableNameWithAlias(table);
if (content.contains(tableNameWithAlias[0])
|| (tableNameWithAlias[1] != null && content.contains(tableNameWithAlias[1]))) {
return true;
}
}

View File

@ -118,17 +118,62 @@ public class TableInfoFactory {
if (mapperClass == null || mapperClass == Object.class) {
return null;
}
return getEntityClass(mapperClass, null);
}
private static Class<?> getEntityClass(Class<?> mapperClass, Type[] actualTypeArguments) {
// 检查基接口
Type[] genericInterfaces = mapperClass.getGenericInterfaces();
if (genericInterfaces.length == 1) {
Type type = genericInterfaces[0];
for (Type type : genericInterfaces) {
if (type instanceof ParameterizedType) {
Type actualTypeArgument = ((ParameterizedType) type).getActualTypeArguments()[0];
return actualTypeArgument instanceof Class ? (Class<?>) actualTypeArgument : null;
// 泛型基接口
ParameterizedType parameterizedType = (ParameterizedType) type;
Type rawType = parameterizedType.getRawType();
Type[] typeArguments = parameterizedType.getActualTypeArguments();
adjustTypeArguments(mapperClass, actualTypeArguments, typeArguments);
if (rawType == BaseMapper.class) {
// 找到了
if (typeArguments[0] instanceof Class) {
return (Class<?>) typeArguments[0];
}
} else if (rawType instanceof Class) {
// 其他泛型基接口
Class<?> entityClass = getEntityClass((Class<?>) rawType, typeArguments);
if (entityClass != null) {
return entityClass;
}
}
} else if (type instanceof Class) {
return getEntityClass((Class<?>) type);
// 其他基接口
Class<?> entityClass = getEntityClass((Class<?>) type);
if (entityClass != null) {
return entityClass;
}
}
}
// 检查基类
Class<?> superclass = mapperClass.getSuperclass();
if (superclass == null || superclass == Object.class) {
return null;
}
Type[] typeArguments = superclass.getTypeParameters();
adjustTypeArguments(mapperClass, actualTypeArguments, typeArguments);
return getEntityClass(superclass, typeArguments);
}
private static void adjustTypeArguments(Class<?> subclass, Type[] subclassTypeArguments, Type[] typeArguments) {
for (int i = 0; i < typeArguments.length; i++) {
if (typeArguments[i] instanceof TypeVariable) {
TypeVariable<?> typeVariable = (TypeVariable<?>) typeArguments[i];
TypeVariable<?>[] typeParameters = subclass.getTypeParameters();
for (int j = 0; j < typeParameters.length; j++) {
if (Objects.equals(typeVariable.getName(), typeParameters[j].getName())) {
typeArguments[i] = subclassTypeArguments[j];
break;
}
}
}
}
return getEntityClass(mapperClass.getSuperclass());
}

View File

@ -303,10 +303,10 @@ public class StringUtil {
: new String[]{tableNameWithSchema.substring(0, index).trim(), tableNameWithSchema.substring(index + 1).trim()};
}
public static String[] getTableNameWithAlisa(String tableNameWithAlisa) {
int index = tableNameWithAlisa.indexOf(".");
return index <= 0 ? new String[]{tableNameWithAlisa, null}
: new String[]{tableNameWithAlisa.substring(0, index), tableNameWithAlisa.substring(index + 1)};
public static String[] getTableNameWithAlias(String tableNameWithAlias) {
int index = tableNameWithAlias.indexOf(".");
return index <= 0 ? new String[]{tableNameWithAlias, null}
: new String[]{tableNameWithAlias.substring(0, index), tableNameWithAlias.substring(index + 1)};
}
public static String tryTrim(String string) {

View File

@ -57,8 +57,8 @@
<mybatis-flex.version>1.7.5</mybatis-flex.version>
<mybatis.version>3.5.13</mybatis.version>
<mybatis-spring.version>2.1.0</mybatis-spring.version>
<mybatis.version>3.5.14</mybatis.version>
<mybatis-spring.version>2.1.2</mybatis-spring.version>
<mybatis-freemarker.version>1.2.4</mybatis-freemarker.version>
<mybatis-velocity.version>2.1.2</mybatis-velocity.version>
<mybatis-thymeleaf.version>1.0.4</mybatis-thymeleaf.version>

View File

@ -1,4 +1,8 @@
![](./docs/assets/images/logo_en.png)
<h4 align="right"><strong>English</strong> | <a href="https://github.com/mybatis-flex/mybatis-flex/blob/main/readme_zh.md">简体中文</a></h4>
<p align="center">
<img src="./docs/assets/images/logo_en.png"/>
</p>
# MyBatis-Flex is an elegant Mybatis Enhancement Framework.
@ -33,20 +37,19 @@
## Features
- 1、MyBatis-Flex is very lightweight, and it only depends on Mybatis and no other third-party dependencies
- 2、Basic CRUD operator and paging query of Entity class
- 3、Row mapping support, you can add, delete, modify and query the database without entity classes
- 4、Support multiple databases, and expand through dialects flexibly.
- 5、Support combined primary keys and different primary key content generation strategies
- 6、Extremely friendly SQL query, IDE automatically prompts and no worries about mistakes
- 7、More little surprises
1. MyBatis-Flex is very lightweight, and it only depends on Mybatis and no other third-party dependencies
2. Basic CRUD operator and paging query of Entity class
3. Row mapping support, you can add, delete, modify and query the database without entity classes
4. Support multiple databases, and expand through dialects flexibly
5. Support combined primary keys and different primary key content generation strategies
6. Extremely friendly SQL query, IDE automatically prompts and no worries about mistakes
7. More little surprises
## hello worldWithout Spring
## hello world(Without Spring)
**step 1: write entity class**
```java
@Table("tb_account")
public class Account {
@ -56,7 +59,7 @@ public class Account {
private Date birthday;
private int sex;
//getter setter
// getter setter
}
```
@ -64,13 +67,13 @@ public class Account {
```java
public interface AccountMapper extends BaseMapper<Account> {
//only Mapper interface define.
// only Mapper interface define
}
```
**step 3: start query data**
e.g. 1 query by primary key
e.g. 1: query by primary key
```java
class HelloWorld {
@ -90,7 +93,7 @@ class HelloWorld {
.getMapper(AccountMapper.class);
//id=100
// id = 100
Account account = mapper.selectOneById(100);
}
}
@ -99,7 +102,7 @@ class HelloWorld {
e.g.2: query list
```java
//use QueryWrapper to build query conditions
// use QueryWrapper to build query conditions
QueryWrapper query = QueryWrapper.create()
.select()
.from(ACCOUNT)
@ -116,7 +119,7 @@ List<Account> accounts = mapper.selectListByQuery(query);
e.g.3: paging query
```java
//use QueryWrapper to build query conditions
// use QueryWrapper to build query conditions
QueryWrapper query = QueryWrapper.create()
.select()
.from(ACCOUNT)
@ -138,7 +141,7 @@ Page<Account> accountPage = mapper.paginate(5, 10, query);
### select *
```java
QueryWrapper query=new QueryWrapper();
QueryWrapper query = new QueryWrapper();
query.select().from(ACCOUNT)
// SQL:
@ -148,7 +151,7 @@ query.select().from(ACCOUNT)
### select columns
```java
QueryWrapper query=new QueryWrapper();
QueryWrapper query = new QueryWrapper();
query.select(ACCOUNT.ID,ACCOUNT.USER_NAME).from(ACCOUNT)
// SQL:
@ -175,7 +178,7 @@ QueryWrapper query = new QueryWrapper()
### select functions
```java
QueryWrapper query=new QueryWrapper()
QueryWrapper query = new QueryWrapper()
.select(
ACCOUNT.ID,
ACCOUNT.USER_NAME,
@ -193,7 +196,7 @@ QueryWrapper query = new QueryWrapper()
### where
```java
QueryWrapper queryWrapper=QueryWrapper.create()
QueryWrapper queryWrapper = QueryWrapper.create()
.select()
.from(ACCOUNT)
.where(ACCOUNT.ID.ge(100))
@ -208,7 +211,7 @@ QueryWrapper queryWrapper=QueryWrapper.create()
### exists, not exists
```java
QueryWrapper queryWrapper=QueryWrapper.create()
QueryWrapper queryWrapper = QueryWrapper.create()
.select()
.from(ACCOUNT)
.where(ACCOUNT.ID.ge(100))
@ -229,7 +232,7 @@ QueryWrapper queryWrapper=QueryWrapper.create()
### and (...) or (...)
```java
QueryWrapper queryWrapper=QueryWrapper.create()
QueryWrapper queryWrapper = QueryWrapper.create()
.select()
.from(ACCOUNT)
.where(ACCOUNT.ID.ge(100))
@ -246,7 +249,7 @@ QueryWrapper queryWrapper=QueryWrapper.create()
### group by
```java
QueryWrapper queryWrapper=QueryWrapper.create()
QueryWrapper queryWrapper = QueryWrapper.create()
.select()
.from(ACCOUNT)
.groupBy(ACCOUNT.USER_NAME);
@ -259,7 +262,7 @@ QueryWrapper queryWrapper=QueryWrapper.create()
### having
```java
QueryWrapper queryWrapper=QueryWrapper.create()
QueryWrapper queryWrapper = QueryWrapper.create()
.select()
.from(ACCOUNT)
.groupBy(ACCOUNT.USER_NAME)
@ -275,7 +278,7 @@ QueryWrapper queryWrapper=QueryWrapper.create()
### orderBy
```java
QueryWrapper queryWrapper=QueryWrapper.create()
QueryWrapper queryWrapper = QueryWrapper.create()
.select()
.from(ACCOUNT)
.orderBy(ACCOUNT.AGE.asc(), ACCOUNT.USER_NAME.desc().nullsLast());
@ -342,19 +345,17 @@ QueryWrapper queryWrapper = QueryWrapper.create()
### Questions
**1、how to generate "ACCOUNT" class for QueryWrapper by Account.java ?**
**1. How to generate "ACCOUNT" class for QueryWrapper by Account.java ?**
build the project by IDE, or execute maven build command: `mvn clean package`
Build the project by IDE, or execute maven build command: `mvn clean package`
![](./docs/assets/images/build_idea.png)
## More Samples
- 1、[Mybatis-Flex Only (Native)](./mybatis-flex-test/mybatis-flex-native-test)
- 2、[Mybatis-Flex with Spring](./mybatis-flex-test/mybatis-flex-spring-test)
- 3、[Mybatis-Flex with Spring boot](./mybatis-flex-test/mybatis-flex-spring-boot-test)
1. [Mybatis-Flex Only (Native)](./mybatis-flex-test/mybatis-flex-native-test)
2. [Mybatis-Flex with Spring](./mybatis-flex-test/mybatis-flex-spring-test)
3. [Mybatis-Flex with Spring boot](./mybatis-flex-test/mybatis-flex-spring-boot-test)

View File

@ -1,4 +1,8 @@
![](./docs/assets/images/logo_en.png)
<h4 align="right"><a href="https://github.com/mybatis-flex/mybatis-flex/blob/main/readme.md">English</a> | <strong>简体中文</strong></h4>
<p align="center">
<img src="./docs/assets/images/logo_en.png"/>
</p>
# MyBatis-Flex 一个优雅的 MyBatis 增强框架
@ -34,18 +38,17 @@
## 特征
#### 1很轻量
#### 1. 很轻量
> MyBatis-Flex 整个框架只依赖 MyBatis再无其他任何第三方依赖。
#### 2只增强
#### 2. 只增强
> MyBatis-Flex 支持 CRUD、分页查询、多表查询、批量操作但不丢失 MyBatis 原有的任何功能。
#### 3高性能
> MyBatis-Flex 采用独特的技术架构、相比许多同类框架MyBatis-Flex 的在增删改查等方面的性能均超越其 5~10 倍或以上。
#### 3. 高性能
> MyBatis-Flex 采用独特的技术架构、相比许多同类框架MyBatis-Flex 的在增删改查等方面的性能均超越其 5-10 倍或以上。
#### 4、更灵动
> MyBatis-Flex 支持多主键、多表查询、逻辑删除、乐观锁、数据脱敏、数据加密、多数据源、分库分表、字段权限、
> 字段加密、多租户、事务管理、SQL 审计... 等等等等。 这一切,免费且灵动。
#### 4. 更灵动
> MyBatis-Flex 支持多主键、多表查询、逻辑删除、乐观锁、数据脱敏、数据加密、多数据源、分库分表、字段权限、字段加密、多租户、事务管理、SQL 审计等特性。 这一切,免费且灵动。
## QQ 群
@ -62,14 +65,11 @@
- 示例 3[Mybatis-Flex with Spring boot](./mybatis-flex-test/mybatis-flex-spring-boot-test)
- 示例 4[Db + Row](./mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/DbTestStarter.java)
## hello world原生
**第 1 步:编写 Entity 实体类**
```java
@Table("tb_account")
public class Account {
@ -79,11 +79,10 @@ public class Account {
private Date birthday;
private int sex;
//getter setter
// getter setter
}
```
**第 2 步:开始查询数据**
示例 1查询 1 条数据
@ -106,21 +105,21 @@ class HelloWorld {
.getMapper(AccountMapper.class);
//示例1查询 id=100 条数据
// 示例1查询 id = 100 条数据
Account account = mapper.selectOneById(100);
}
}
```
> 以上的 `AccountMapper.class` 为 Mybatis-Flex 自动通过 APT 生成,无需手动编码。也可以关闭自动生成功能,手动编写 AccountMapper更多查看 APT 文档。
> 以上的 `AccountMapper.class` 为 MyBatis-Flex 自动通过 APT 生成,无需手动编码。也可以关闭自动生成功能,手动编写 AccountMapper更多查看 APT 文档。
示例2查询列表
示例 2查询列表
```java
//示例2通过 QueryWrapper 构建条件查询数据列表
// 示例 2通过 QueryWrapper 构建条件查询数据列表
QueryWrapper query = QueryWrapper.create()
.select()
.from(ACCOUNT) // 单表查询时表名可省略自动使用Mapper泛型对应的表
.from(ACCOUNT) // 单表查询时表名可省略,自动使用 Mapper 泛型对应的表
.where(ACCOUNT.ID.ge(100))
.and(ACCOUNT.USER_NAME.like("张").or(ACCOUNT.USER_NAME.like("李")));
@ -131,10 +130,10 @@ QueryWrapper query = QueryWrapper.create()
List<Account> accounts = accountMapper.selectListByQuery(query);
```
示例3分页查询
示例 3分页查询
```java
// 示例3分页查询
// 示例 3分页查询
// 查询第 5 页,每页 10 条数据,通过 QueryWrapper 构建条件查询
QueryWrapper query=QueryWrapper.create()
.select()
@ -299,7 +298,7 @@ QueryWrapper queryWrapper = QueryWrapper.create()
### exists, not exists
```java
QueryWrapper queryWrapper=QueryWrapper.create()
QueryWrapper queryWrapper = QueryWrapper.create()
.select()
.from(ACCOUNT)
.where(ACCOUNT.ID.ge(100))
@ -431,8 +430,7 @@ QueryWrapper queryWrapper = QueryWrapper.create()
// SELECT * FROM "tb_account" ORDER BY "id" DESC ROWS 20 TO 30
```
> 在以上的 "limit... offset" 示例中Mybatis-Flex 能够自动识别当前数据库,并生成不同的 SQL用户也可以很轻易的通过
> `DialectFactory` 注册(新增或改写)自己的实现方言。
> 在以上的 "limit... offset" 示例中MyBatis-Flex 能够自动识别当前数据库,并生成不同的 SQL用户也可以很轻易的通过 `DialectFactory` 注册(新增或改写)自己的实现方言。
### 存在疑问?
@ -443,43 +441,42 @@ QueryWrapper queryWrapper = QueryWrapper.create()
**疑问 2如何通过实体类 Account.java 生成 QueryWrapper 所需要的 "ACCOUNT" 类 ?**
Mybatis-Flex 使用了 APTAnnotation Processing Tool技术在项目编译的时候会自动根据 Entity 类定义的字段帮你生成 "ACCOUNT" 类以及 Entity 对应的 Mapper 类,
通过开发工具构建项目(如下图),或者执行 maven 编译命令: `mvn clean package` 都可以自动生成。这个原理和 lombok 一致。
MyBatis-Flex 使用了 APTAnnotation Processing Tool技术在项目编译的时候会自动根据 Entity 类定义的字段帮你生成 "ACCOUNT" 类以及 Entity 对应的 Mapper 类,
通过开发工具构建项目(如下图),或者执行 maven 编译命令: `mvn clean package` 都可以自动生成。这个原理和 Lombok 一致。
![](./docs/assets/images/build_idea.png)
> 更多关于 Mybatis-Flex APT 的配置,请点击 [这里](./docs/zh/others/apt.md)。
> 更多关于 MyBatis-Flex APT 的配置,请点击 [这里](./docs/zh/others/apt.md)。
## Db + Row 工具类
Db + Row 工具类,提供了在 Entity 实体类之外的数据库操作能力。使用 Db + Row 时,无需对数据库表进行映射, Row 是一个 HashMap 的子类,相当于一个通用的 Entity。以下为 Db + Row
的一些示例:
Db + Row 工具类,提供了在 Entity 实体类之外的数据库操作能力。使用 Db + Row 时,无需对数据库表进行映射, Row 是一个 HashMap 的子类,相当于一个通用的 Entity。以下为 Db + Row 的一些示例:
```java
//使用原生 SQL 插入数据
// 使用原生 SQL 插入数据
String sql="insert into tb_account(id,name) value (?, ?)";
Db.insertBySql(sql, 1, "michael");
//使用 Row 插入数据
// 使用 Row 插入数据
Row account = new Row();
account.set("id", 100);
account.set("name", "Michael");
Db.insert("tb_account", account);
//根据主键查询数据
// 根据主键查询数据
Row row = Db.selectOneById("tb_account", "id", 1);
//Row 可以直接转换为 Entity 实体类,且性能极高
// Row 可以直接转换为 Entity 实体类,且性能极高
Account account = row.toEntity(Account.class);
//查询所有大于 18 岁的用户
// 查询所有大于 18 岁的用户
String listsql = "select * from tb_account where age > ?"
List<Row> rows = Db.selectListBySql(sql, 18);
//分页查询:每页 10 条数据,查询第 3 页的年龄大于 18 的用户
// 分页查询:每页 10 条数据,查询第 3 页的年龄大于 18 的用户
QueryWrapper query = QueryWrapper.create()
.where(ACCOUNT.AGE.ge(18));
Page<Row> rowPage = Db.paginate("tb_account", 3, 10, query);
@ -487,19 +484,19 @@ Page<Row> rowPage = Db.paginate("tb_account", 3, 10, query);
> Db 工具类还提供了更多 增、删、改、查和分页查询等方法。
>
> 具体参考: [Db.java](./mybatis-flex-core/src/main/java/com/mybatisflex/core/row/Db.java) 。
> 具体参考:[Db.java](./mybatis-flex-core/src/main/java/com/mybatisflex/core/row/Db.java) 。
>
> 更多关于 Row 插入时的**主键生成机制**、以及Db 的**事务管理**等,请点击 [这里](./docs/zh/core/db-row.md) 。
> 更多关于 Row 插入时的 **主键生成机制**、以及Db 的 **事务管理** 等,请点击 [这里](./docs/zh/core/db-row.md) 。
## Entity 部分字段更新
相比市面上的其他框架,这部分的功能应该也算是 MyBatis-Flex 的亮点之一。在 BaseMapper 中Mybatis-Flex 提供了如下的方法:
相比市面上的其他框架,这部分的功能应该也算是 MyBatis-Flex 的亮点之一。在 BaseMapper 中MyBatis-Flex 提供了如下的方法:
```java
update(T entity)
```
有些场景下,我们可能希望只更新 几个 字段,而其中个别字段需要更新为 null。此时需要用到 `UpdateEntity` 工具类,以下是示例代码
有些场景下,我们可能希望只更新 几个 字段,而其中个别字段需要更新为 `null`此时需要用到 `UpdateEntity` 工具类:
```java
Account account = UpdateEntity.of(Account.class);
@ -510,21 +507,20 @@ account.setSex(1);
accountMapper.update(account);
```
以上的示例中,会把 id 为 100 这条数据中的 user_name 字段更新为 nullsex 字段更新为 1其他字段不会被更新。也就是说通过 `UpdateEntity`
创建的对象,只会更新调用了 setter 方法的字段,若不调用 setter 方法,不管这个对象里的属性的值是什么,都不会更新到数据库。
在以上的示例中,会把 id 为 100 这条数据中的 user_name 字段更新为 `null`sex 字段更新为 1其他字段不会被更新。也就是说通过 `UpdateEntity` 创建的对象,只会更新调用了 setter 方法的字段,若不调用 setter 方法,不管这个对象里的属性的值是什么,都不会更新到数据库。
其生成的 sql 内容如下:
其生成的 SQL 内容如下:
```sql
update tb_account
set user_name = ?, sex = ? where id = ?
#params: null,1,100
# params: null,1,100
```
## 自定义 TypeHandler
使用 @Column 注解:
使用 `@Column` 注解:
```java
@Table("tb_account")
@ -538,7 +534,7 @@ public class Account {
@Column(typeHandler = Fastjson2TypeHandler.class)
private Map<String, Object> options;
//getter setter
// getter setter
public void addOption(String key, Object value) {
if (options == null) {
@ -558,7 +554,7 @@ account.addOption("c1", 11);
account.addOption("c2", "zhang");
account.addOption("c3", new Date());
```
mybatis 日志:
MyBatis 日志:
```
==> Preparing: INSERT INTO tb_account (user_name, options) VALUES (?, ?)
==> Parameters: test(String), {"c3":"2023-03-17 09:10:16.546","c1":11,"c2":"zhang"}(String)
@ -566,7 +562,7 @@ mybatis 日志:
## 多主键
Mybatis-Flex 多主键就是在 Entity 类里有多个 `@Id` 注解标识而已,比如:
MyBatis-Flex 多主键就是在 Entity 类里有多个 `@Id` 注解标识而已,比如:
```java
@ -583,11 +579,11 @@ public class Account {
}
```
当我们保存数据Account 的 id 主键为自增,而 otherId 主键则通过 uuid 生成。
保存数据时Account 的 id 主键为自增,而 otherId 主键则通过 uuid 生成。
### 自定义主键生成器
第 1 步:编写一个类,实现 `IKeyGenerator` 接口,例如
第 1 步:编写一个类,实现 `IKeyGenerator` 接口:
```java
public class UUIDKeyGenerator implements IKeyGenerator {
@ -599,7 +595,7 @@ public class UUIDKeyGenerator implements IKeyGenerator {
}
```
第 2 步:注册 UUIDKeyGenerator
第 2 步:注册 `UUIDKeyGenerator`
```java
KeyGeneratorFactory.register("myUUID", new UUIDKeyGenerator());
@ -622,7 +618,6 @@ public class Account {
### 使用数据库 Sequence 生成
```java
@Table("tb_account")
public class Account {
@ -636,11 +631,13 @@ public class Account {
## 更多文档
- [https://mybatis-flex.com](https://mybatis-flex.com)
- [mybatis-flex](https://mybatis-flex.com)
## 还有问题?
加入 QQ 交流群: 532992631
![](./docs/assets/images/qq_group.png)
<p align="center">
<img src="./docs/assets/images/qq_group.png"/>
</p>