This commit is contained in:
开源海哥 2023-12-03 12:58:27 +08:00
commit a594569307
7 changed files with 178 additions and 96 deletions

View File

@ -42,6 +42,7 @@ public enum DbType {
* DB2 * DB2
*/ */
DB2("db2", "DB2 数据库"), DB2("db2", "DB2 数据库"),
DB2_1005("db2_1005", "DB2 10.5版本数据库"),
/** /**
* H2 * H2

View File

@ -16,15 +16,15 @@
package com.mybatisflex.core.dialect; package com.mybatisflex.core.dialect;
import java.util.EnumMap;
import java.util.Map;
import org.apache.ibatis.util.MapUtil;
import com.mybatisflex.core.FlexGlobalConfig; import com.mybatisflex.core.FlexGlobalConfig;
import com.mybatisflex.core.dialect.impl.CommonsDialectImpl; import com.mybatisflex.core.dialect.impl.CommonsDialectImpl;
import com.mybatisflex.core.dialect.impl.DB2105Dialect;
import com.mybatisflex.core.dialect.impl.DmDialect; import com.mybatisflex.core.dialect.impl.DmDialect;
import com.mybatisflex.core.dialect.impl.OracleDialect; import com.mybatisflex.core.dialect.impl.OracleDialect;
import com.mybatisflex.core.util.ObjectUtil; import com.mybatisflex.core.util.ObjectUtil;
import org.apache.ibatis.util.MapUtil;
import java.util.EnumMap;
import java.util.Map;
/** /**
* 方言工厂类用于创建方言 * 方言工厂类用于创建方言
@ -139,6 +139,8 @@ public class DialectFactory {
case FIREBIRD: case FIREBIRD:
case DB2: case DB2:
return new CommonsDialectImpl(KeywordWrap.NONE, LimitOffsetProcessor.DERBY); return new CommonsDialectImpl(KeywordWrap.NONE, LimitOffsetProcessor.DERBY);
case DB2_1005:
return new DB2105Dialect(KeywordWrap.NONE, DB2105Dialect.DB2105LimitOffsetProcessor.DB2105);
case SQLSERVER: case SQLSERVER:
return new CommonsDialectImpl(KeywordWrap.SQUARE_BRACKETS, LimitOffsetProcessor.SQLSERVER); return new CommonsDialectImpl(KeywordWrap.SQUARE_BRACKETS, LimitOffsetProcessor.SQLSERVER);
case SQLSERVER_2005: case SQLSERVER_2005:

View File

@ -0,0 +1,36 @@
package com.mybatisflex.core.dialect.impl;
import com.mybatisflex.core.dialect.KeywordWrap;
import com.mybatisflex.core.dialect.LimitOffsetProcessor;
public class DB2105Dialect extends CommonsDialectImpl {
//TODO: 根据DatabaseMetaData获取数据库厂商名和版本号
public static final String DB2_1005_PRODUCT_VERSION = "1005";
public static final String DB2_PRODUCT_NAME = "DB2";
public DB2105Dialect(KeywordWrap keywordWrap, LimitOffsetProcessor limitOffsetProcessor) {
super(keywordWrap, limitOffsetProcessor);
}
public interface DB2105LimitOffsetProcessor {
LimitOffsetProcessor DB2105 = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
StringBuilder limitSqlFragment = new StringBuilder(
"select * from ( select u_.*,rownumber() over() as rn from ( ");
limitSqlFragment.append(sql);
limitSqlFragment.append(" )u_ ) temp_ where temp_.rn between ");
if (limitRows != null && limitOffset != null) {
limitSqlFragment.append(limitOffset + 1);
limitSqlFragment.append(" and ");
limitSqlFragment.append(limitRows + limitOffset);
} else if (limitRows != null) {
limitSqlFragment.append("1 and ");
limitSqlFragment.append(limitRows);
} else {
return sql;
}
return limitSqlFragment;
};
}
}

View File

@ -118,17 +118,62 @@ public class TableInfoFactory {
if (mapperClass == null || mapperClass == Object.class) { if (mapperClass == null || mapperClass == Object.class) {
return null; return null;
} }
return getEntityClass(mapperClass, null);
}
private static Class<?> getEntityClass(Class<?> mapperClass, Type[] actualTypeArguments) {
// 检查基接口
Type[] genericInterfaces = mapperClass.getGenericInterfaces(); Type[] genericInterfaces = mapperClass.getGenericInterfaces();
if (genericInterfaces.length == 1) { for (Type type : genericInterfaces) {
Type type = genericInterfaces[0];
if (type instanceof ParameterizedType) { 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) { } 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

@ -57,8 +57,8 @@
<mybatis-flex.version>1.7.5</mybatis-flex.version> <mybatis-flex.version>1.7.5</mybatis-flex.version>
<mybatis.version>3.5.13</mybatis.version> <mybatis.version>3.5.14</mybatis.version>
<mybatis-spring.version>2.1.0</mybatis-spring.version> <mybatis-spring.version>2.1.2</mybatis-spring.version>
<mybatis-freemarker.version>1.2.4</mybatis-freemarker.version> <mybatis-freemarker.version>1.2.4</mybatis-freemarker.version>
<mybatis-velocity.version>2.1.2</mybatis-velocity.version> <mybatis-velocity.version>2.1.2</mybatis-velocity.version>
<mybatis-thymeleaf.version>1.0.4</mybatis-thymeleaf.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. # MyBatis-Flex is an elegant Mybatis Enhancement Framework.
@ -33,20 +37,19 @@
## Features ## Features
- 1、MyBatis-Flex is very lightweight, and it only depends on Mybatis and no other third-party dependencies 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 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 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. 4. Support multiple databases, and expand through dialects flexibly
- 5、Support combined primary keys and different primary key content generation strategies 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 6. Extremely friendly SQL query, IDE automatically prompts and no worries about mistakes
- 7、More little surprises 7. More little surprises
## hello worldWithout Spring ## hello world(Without Spring)
**step 1: write entity class** **step 1: write entity class**
```java ```java
@Table("tb_account") @Table("tb_account")
public class Account { public class Account {
@ -64,13 +67,13 @@ public class Account {
```java ```java
public interface AccountMapper extends BaseMapper<Account> { public interface AccountMapper extends BaseMapper<Account> {
//only Mapper interface define. // only Mapper interface define
} }
``` ```
**step 3: start query data** **step 3: start query data**
e.g. 1 query by primary key e.g. 1: query by primary key
```java ```java
class HelloWorld { class HelloWorld {
@ -342,19 +345,17 @@ QueryWrapper queryWrapper = QueryWrapper.create()
### Questions ### 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) ![](./docs/assets/images/build_idea.png)
## More Samples ## More Samples
- 1、[Mybatis-Flex Only (Native)](./mybatis-flex-test/mybatis-flex-native-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) 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) 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 增强框架 # MyBatis-Flex 一个优雅的 MyBatis 增强框架
@ -34,18 +38,17 @@
## 特征 ## 特征
#### 1很轻量 #### 1. 很轻量
> MyBatis-Flex 整个框架只依赖 MyBatis再无其他任何第三方依赖。 > MyBatis-Flex 整个框架只依赖 MyBatis再无其他任何第三方依赖。
#### 2只增强 #### 2. 只增强
> MyBatis-Flex 支持 CRUD、分页查询、多表查询、批量操作但不丢失 MyBatis 原有的任何功能。 > MyBatis-Flex 支持 CRUD、分页查询、多表查询、批量操作但不丢失 MyBatis 原有的任何功能。
#### 3高性能 #### 3. 高性能
> MyBatis-Flex 采用独特的技术架构、相比许多同类框架MyBatis-Flex 的在增删改查等方面的性能均超越其 5~10 倍或以上。 > MyBatis-Flex 采用独特的技术架构、相比许多同类框架MyBatis-Flex 的在增删改查等方面的性能均超越其 5-10 倍或以上。
#### 4、更灵动 #### 4. 更灵动
> MyBatis-Flex 支持多主键、多表查询、逻辑删除、乐观锁、数据脱敏、数据加密、多数据源、分库分表、字段权限、 > MyBatis-Flex 支持多主键、多表查询、逻辑删除、乐观锁、数据脱敏、数据加密、多数据源、分库分表、字段权限、字段加密、多租户、事务管理、SQL 审计等特性。 这一切,免费且灵动。
> 字段加密、多租户、事务管理、SQL 审计... 等等等等。 这一切,免费且灵动。
## QQ 群 ## QQ 群
@ -62,14 +65,11 @@
- 示例 3[Mybatis-Flex with Spring boot](./mybatis-flex-test/mybatis-flex-spring-boot-test) - 示例 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) - 示例 4[Db + Row](./mybatis-flex-test/mybatis-flex-native-test/src/main/java/com/mybatisflex/test/DbTestStarter.java)
## hello world原生 ## hello world原生
**第 1 步:编写 Entity 实体类** **第 1 步:编写 Entity 实体类**
```java ```java
@Table("tb_account") @Table("tb_account")
public class Account { public class Account {
@ -83,7 +83,6 @@ public class Account {
} }
``` ```
**第 2 步:开始查询数据** **第 2 步:开始查询数据**
示例 1查询 1 条数据 示例 1查询 1 条数据
@ -112,7 +111,7 @@ class HelloWorld {
} }
``` ```
> 以上的 `AccountMapper.class` 为 Mybatis-Flex 自动通过 APT 生成,无需手动编码。也可以关闭自动生成功能,手动编写 AccountMapper更多查看 APT 文档。 > 以上的 `AccountMapper.class` 为 MyBatis-Flex 自动通过 APT 生成,无需手动编码。也可以关闭自动生成功能,手动编写 AccountMapper更多查看 APT 文档。
示例 2查询列表 示例 2查询列表
@ -431,8 +430,7 @@ QueryWrapper queryWrapper = QueryWrapper.create()
// SELECT * FROM "tb_account" ORDER BY "id" DESC ROWS 20 TO 30 // SELECT * FROM "tb_account" ORDER BY "id" DESC ROWS 20 TO 30
``` ```
> 在以上的 "limit... offset" 示例中Mybatis-Flex 能够自动识别当前数据库,并生成不同的 SQL用户也可以很轻易的通过 > 在以上的 "limit... offset" 示例中MyBatis-Flex 能够自动识别当前数据库,并生成不同的 SQL用户也可以很轻易的通过 `DialectFactory` 注册(新增或改写)自己的实现方言。
> `DialectFactory` 注册(新增或改写)自己的实现方言。
### 存在疑问? ### 存在疑问?
@ -443,17 +441,16 @@ QueryWrapper queryWrapper = QueryWrapper.create()
**疑问 2如何通过实体类 Account.java 生成 QueryWrapper 所需要的 "ACCOUNT" 类 ?** **疑问 2如何通过实体类 Account.java 生成 QueryWrapper 所需要的 "ACCOUNT" 类 ?**
Mybatis-Flex 使用了 APTAnnotation Processing Tool技术在项目编译的时候会自动根据 Entity 类定义的字段帮你生成 "ACCOUNT" 类以及 Entity 对应的 Mapper 类, MyBatis-Flex 使用了 APTAnnotation Processing Tool技术在项目编译的时候会自动根据 Entity 类定义的字段帮你生成 "ACCOUNT" 类以及 Entity 对应的 Mapper 类,
通过开发工具构建项目(如下图),或者执行 maven 编译命令: `mvn clean package` 都可以自动生成。这个原理和 lombok 一致。 通过开发工具构建项目(如下图),或者执行 maven 编译命令: `mvn clean package` 都可以自动生成。这个原理和 Lombok 一致。
![](./docs/assets/images/build_idea.png) ![](./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 工具类
Db + Row 工具类,提供了在 Entity 实体类之外的数据库操作能力。使用 Db + Row 时,无需对数据库表进行映射, Row 是一个 HashMap 的子类,相当于一个通用的 Entity。以下为 Db + Row Db + Row 工具类,提供了在 Entity 实体类之外的数据库操作能力。使用 Db + Row 时,无需对数据库表进行映射, Row 是一个 HashMap 的子类,相当于一个通用的 Entity。以下为 Db + Row 的一些示例:
的一些示例:
```java ```java
// 使用原生 SQL 插入数据 // 使用原生 SQL 插入数据
@ -493,13 +490,13 @@ Page<Row> rowPage = Db.paginate("tb_account", 3, 10, query);
## Entity 部分字段更新 ## Entity 部分字段更新
相比市面上的其他框架,这部分的功能应该也算是 MyBatis-Flex 的亮点之一。在 BaseMapper 中Mybatis-Flex 提供了如下的方法: 相比市面上的其他框架,这部分的功能应该也算是 MyBatis-Flex 的亮点之一。在 BaseMapper 中MyBatis-Flex 提供了如下的方法:
```java ```java
update(T entity) update(T entity)
``` ```
有些场景下,我们可能希望只更新 几个 字段,而其中个别字段需要更新为 null。此时需要用到 `UpdateEntity` 工具类,以下是示例代码 有些场景下,我们可能希望只更新 几个 字段,而其中个别字段需要更新为 `null`此时需要用到 `UpdateEntity` 工具类:
```java ```java
Account account = UpdateEntity.of(Account.class); Account account = UpdateEntity.of(Account.class);
@ -510,10 +507,9 @@ account.setSex(1);
accountMapper.update(account); accountMapper.update(account);
``` ```
以上的示例中,会把 id 为 100 这条数据中的 user_name 字段更新为 nullsex 字段更新为 1其他字段不会被更新。也就是说通过 `UpdateEntity` 在以上的示例中,会把 id 为 100 这条数据中的 user_name 字段更新为 `null`sex 字段更新为 1其他字段不会被更新。也就是说通过 `UpdateEntity` 创建的对象,只会更新调用了 setter 方法的字段,若不调用 setter 方法,不管这个对象里的属性的值是什么,都不会更新到数据库。
创建的对象,只会更新调用了 setter 方法的字段,若不调用 setter 方法,不管这个对象里的属性的值是什么,都不会更新到数据库。
其生成的 sql 内容如下: 其生成的 SQL 内容如下:
```sql ```sql
update tb_account update tb_account
@ -524,7 +520,7 @@ set user_name = ?, sex = ? where id = ?
## 自定义 TypeHandler ## 自定义 TypeHandler
使用 @Column 注解: 使用 `@Column` 注解:
```java ```java
@Table("tb_account") @Table("tb_account")
@ -558,7 +554,7 @@ account.addOption("c1", 11);
account.addOption("c2", "zhang"); account.addOption("c2", "zhang");
account.addOption("c3", new Date()); account.addOption("c3", new Date());
``` ```
mybatis 日志: MyBatis 日志:
``` ```
==> Preparing: INSERT INTO tb_account (user_name, options) VALUES (?, ?) ==> 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) ==> 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 ```java
@ -583,11 +579,11 @@ public class Account {
} }
``` ```
当我们保存数据Account 的 id 主键为自增,而 otherId 主键则通过 uuid 生成。 保存数据时Account 的 id 主键为自增,而 otherId 主键则通过 uuid 生成。
### 自定义主键生成器 ### 自定义主键生成器
第 1 步:编写一个类,实现 `IKeyGenerator` 接口,例如 第 1 步:编写一个类,实现 `IKeyGenerator` 接口:
```java ```java
public class UUIDKeyGenerator implements IKeyGenerator { public class UUIDKeyGenerator implements IKeyGenerator {
@ -599,7 +595,7 @@ public class UUIDKeyGenerator implements IKeyGenerator {
} }
``` ```
第 2 步:注册 UUIDKeyGenerator 第 2 步:注册 `UUIDKeyGenerator`
```java ```java
KeyGeneratorFactory.register("myUUID", new UUIDKeyGenerator()); KeyGeneratorFactory.register("myUUID", new UUIDKeyGenerator());
@ -622,7 +618,6 @@ public class Account {
### 使用数据库 Sequence 生成 ### 使用数据库 Sequence 生成
```java ```java
@Table("tb_account") @Table("tb_account")
public class 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 加入 QQ 交流群: 532992631
![](./docs/assets/images/qq_group.png) <p align="center">
<img src="./docs/assets/images/qq_group.png"/>
</p>