Merge remote-tracking branch 'origin/main'

# Conflicts:
#	docs/zh/intro/use-in-kotlin.md
This commit is contained in:
kamosama 2023-09-24 14:35:54 +08:00
commit 71846bad1c
92 changed files with 1130 additions and 150 deletions

View File

@ -7,7 +7,7 @@ body:
attributes:
label: 这个 Bug 是否已经存在:
options:
- label: 已经搜索过现有的问题 (https://gitee.com/mybatis-flex/mybatis-flex/issues)
- label: 确定已经把 MyBatis-Flex 升级到最新版本 v1.6.6,并已搜索过现有的问题 (https://gitee.com/mybatis-flex/mybatis-flex/issues)
required: true
- type: textarea
attributes:

View File

@ -13,7 +13,7 @@ body:
attributes:
label: 这个问题是否已经存在:
options:
- label: 已经搜索过现有的问题 (https://gitee.com/mybatis-flex/mybatis-flex/issues)
- label: 确定已经把 MyBatis-Flex 升级到最新版本 v1.6.6,并已搜索过现有的问题 (https://gitee.com/mybatis-flex/mybatis-flex/issues)
required: true
- type: textarea
id: question-description

View File

@ -0,0 +1,23 @@
## 该 PR 关联的 Issue
关联 Issue 的解决说明
## 修改描述
- 第一部分
- 第二部分
- 第三部分
## 测试用例
1. 第一步
2. 第二步
3. 第三步
## 修复效果
附上图片或测试输出,测试输出请用格式:
> ```
> 测试输出
> ```

View File

@ -4,6 +4,42 @@
## v1.6.6 20230922:
- 新增UpdateChain.of 使用 Mapper 进行构造方便在批量操作使用的功能
- 新增QueryWrapper.select(Iterable) 方法,方便 Kotlin 扩展
- 新增Relation 注解新增 valueField 配置,当不为空串时值进行某个字段赋值,感谢 @ice-samll
- 优化:转驼峰方法多次转换保持结果一致,感谢 @617054137
- 优化:生成列别名规范,保持用户原始的列别名命名,感谢 @font-c
- 修复QueryWrapper 在某些场景下构建 SQL 会出现两个 AS 关键字的问题
- 修复Db 或 MyBatis 原生查询驼峰转换需处理不包含下划线的字段,感谢 @617054137
- 测试:增加 Relation 注解单字段赋值 Springboot 测试,感谢 @ice-samll
- 文档:添加 Relation 注解单字段赋值的相关文档,感谢 @ice-samll
- 文档:添加关于批量操作使用 UpdateChain 的相关示例
## v1.6.5 20230914:
- 新增:代码生成器为 Oracle 的 JdbcTypeMapping 类型 OracleBlob 添加映射处理
- 新增LogicDeleteManager 和 TenantManager 添加 Runnable 无返回值重载,感谢 @Suomm
- 新增RawQueryColumn 添加参数占位符的支持功能,感谢 @Suomm
- 新增:代码生成器添加关于 solon Controller 生成的代码模板,感谢 @ZhuHJay
- 新增UpdateEntity 添加自动去除有忽略注解的字段的功能,感谢 @aqnghu
- 优化:代码生成器配置类添加 Serializable 接口实现的支持,方便自定义缓存保存,感谢 @zoufang162
- 优化:使用 lambda 优化部分写法,感谢 @handy-git
- 优化:使用 try-with-resources 释放 Connection感谢 @handy-git
- 优化DataSourceBuilder 出错时,吞掉原始的 exception 的 message 信息的问题 #I7YYRF
- 优化:`@Table` 注解增加 `@Inherited` 修饰,感谢 @jerryzhengsz1
- 修复:当工作流引擎 activti6 整合 MyBatis-Flex 可能出现 NPE 的问题
- 修复:通过 XML 自定义的 SQL 查询不兼容自定义枚举使用的问题,感谢 @lifejwang11
- 文档:更新关于 MyBatis-Flex-Admin 的相关文档
- 文档:添加关于 MyBatis-Flex-Kotlin 的相关文档 感谢 @kamo-sama
- 文档:修正自定义脱敏处理器的示例代码错误,感谢 @wang_yong_ji
- 文档:添加关于 MyBatis-Flex 与 activiti6 以及 Flowable 等工作流引擎集成的相关文档,感谢 @simple_wind
- 文档:修复逻辑删除文档的个别错别字,感谢 @cainiao3853
- 文档:修正自定义映射的相关代码示例错误,感谢 @tycms
## v1.6.4 20230903:
- 新增QueryWrapper 添加动态排序功能的支持,感谢 @Suomm
- 优化Solon 取消无必要的 FlexSqlSessionFactoryBuilder 注入,感谢 @noear_admin

View File

@ -49,6 +49,7 @@ export default defineConfig({
{text: '快速开始', link: '/zh/intro/getting-started'},
{text: 'Maven 依赖', link: '/zh/intro/maven'},
{text: 'Gradle 依赖', link: '/zh/intro/gradle'},
{text: 'Kotlin 使用', link: '/zh/intro/use-in-kotlin'},
{text: '和同类框架「功能」对比', link: '/zh/intro/comparison'},
{text: '和同类框架「性能」对比', link: '/zh/intro/benchmark'},
{text: '使用 Mybatis 原生功能', link: '/zh/intro/use-mybatis-native'},
@ -87,6 +88,7 @@ export default defineConfig({
{text: 'SQL 审计', link: '/zh/core/audit'},
{text: 'SQL 打印', link: '/zh/core/sql-print'},
{text: '多数据源', link: '/zh/core/multi-datasource'},
{text: '读写分离 💥', link: '/zh/core/read-write-splitting'},
{text: '数据源加密', link: '/zh/core/datasource-encryption'},
{text: '动态表名', link: '/zh/core/dynamic-table'},
{text: '事务管理', link: '/zh/core/tx'},

View File

@ -71,7 +71,7 @@ public class Account {
`AccountMapper` 方法直接查询,例如:
```java
QueryWrapper qw = new QeuryWrapper();
QueryWrapper qw = new QueryWrapper();
qw.select(ACCOUNT.ALL_COLUMNS)
.where(ACCOUNT.ID.ge(100));

View File

@ -87,7 +87,18 @@ Db.executeBatch(accounts, 1000, AccountMapper.class
```
以上的 **错误** 示例,是因为没有用到 `mapper` 参数,因此,不仅仅 `Db.executeBatch` 返回的都是失败的内容,而且也起不到批量操作的作用。
以上代码需要修改为:
```java
List<Account> accounts = ....
Db.executeBatch(accounts, 1000, AccountMapper.class
, (mapper, account) -> {
UpdateChina.of(mapper) //使用 mapper 参数,才能起到批量执行的效果
.set(Account::getUserName, "张三")
.update();
});
```
## `Db.updateBatch` 方法

View File

@ -352,6 +352,92 @@ public class Account implements Serializable {
}
```
## 只查询一个字段值 <Badge type="tip" text="v1.6.6" />
`RelationOneToOne``RelationOneToMany``RelationManyToOne``RelationManyToMany`新增属性`valueField`
```java 7
/**
* 目标对象的关系实体类的属性绑定
* <p>
* 当字段不为空串时,只进行某个字段赋值(使用对应字段类型接收)
* @return 属性名称
*/
String valueField() default "";
```
> 注解其他属性配置使用不变,当配置了`valueField`值时,只提取目标对象关系实体类的该属性
>
> **使用场景**:例如,操作日志中有个 `createBy` (操作人)字段,此时在日志信息中需要显示操作人名称,且只需要这一个字段,此时使用实体接收会导致不必要的字段出现,接口文档也会变得混乱。
假设一个账户实体类 `UserVO5.java`
- 每个账户有一个唯一对应的`id_number`列在表`tb_id_card`
- 一个账户可以有多个角色,一个角色也可以分配给多个账户,他们通过中间表`tb_user_role`进行关系映射
```java {12,21,29}
@Table("tb_user")
public class UserVO5 {
@Id
private Integer userId;
private String userName;
private String password;
@RelationOneToOne(
selfField = "userId",
targetTable = "tb_id_card",
targetField = "id",
valueField = "idNumber"
)
//该处可以定义其他属性名,不一定要是目标对象的字段名
private String idNumberCustomFieldName;
@RelationManyToMany(
selfField = "userId",
targetTable = "tb_role",
targetField = "roleId",
valueField = "roleName",
joinTable = "tb_user_role",
joinSelfColumn = "user_id",
joinTargetColumn = "role_id"
)
private List<String> roleNameList;
//getter setter toString
}
```
进行查询
```java
List<UserVO5> userVO5List = userMapper.selectListWithRelationsByQueryAs(QueryWrapper.create(), UserVO5.class);
System.out.println(JSON.toJSONString(userVO5List));
```
输出结果
```json {6,7,13,14,20,21}
[
{
userId = 1,
userName = '张三',
password = '12345678',
idNumberCustomFieldName = 'F281C807-C40B-472D-82F5-6130199C6328',
roleNameList = [普通用户]
},
{
userId = 2,
userName = '李四',
password = '87654321',
idNumberCustomFieldName = '6176E9AD-36EF-4201-A5F7-CCE89B254952',
roleNameList = [普通用户, 贵族用户]
},
{
userId = 3,
userName = '王五',
password = '09897654',
idNumberCustomFieldName = 'A038E6EA-1FDE-4191-AA41-06F78E91F6C2',
roleNameList = [普通用户, 贵族用户, 超级贵族用户]
}
]
```
## 父子关系查询
比如在一些系统中,比如菜单会有一些父子关系,例如菜单表如下:

View File

@ -4,6 +4,42 @@
## v1.6.6 20230922:
- 新增UpdateChain.of 使用 Mapper 进行构造方便在批量操作使用的功能
- 新增QueryWrapper.select(Iterable) 方法,方便 Kotlin 扩展
- 新增Relation 注解新增 valueField 配置,当不为空串时值进行某个字段赋值,感谢 @ice-samll
- 优化:转驼峰方法多次转换保持结果一致,感谢 @617054137
- 优化:生成列别名规范,保持用户原始的列别名命名,感谢 @font-c
- 修复QueryWrapper 在某些场景下构建 SQL 会出现两个 AS 关键字的问题
- 修复Db 或 MyBatis 原生查询驼峰转换需处理不包含下划线的字段,感谢 @617054137
- 测试:增加 Relation 注解单字段赋值 Springboot 测试,感谢 @ice-samll
- 文档:添加 Relation 注解单字段赋值的相关文档,感谢 @ice-samll
- 文档:添加关于批量操作使用 UpdateChain 的相关示例
## v1.6.5 20230914:
- 新增:代码生成器为 Oracle 的 JdbcTypeMapping 类型 OracleBlob 添加映射处理
- 新增LogicDeleteManager 和 TenantManager 添加 Runnable 无返回值重载,感谢 @Suomm
- 新增RawQueryColumn 添加参数占位符的支持功能,感谢 @Suomm
- 新增:代码生成器添加关于 solon Controller 生成的代码模板,感谢 @ZhuHJay
- 新增UpdateEntity 添加自动去除有忽略注解的字段的功能,感谢 @aqnghu
- 优化:代码生成器配置类添加 Serializable 接口实现的支持,方便自定义缓存保存,感谢 @zoufang162
- 优化:使用 lambda 优化部分写法,感谢 @handy-git
- 优化:使用 try-with-resources 释放 Connection感谢 @handy-git
- 优化DataSourceBuilder 出错时,吞掉原始的 exception 的 message 信息的问题 #I7YYRF
- 优化:`@Table` 注解增加 `@Inherited` 修饰,感谢 @jerryzhengsz1
- 修复:当工作流引擎 activti6 整合 MyBatis-Flex 可能出现 NPE 的问题
- 修复:通过 XML 自定义的 SQL 查询不兼容自定义枚举使用的问题,感谢 @lifejwang11
- 文档:更新关于 MyBatis-Flex-Admin 的相关文档
- 文档:添加关于 MyBatis-Flex-Kotlin 的相关文档 感谢 @kamo-sama
- 文档:修正自定义脱敏处理器的示例代码错误,感谢 @wang_yong_ji
- 文档:添加关于 MyBatis-Flex 与 activiti6 以及 Flowable 等工作流引擎集成的相关文档,感谢 @simple_wind
- 文档:修复逻辑删除文档的个别错别字,感谢 @cainiao3853
- 文档:修正自定义映射的相关代码示例错误,感谢 @tycms
## v1.6.4 20230903:
- 新增QueryWrapper 添加动态排序功能的支持,感谢 @Suomm
- 优化Solon 取消无必要的 FlexSqlSessionFactoryBuilder 注入,感谢 @noear_admin

View File

@ -197,7 +197,7 @@ public interface LogicDeleteProcessor {
针对这种场景,目前有两个方案:
- **方案1 IService 的 removeById 方法**
- **方案1 IService 的 removeById 方法**
先更新 `deleteTime``deleteUserId`,然后再进行逻辑删除。同时需要保证这两个方法在同一个事务里进行,
如下代码:

View File

@ -60,7 +60,56 @@ public interface TenantFactory {
除了显示租户自己的数据以外,还包含下级租户的数据,这种场景则要求 `getTenantIds` 返回多个值。
- **场景3**:忽略租户条件,由代码自定义条件查询,此项要求 `getTenantIds` 返回 null 或者 空数组。
**注意!注意!注意!**
> 在整个应用中,应该只有一个 `TenantFactory` 实例,然后再通过其 `getTenantIds()` 方法里去获取当前的租户 ID在 Spring 常见中,我们可以通过在
> RequestContextHolder 中去获取当前的租户 ID。在其他框架中我们可以通过自定义 ThreadLocal 去获取 TenantId。
## 示例代码
```java
public class MyTenantFactory implements TenantFactory {
public Object[] getTenantIds(){
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
Long tenantId = attributes.getAttribute("tenantId", RequestAttributes.SCOPE_REQUEST);
return new Object[]{tenantId};
}
}
```
当然,`MyTenantFactory` 需要正常工作,我们需要在 Spring 拦截器里,需要通过 request 去获取当前的租户 ID并设置到 request 的 attribute如下代码所示
```java
public class TenantInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request
, HttpServletResponse response, Object handler) throws Exception {
//通过 request 去获取租户 ID
Long tenantId = getTenantIdByReuqest(request);
//设置租户ID到 request 的 attribute
request.setAttribute("tenantId", tenantId);
return true;
}
}
```
同时,在 `WebMvcConfigurer` 中,通过重写 `addInterceptors` 方法添加一下我们自定义的多租户拦截器:`TenantInterceptor`
```java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TenantInterceptor());
}
}
```
## SpringBoot 支持
@ -69,10 +118,9 @@ public interface TenantFactory {
```java
@Configuration
public class MyConfiguration {
@Bean
public TenantFactory tenantFactory(){
TenantFactory tenantFactory = new ....;
TenantFactory tenantFactory = new MyTenantFactory();
return tenantFactory;
}

View File

@ -0,0 +1,104 @@
# 读写分离
MyBatis-Flex 的读写分离功能是基于 【[多数据源](./multi-datasource.md)】 功能来实现的。
读写分离的功能,要求当前环境必须是多个数据库(也可理解为多个数据源),其原理是:
让主数据库master处理事务性操作比如增、删、改INSERT、DELETE、UPDATE而从数据库slave处理查询SELECT操作。
## 实现原理
在 MyBatis 框架中,我们知道: 所有关于数据库的的操作都是通过 Mapper 来进行的Mapper 里的一个方法,往往是和一个执行 SQL 一一对应。
因此,在 MyBatis-Flex 中,提供了一种基于 Mapper 方法的读写分离策略。
## 数据源分片策略
在 MyBatis-Flex 框架中,我们需要通过实现 `DataSourceShardingStrategy` 接口来自定义自己的数据源读写分离策略(分片策略)例如:
```java
public class MyStrategy implements DataSourceShardingStrategy {
public String doSharding(String currentDataSourceKey
, Object mapper, Method mapperMethod, Object[] methodArgs){
//返回新的数据源 key
return "newDataSourceKey";
}
}
```
doSharding 的参数分别为:
- currentDataSourceKey当前使用的数据源 key
- mapper当前的 mapper 对象
- mapperMethod: 当前的 mapper 方法
- methodArgs当前的 mapper 方法的参数内容
自定义好 数据源分片策略后,在项目启动时,需要通过 `DataSourceManager` 配置自己的自定义分片策略:
```java
DataSourceManager.setDataSourceShardingStrategy(new MyStrategy());
```
## 示例代码
假设数据源配置如下:
```yaml
mybatis-flex:
datasource:
master:
type: druid
url: jdbc:mysql://127.0.0.1:3306/master-db
username: root
password: 123456
slave1:
type: com.your.datasource.type2
url: jdbc:mysql://127.0.0.1:3306/slave1
username: root
password: 123456
slave2:
type: com.your.datasource.type2
url: jdbc:mysql://127.0.0.1:3306/slave2
username: root
password: 123456
other:
type: com.your.datasource.type2
url: jdbc:mysql://127.0.0.1:3306/other
username: root
password: 123456
```
以上配置中,一共有 4 个数据源,分别为 `master``slave1``slave2``other`
我们的需求是:在 增删改 时,走 master 数据源,而在查询时,随机自动使用 `slave1``slave2` 数据源进行负载均衡。
那么,我们的分片策略代码如下:
```java
public class MyStrategy implements DataSourceShardingStrategy {
public String doSharding(String currentDataSourceKey
, Object mapper, Method mapperMethod, Object[] methodArgs){
// 不管 other 数据源的情况
if ("other".equals(currentDataSourceKey)){
return currentDataSourceKey;
}
// 如果 mapper 的方法属于 增删改,使用 master 数据源
if (StringUtil.startWithAny(mapperMethod.getName(),
"insert", "delete", "update")){
return "master";
}
//其他场景,使用 slave1 或者 slave2 进行负载均衡
return "slave*";
}
}
```
## 注意事项
> MyBatis-Flex 的读写分离组件,只进行数据查询和数据操作时的读写分离,并不涉及主从数据库之间的数据同步,主从数据库同步需要用户自己在数据库服务器,通过第三方组件去实现。

View File

@ -307,3 +307,26 @@ System.out.println("插入成功的主键: " + row.get("my_id"));
## 如何替换 Ruoyi 项目中的 MyBatis 为 MyBatis-Flex ?
参考 issuehttps://gitee.com/mybatis-flex/mybatis-flex/issues/I7UX96
## MyBatis-Flex 如何 activiti6 以及 Flowable 等工作流引擎集成?
当 MyBatis-Flex 与 activiti6 (或者 Flowable集成时需要覆盖其自动配置添加 mybatis-flex 的事务管理器FlexTransactionManager和 DataSourceFlexDataSource
注入到 ProcessEngineConfiguration配置代码如下
```java
@Bean
public ProcessEngineConfiguration processEngineConfiguration(
SqlSessionFactory sqlSessionFactory,
PlatformTransactionManager annotationDrivenTransactionManager) {
SpringProcessEngineConfiguration processEngineConfiguration = new SpringProcessEngineConfiguration();
// 指定 MyBatis-Flex 数据源
processEngineConfiguration.setDataSource(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource());
// 配置 MyBatis-Flex 的事务管理器
processEngineConfiguration.setTransactionManager(annotationDrivenTransactionManager);
...
}
```

View File

@ -62,7 +62,7 @@ VALUES (1, '张三', 18, '2020-01-11'),
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-spring-boot-starter</artifactId>
<version>1.6.4</version>
<version>1.6.6</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>

View File

@ -10,7 +10,7 @@
```kotlin
dependencies {
implementation("com.mybatis-flex:mybatis-flex-core:1.6.4")
implementation("com.mybatis-flex:mybatis-flex-core:1.6.6")
}
```
@ -18,7 +18,7 @@ dependencies {
```groovy
dependencies {
implementation 'com.mybatis-flex:mybatis-flex-core:1.6.4'
implementation 'com.mybatis-flex:mybatis-flex-core:1.6.6'
}
```
@ -28,7 +28,7 @@ dependencies {
```kotlin
dependencies {
implementation("com.mybatis-flex:mybatis-flex-spring:1.6.4")
implementation("com.mybatis-flex:mybatis-flex-spring:1.6.6")
}
```
@ -36,7 +36,7 @@ dependencies {
```groovy
dependencies {
implementation 'com.mybatis-flex:mybatis-flex-spring:1.6.4'
implementation 'com.mybatis-flex:mybatis-flex-spring:1.6.6'
}
```
@ -46,7 +46,7 @@ dependencies {
```kotlin
dependencies {
implementation("com.mybatis-flex:mybatis-flex-spring-boot-starter:1.6.4")
implementation("com.mybatis-flex:mybatis-flex-spring-boot-starter:1.6.6")
}
```
@ -54,7 +54,7 @@ dependencies {
```groovy
dependencies {
implementation 'com.mybatis-flex:mybatis-flex-spring-boot-starter:1.6.4'
implementation 'com.mybatis-flex:mybatis-flex-spring-boot-starter:1.6.6'
}
```
@ -70,7 +70,7 @@ dependencies {
```kotlin
dependencies {
annotationProcessor("com.mybatis-flex:mybatis-flex-processor:1.6.4")
annotationProcessor("com.mybatis-flex:mybatis-flex-processor:1.6.6")
}
```
@ -78,6 +78,6 @@ dependencies {
```groovy
dependencies {
annotationProcessor 'com.mybatis-flex:mybatis-flex-processor:1.6.4'
annotationProcessor 'com.mybatis-flex:mybatis-flex-processor:1.6.6'
}
```

View File

@ -12,12 +12,12 @@
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-core</artifactId>
<version>1.6.4</version>
<version>1.6.6</version>
</dependency>
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-processor</artifactId>
<version>1.6.4</version>
<version>1.6.6</version>
<scope>provided</scope>
</dependency>
```
@ -28,12 +28,12 @@
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-spring</artifactId>
<version>1.6.4</version>
<version>1.6.6</version>
</dependency>
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-processor</artifactId>
<version>1.6.4</version>
<version>1.6.6</version>
<scope>provided</scope>
</dependency>
``````
@ -44,12 +44,12 @@
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-spring-boot-starter</artifactId>
<version>1.6.4</version>
<version>1.6.6</version>
</dependency>
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-processor</artifactId>
<version>1.6.4</version>
<version>1.6.6</version>
<scope>provided</scope>
</dependency>
```
@ -72,7 +72,7 @@
<path>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-processor</artifactId>
<version>1.6.4</version>
<version>1.6.6</version>
</path>
</annotationProcessorPaths>
</configuration>

View File

@ -1,21 +1,22 @@
| | | | | |
|-----|-----|-----|-----|-----|
|![](https://foruda.gitee.com/avatar/1676898191051702081/61279_fuhai_1578915942.png!avatar60)Michael Yang|![](https://foruda.gitee.com/avatar/1677105717059215385/7984572_suomm_1624454114.png!avatar60)王帅|丌冰|![](https://foruda.gitee.com/avatar/1691044597656855579/7563907_lifejwang11_1691044597.png!avatar60)life|snyk-bot|
|![](https://foruda.gitee.com/avatar/1691636027051962328/11233353_kamo-sama_1691636027.png!avatar60)卡莫sama|lhzsdnu|![](https://foruda.gitee.com/avatar/1683858335519306352/15535_noear_admin_1683858335.png!avatar60)西东|pengpeng|庄佳彬|
|Font_C|笨小孩|CloudPlayer|snow|Jerry|
|草语|Jerry_Zheng|wujl|![](https://foruda.gitee.com/avatar/1676895504036051867/8807_piggsoft_1578914592.jpg!avatar60)piggsoft|![](https://foruda.gitee.com/avatar/1674286432514482953/4807650_fandai_fandaidzsw_1674286432.png!avatar60)赤兮丷|
|![](https://foruda.gitee.com/avatar/1677053740056224121/5462387_i_tell_you_1618064317.png!avatar60)liibang|cainiao3853|黄沐鸿|barql|loong0306|
|yangs|菜鸟3853|![](https://foruda.gitee.com/avatar/1677086127012961929/7598208_robot-l_1590219712.png!avatar60)Robot.L|![](https://foruda.gitee.com/avatar/1677005791814507674/2130728_lemonbx_1622621180.png!avatar60)落羽er|沈君锋|
|![](https://foruda.gitee.com/avatar/1691034002435340221/1920167_qimincow_1691034002.png!avatar60)英雄路|tan90|BQ60ziOxlFI0R0|淡定|qixy|
|font-C|大周|yuanbaolong|zhijieqing|2han9wen71an|
|欢乐码农|![](https://foruda.gitee.com/avatar/1677237805724097193/11485875_bygkn_1660893367.png!avatar60)bygkn|Shark|hans|zhongyong|
|![](https://foruda.gitee.com/avatar/1676978624694631546/1600987_youthdream_1592959590.png!avatar60)锟斤拷|庄佳彬|![](https://foruda.gitee.com/avatar/1674121508509280199/9288653_saoforestt_1674121508.png!avatar60)Saoforest|XiaoLin|dgmico|
|![](https://foruda.gitee.com/avatar/1677111694079591934/8088436_yang-zzu_1604969134.png!avatar60)yang_zzu|![](https://foruda.gitee.com/avatar/1677162544015233775/9094323_lymph_java_1624796992.png!avatar60)Ikko Eltociear Ashimine|![](https://foruda.gitee.com/avatar/1676895416224286260/8331_chaosforever_1578914555.png!avatar60)锁力|chenjh3|![](https://foruda.gitee.com/avatar/1679885039814030308/5151444_yangbuyi_1679885039.png!avatar60)阿志同学|
|![](https://foruda.gitee.com/avatar/1684129987239221781/1731138_toycat93_1684129987.png!avatar60)玩具猫|chenjian835|![](https://foruda.gitee.com/avatar/1676896562075035262/20021_duxlei_1578915302.png!avatar60)duxlei|meng.liu3|yaochen4|
|![](https://foruda.gitee.com/avatar/1676959401839738321/1269497_zhy_balck_1578947791.png!avatar60)zhy_black|![](https://foruda.gitee.com/avatar/1676905453682965545/327218_gm173119755_1648555045.png!avatar60)豌豆粉|![](https://foruda.gitee.com/avatar/1676974596171836113/1532463_1395961821_1578953848.png!avatar60)ζั͡ ั͡ ั͡ ั͡Wm|18007559437|![](https://foruda.gitee.com/avatar/1676894749123859490/2132_hopper_1578914095.jpg!avatar60)陈国正|
|凌尘|luy|gongzhongqiang|乌鸦笑猪黑|EafonYoung|
|Alex|wnp|![](https://foruda.gitee.com/avatar/1676901646505077446/106613_myron_1578917779.png!avatar60)MyronLi|![](https://foruda.gitee.com/avatar/1677170868635098448/9319924_pioneer-sun_1624354686.png!avatar60)Pioneer-Sun|norkts|
|![](https://foruda.gitee.com/avatar/1677166292370951564/9173563_q-alex_1627784508.png!avatar60)Q_Alex|![](https://foruda.gitee.com/avatar/1677052070334379576/5421002_wlf213_1612139033.png!avatar60)wlf|aqnghu|winnerself|![](https://foruda.gitee.com/avatar/1676898238064465096/61541_whitedolphin_1578915956.png!avatar60)CrazyAirhead|
|![](https://foruda.gitee.com/avatar/1677182504887358627/9655223_animal553_1631088642.png!avatar60)她出去赚钱了|XiaoLin|张春根|![](https://foruda.gitee.com/avatar/1693449200752633970/11209107_jl_0417_1693449200.png!avatar60)疾浪|![](https://foruda.gitee.com/avatar/1676983827162237415/1697554_xinjump_1654653784.png!avatar60)xinjump|
|sppan|![](https://foruda.gitee.com/avatar/1662084101462823713/2079235_djxchi_1662084101.png!avatar60)时间淡忘一切|
|![](https://foruda.gitee.com/avatar/1676898191051702081/61279_fuhai_1578915942.png!avatar60)Michael Yang|![](https://foruda.gitee.com/avatar/1677105717059215385/7984572_suomm_1624454114.png!avatar60)王帅|丌冰|![](https://foruda.gitee.com/avatar/1691044597656855579/7563907_lifejwang11_1691044597.png!avatar60)life|![](https://foruda.gitee.com/avatar/1691636027051962328/11233353_kamo-sama_1691636027.png!avatar60)卡莫sama|
|snyk-bot|lhzsdnu|![](https://foruda.gitee.com/avatar/1683858335519306352/15535_noear_admin_1683858335.png!avatar60)西东|pengpeng|庄佳彬|
|Font_C|笨小孩|CloudPlayer|Jerry|snow|
|草语|Ice-samll|菜鸟3853|Jerry_Zheng|wujl|
|![](https://foruda.gitee.com/avatar/1676895504036051867/8807_piggsoft_1578914592.jpg!avatar60)piggsoft|![](https://foruda.gitee.com/avatar/1674286432514482953/4807650_fandai_fandaidzsw_1674286432.png!avatar60)赤兮丷|![](https://foruda.gitee.com/avatar/1677053740056224121/5462387_i_tell_you_1618064317.png!avatar60)liibang|黄沐鸿|cainiao3853|
|loong0306|yangs|barql|![](https://foruda.gitee.com/avatar/1677086127012961929/7598208_robot-l_1590219712.png!avatar60)Robot.L|tangxin|
|![](https://foruda.gitee.com/avatar/1677005791814507674/2130728_lemonbx_1622621180.png!avatar60)落羽er|沈君锋|![](https://foruda.gitee.com/avatar/1691034002435340221/1920167_qimincow_1691034002.png!avatar60)英雄路|aqnghu|tan90|
|BQ60ziOxlFI0R0|淡定|qixy|font-C|大周|
|yuanbaolong|zhijieqing|2han9wen71an|![](https://foruda.gitee.com/avatar/1677237805724097193/11485875_bygkn_1660893367.png!avatar60)bygkn|欢乐码农|
|hans|zhongyong|![](https://foruda.gitee.com/avatar/1676978624694631546/1600987_youthdream_1592959590.png!avatar60)锟斤拷|庄佳彬|![](https://foruda.gitee.com/avatar/1674121508509280199/9288653_saoforestt_1674121508.png!avatar60)Saoforest|
|![](https://foruda.gitee.com/avatar/1677111694079591934/8088436_yang-zzu_1604969134.png!avatar60)yang_zzu|dgmico|XiaoLin|Shark|![](https://foruda.gitee.com/avatar/1677162544015233775/9094323_lymph_java_1624796992.png!avatar60)Ikko Eltociear Ashimine|
|![](https://foruda.gitee.com/avatar/1678377314939642686/1604115_handy-git_1678377314.png!avatar60)handy|![](https://foruda.gitee.com/avatar/1676895416224286260/8331_chaosforever_1578914555.png!avatar60)锁力|chenjh3|![](https://foruda.gitee.com/avatar/1691737477130376308/1673084_wang_yong_ji_1691737477.png!avatar60)老吉丶|![](https://foruda.gitee.com/avatar/1684129987239221781/1731138_toycat93_1684129987.png!avatar60)玩具猫|
|chenjian835|![](https://foruda.gitee.com/avatar/1679885039814030308/5151444_yangbuyi_1679885039.png!avatar60)阿志同学|![](https://foruda.gitee.com/avatar/1676896562075035262/20021_duxlei_1578915302.png!avatar60)duxlei|meng.liu3|yaochen4|
|![](https://foruda.gitee.com/avatar/1676959401839738321/1269497_zhy_balck_1578947791.png!avatar60)zhy_black|![](https://foruda.gitee.com/avatar/1676905453682965545/327218_gm173119755_1648555045.png!avatar60)豌豆粉|![](https://foruda.gitee.com/avatar/1676974596171836113/1532463_1395961821_1578953848.png!avatar60)ζั͡ ั͡ ั͡ ั͡Wm|matthew|![](https://foruda.gitee.com/avatar/1676894749123859490/2132_hopper_1578914095.jpg!avatar60)陈国正|
|![](https://foruda.gitee.com/avatar/1691805683099463215/8904907_zhuhjay_1691805683.png!avatar60)ZhuHJay|凌尘|luy|gongzhongqiang|![](https://foruda.gitee.com/avatar/1677071665480088881/6561865_zoufang162_1585144118.png!avatar60)zoufang162|
|Alex|EafonYoung|乌鸦笑猪黑|wnp|![](https://foruda.gitee.com/avatar/1676901646505077446/106613_myron_1578917779.png!avatar60)MyronLi|
|![](https://foruda.gitee.com/avatar/1677170868635098448/9319924_pioneer-sun_1624354686.png!avatar60)Pioneer-Sun|norkts|![](https://foruda.gitee.com/avatar/1677166292370951564/9173563_q-alex_1627784508.png!avatar60)Q_Alex|拓宇在思考|![](https://foruda.gitee.com/avatar/1677052070334379576/5421002_wlf213_1612139033.png!avatar60)wlf|
|winnerself|![](https://foruda.gitee.com/avatar/1676898238064465096/61541_whitedolphin_1578915956.png!avatar60)CrazyAirhead|![](https://foruda.gitee.com/avatar/1677182504887358627/9655223_animal553_1631088642.png!avatar60)她出去赚钱了|张春根|XiaoLin|
|![](https://foruda.gitee.com/avatar/1693449200752633970/11209107_jl_0417_1693449200.png!avatar60)疾浪|![](https://foruda.gitee.com/avatar/1676983827162237415/1697554_xinjump_1654653784.png!avatar60)xinjump|sppan|![](https://foruda.gitee.com/avatar/1662084101462823713/2079235_djxchi_1662084101.png!avatar60)时间淡忘一切|

View File

@ -1,8 +1,15 @@
# 在 Kotlin 中使用 Mybatis-Flex
> Mybatis-Flex-Kotlin 是一个 [Mybatis-Flex](https://mybatis-flex.com) 框架的扩展模块,
> 它继承了 Mybatis-Flex 轻量的特性,同时拥有 Kotlin 特有的扩展方法、中缀表达式与DSL等语法支持
> 使其拥有了更高的灵活性。让我们可以更加轻松的在 Kotlin 中使用 Mybaits-Flex 所带来的开发效率和开发体验。
**MyBatis-Flex-Kotlin 基于 Mybatis-Flex 的 Kotlin 扩展模块,方便 Kotlin 开发者使用 MyBatis-Flex 进行开发。**
>它继承了 Mybatis-Flex 轻量的特性,同时拥有 Kotlin 特有的扩展方法、中缀表达式与DSL等语法支持
>使其拥有了更高的灵活性。让我们可以更加轻松的在 Kotlin 中使用 Mybaits-Flex 所带来的开发效率和开发体验。
Git 地址https://github.com/KAMO030/MyBatis-Flex-Kotlin
## 特征
@ -10,6 +17,11 @@
- 简明:使用 DSL 让查询语句更加简单明了
- 快捷:结合 Kotlin 特性快速对数据库进行操作
## 快速开始
点击链接进入详情:
https://github.com/KAMO030/MyBatis-Flex-Kotlin#%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B
## 亮点
- 快速构建启动通过DSL重载运算符快速配置 MybatisFlexBootstrap 实例并启动:
@ -29,23 +41,26 @@
>- `query<Account>(queryScope: QueryScope.()->Unit)` 较复杂查泛型对应的表的数据 (如分组,排序等)
- 简明地构建条件:通过中缀表达式➕扩展方法能更加简单明了的构建条件:
* **【原生方式】**
```kotlin
val queryWrapper = QueryWrapper.create()
.select(Account::id.column(), Account::userName.column())
.where(Account::age.column().`in`(17, 18, 19))
.orderBy(Account::id.column().desc())
mapper<AccountMapper>().selectListByQuery(queryWrapper)
```
* **【原生方式】**
```kotlin
val queryWrapper = QueryWrapper.create()
.select(Account::id.column(), Account::userName.column())
.where(Account::age.column().`in`(17, 18, 19))
.orderBy(Account::id.column().desc())
mapper<AccountMapper>().selectListByQuery(queryWrapper)
```
* **【扩展方式】**
```kotlin
query<Account> {
select { listOf(Account::id, Account::userName) }
* **【扩展方式】**
```kotlin
query<Account> {
select(Account::id, Account::userName)
where { Account::age `in` (17..19) } orderBy -Account::id
}
```
}
```
> 执行的SQL:
```sql
SELECT `id`, `user_name` FROM `tb_account` WHERE `age` IN (17, 18, 19) ORDER BY `id` DESC
```
- 摆脱APT: 使用扩展方法摆脱对 APT(注解处理器) 的使用,直接使用属性引用让代码更加灵活优雅:
> 使用APT: `ACCOUNT.ID eq 1` ,使用属性引用: `Account::id eq 1`
>

View File

@ -220,7 +220,7 @@ pom.xml 添加 `annotationProcessorPaths` 配置,
```
dependencies {
...
annotationProcessor 'com.mybatis-flex:mybatis-flex-processor:1.6.4'
annotationProcessor 'com.mybatis-flex:mybatis-flex-processor:1.6.6'
}
```

View File

@ -10,7 +10,7 @@
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-codegen</artifactId>
<version>1.6.4</version>
<version>1.6.6</version>
</dependency>
```

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.6.4</version>
<version>1.6.6</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -67,6 +67,14 @@ public @interface RelationManyToMany {
*/
String targetField() default "";
/**
* 目标对象的关系实体类的属性绑定
* <p>
* 当字段不为空串时,只进行某个字段赋值(使用对应字段类型接收)
* @return 属性名称
*/
String valueField() default "";
/**
* 当映射是一个 map 使用哪个内容来当做 map Key
* @return 指定的列

View File

@ -67,6 +67,14 @@ public @interface RelationManyToOne {
*/
String targetField() default "";
/**
* 目标对象的关系实体类的属性绑定
* <p>
* 当字段不为空串时,只进行某个字段赋值(使用对应字段类型接收)
* @return 属性名称
*/
String valueField() default "";
/**
* 中间表名称一对一的关系是通过通过中间表维护时需要添加此项配置
*

View File

@ -67,6 +67,14 @@ public @interface RelationOneToMany {
*/
String targetField();
/**
* 目标对象的关系实体类的属性绑定
* <p>
* 当字段不为空串时,只进行某个字段赋值(使用对应字段类型接收)
* @return 属性名称
*/
String valueField() default "";
/**
* 当映射是一个 map 使用哪个内容来当做 map Key
* @return 指定的列

View File

@ -67,6 +67,14 @@ public @interface RelationOneToOne {
*/
String targetField();
/**
* 目标对象的关系实体类的属性绑定
* <p>
* 当字段不为空串时,只进行某个字段赋值(使用对应字段类型接收)
* @return 属性名称
*/
String valueField() default "";
/**
* 中间表名称一对一的关系是通过通过中间表维护时需要添加此项配置
*

View File

@ -24,7 +24,7 @@ import java.lang.annotation.*;
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Inherited
//@Inherited 需要注释否则会在 vo 等继承 model 的实体类中生成多余的或冲突的 tableDef
public @interface Table {
/**

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.6.4</version>
<version>1.6.6</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -15,6 +15,8 @@
*/
package com.mybatisflex.codegen.config;
import java.io.Serializable;
/**
* 生成 Controller 的配置
*
@ -22,8 +24,9 @@ package com.mybatisflex.codegen.config;
* @since 2023-05-15
*/
@SuppressWarnings("unused")
public class ControllerConfig {
public class ControllerConfig implements Serializable {
private static final long serialVersionUID = 8391630904705910611L;
/**
* Controller 类的前缀
*/

View File

@ -24,8 +24,9 @@ import java.io.Serializable;
* @since 2023-05-15
*/
@SuppressWarnings("unused")
public class EntityConfig {
public class EntityConfig implements Serializable {
private static final long serialVersionUID = -6790274333595436008L;
/**
* Entity 类的前缀
*/

View File

@ -17,6 +17,7 @@ package com.mybatisflex.codegen.config;
import com.mybatisflex.codegen.template.ITemplate;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@ -32,7 +33,8 @@ import java.util.function.UnaryOperator;
* @since 2023-05-15
*/
@SuppressWarnings("unused")
public class GlobalConfig {
public class GlobalConfig implements Serializable {
private static final long serialVersionUID = 5033600623041298000L;
// === 必须配置 ===

View File

@ -18,6 +18,7 @@ package com.mybatisflex.codegen.config;
import com.mybatisflex.core.util.StringUtil;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.function.Function;
@ -31,8 +32,9 @@ import java.util.function.UnaryOperator;
* @since 2023-05-17
*/
@SuppressWarnings("unused")
public class JavadocConfig {
public class JavadocConfig implements Serializable {
private static final long serialVersionUID = -4280345489968397327L;
/**
* 作者
*/

View File

@ -17,6 +17,8 @@ package com.mybatisflex.codegen.config;
import com.mybatisflex.core.BaseMapper;
import java.io.Serializable;
/**
* 生成 Mapper 的配置
*
@ -24,8 +26,9 @@ import com.mybatisflex.core.BaseMapper;
* @since 2023-05-15
*/
@SuppressWarnings("unused")
public class MapperConfig {
public class MapperConfig implements Serializable {
private static final long serialVersionUID = 1937442008907641534L;
/**
* Mapper 类的前缀
*/

View File

@ -16,6 +16,8 @@
package com.mybatisflex.codegen.config;
import java.io.Serializable;
/**
* 生成 MapperXml 的配置
*
@ -23,8 +25,9 @@ package com.mybatisflex.codegen.config;
* @since 2023-05-17
*/
@SuppressWarnings("unused")
public class MapperXmlConfig {
public class MapperXmlConfig implements Serializable {
private static final long serialVersionUID = 7836897652282634412L;
/**
* MapperXml 文件的前缀
*/

View File

@ -18,6 +18,8 @@ package com.mybatisflex.codegen.config;
import com.mybatisflex.core.util.StringUtil;
import java.io.Serializable;
/**
* 生成软件包的配置
*
@ -25,8 +27,9 @@ import com.mybatisflex.core.util.StringUtil;
* @since 2023-05-15
*/
@SuppressWarnings("unused")
public class PackageConfig {
public class PackageConfig implements Serializable {
private static final long serialVersionUID = -8257632247633439537L;
/**
* 代码生成目录
*/

View File

@ -18,6 +18,8 @@ package com.mybatisflex.codegen.config;
import com.mybatisflex.core.service.IService;
import java.io.Serializable;
/**
* 生成 Service 的配置
*
@ -25,8 +27,9 @@ import com.mybatisflex.core.service.IService;
* @since 2023-05-15
*/
@SuppressWarnings("unused")
public class ServiceConfig {
public class ServiceConfig implements Serializable {
private static final long serialVersionUID = -2152473328300910220L;
/**
* Service 类的前缀
*/

View File

@ -15,6 +15,8 @@
*/
package com.mybatisflex.codegen.config;
import java.io.Serializable;
/**
* 生成 ServiceImpl 的配置
*
@ -22,8 +24,9 @@ package com.mybatisflex.codegen.config;
* @since 2023-05-15
*/
@SuppressWarnings("unused")
public class ServiceImplConfig {
public class ServiceImplConfig implements Serializable {
private static final long serialVersionUID = 17115432462168151L;
/**
* ServiceImpl 类的前缀
*/

View File

@ -18,6 +18,7 @@ package com.mybatisflex.codegen.config;
import com.mybatisflex.core.util.StringUtil;
import java.io.Serializable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@ -30,8 +31,9 @@ import java.util.Set;
* @since 2023-05-14
*/
@SuppressWarnings("unused")
public class StrategyConfig {
public class StrategyConfig implements Serializable {
private static final long serialVersionUID = 504853587703061034L;
/**
* 数据库表前缀多个前缀用英文逗号, 隔开
*/

View File

@ -19,6 +19,7 @@ import com.mybatisflex.annotation.InsertListener;
import com.mybatisflex.annotation.SetListener;
import com.mybatisflex.annotation.UpdateListener;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
@ -26,9 +27,10 @@ import java.util.Map;
* 表的单独设置
*/
@SuppressWarnings({"unused", "UnusedReturnValue"})
public class TableConfig {
public class TableConfig implements Serializable {
public static final String ALL_TABLES = "*";
private static final long serialVersionUID = -2568968178699265858L;
/**
* 数据库的 schema模式

View File

@ -17,6 +17,8 @@ package com.mybatisflex.codegen.config;
import com.mybatisflex.core.util.StringUtil;
import java.io.Serializable;
/**
* 生成 TableDef 的配置
*
@ -24,8 +26,9 @@ import com.mybatisflex.core.util.StringUtil;
* @since 2023-05-15
*/
@SuppressWarnings("unused")
public class TableDefConfig {
public class TableDefConfig implements Serializable {
private static final long serialVersionUID = 8137903163796008036L;
/**
* TableDef 类的前缀
*/

View File

@ -20,6 +20,8 @@ import com.mybatisflex.codegen.generator.GeneratorFactory;
import com.mybatisflex.codegen.template.ITemplate;
import com.mybatisflex.codegen.template.impl.EnjoyTemplate;
import java.io.Serializable;
/**
* 模板配置
*
@ -27,8 +29,9 @@ import com.mybatisflex.codegen.template.impl.EnjoyTemplate;
* @since 2023-05-17
*/
@SuppressWarnings("unused")
public class TemplateConfig {
public class TemplateConfig implements Serializable {
private static final long serialVersionUID = 6700855804948021101L;
/**
* 生成代码的模板引擎
*/

View File

@ -0,0 +1,152 @@
#set(tableComment = table.getComment())
#set(entityClassName = table.buildEntityClassName())
#set(entityVarName = firstCharToLowerCase(entityClassName))
#set(serviceVarName = firstCharToLowerCase(table.buildServiceClassName()))
package #(packageConfig.controllerPackage);
import com.mybatisflex.core.paginate.Page;
import org.noear.solon.annotation.*;
import #(packageConfig.entityPackage).#(entityClassName);
import #(packageConfig.servicePackage).#(table.buildServiceClassName());
#if(controllerConfig.superClass != null)
import #(controllerConfig.buildSuperClassImport());
#end
#if(withSwagger && swaggerVersion.getName() == "FOX")
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
#end
#if(withSwagger && swaggerVersion.getName() == "DOC")
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
#end
import java.util.List;
/**
* #(tableComment) 控制层。
*
* @author #(javadocConfig.getAuthor())
* @since #(javadocConfig.getSince())
*/
@Controller
#if(withSwagger && swaggerVersion.getName() == "FOX")
@Api("#(tableComment)接口")
#end
#if(withSwagger && swaggerVersion.getName() == "DOC")
@Tag(name = "#(tableComment)接口")
#end
@Mapping("/#(firstCharToLowerCase(entityClassName))")
public class #(table.buildControllerClassName()) #if(controllerConfig.superClass)extends #(controllerConfig.buildSuperClassName()) #end {
@Inject
private #(table.buildServiceClassName()) #(serviceVarName);
/**
* 添加#(tableComment)。
*
* @param #(entityVarName) #(tableComment)
* @return {@code true} 添加成功,{@code false} 添加失败
*/
@Post
@Mapping("save")
#if(withSwagger && swaggerVersion.getName() == "FOX")
@ApiOperation("保存#(tableComment)")
#end
#if(withSwagger && swaggerVersion.getName() == "DOC")
@Operation(description="保存#(tableComment)")
#end
public boolean save(@Body #if(withSwagger && swaggerVersion.getName() == "FOX")@ApiParam("#(tableComment)") #end #if(withSwagger && swaggerVersion.getName() == "DOC")@Parameter(description="#(tableComment)")#end #(entityClassName) #(entityVarName)) {
return #(serviceVarName).save(#(entityVarName));
}
/**
* 根据主键删除#(tableComment)。
*
* @param id 主键
* @return {@code true} 删除成功,{@code false} 删除失败
*/
@Delete
@Mapping("remove/{id}")
#if(withSwagger && swaggerVersion.getName() == "FOX")
@ApiOperation("根据主键#(tableComment)")
#end
#if(withSwagger && swaggerVersion.getName() == "DOC")
@Operation(description="根据主键#(tableComment)")
#end
public boolean remove(@Path #if(withSwagger && swaggerVersion.getName() == "FOX")@ApiParam("#(tableComment)主键") #end #if(withSwagger && swaggerVersion.getName() == "DOC")@Parameter(description="#(tableComment)主键")#end Long id) {
return #(serviceVarName).removeById(id);
}
/**
* 根据主键更新#(tableComment)。
*
* @param #(entityVarName) #(tableComment)
* @return {@code true} 更新成功,{@code false} 更新失败
*/
@Put
@Mapping("update")
#if(withSwagger && swaggerVersion.getName() == "FOX")
@ApiOperation("根据主键更新#(tableComment)")
#end
#if(withSwagger && swaggerVersion.getName() == "DOC")
@Operation(description="根据主键更新#(tableComment)")
#end
public boolean update(@Body #if(withSwagger && swaggerVersion.getName() == "FOX")@ApiParam("#(tableComment)主键") #end #if(withSwagger && swaggerVersion.getName() == "DOC")@Parameter(description="#(tableComment)主键")#end#(entityClassName) #(entityVarName)) {
return #(serviceVarName).updateById(#(entityVarName));
}
/**
* 查询所有#(tableComment)。
*
* @return 所有数据
*/
@Get
@Mapping("list")
#if(withSwagger && swaggerVersion.getName() == "FOX")
@ApiOperation("查询所有#(tableComment)")
#end
#if(withSwagger && swaggerVersion.getName() == "DOC")
@Operation(description="查询所有#(tableComment)")
#end
public List<#(entityClassName)> list() {
return #(serviceVarName).list();
}
/**
* 根据#(tableComment)主键获取详细信息。
*
* @param id #(tableComment)主键
* @return #(tableComment)详情
*/
@Get
@Mapping("getInfo/{id}")
#if(withSwagger && swaggerVersion.getName() == "FOX")
@ApiOperation("根据主键获取#(tableComment)")
#end
#if(withSwagger && swaggerVersion.getName() == "DOC")
@Operation(description="根据主键获取#(tableComment)")
#end
public #(entityClassName) getInfo(@Path #if(withSwagger && swaggerVersion.getName() == "FOX")@ApiParam("#(tableComment)主键") #if(withSwagger && swaggerVersion.getName() == "DOC")@Parameter(description="#(tableComment)主键")#end#end Long id) {
return #(serviceVarName).getById(id);
}
/**
* 分页查询#(tableComment)。
*
* @param page 分页对象
* @return 分页对象
*/
@Get
@Mapping("page")
#if(withSwagger && swaggerVersion.getName() == "FOX")
@ApiOperation("分页查询#(tableComment)")
#end
#if(withSwagger && swaggerVersion.getName() == "DOC")
@Operation(description="分页查询#(tableComment)")
#end
public Page<#(entityClassName)> page(#if(withSwagger && swaggerVersion.getName() == "FOX")@ApiParam("分页信息") #end #if(withSwagger && swaggerVersion.getName() == "DOC")@Parameter(description="分页信息")#end Page<#(entityClassName)> page) {
return #(serviceVarName).page(page);
}
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.6.4</version>
<version>1.6.6</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -27,7 +27,7 @@ public class FlexConsts {
}
public static final String NAME = "MyBatis-Flex";
public static final String VERSION = "1.6.4";
public static final String VERSION = "1.6.6";
public static final String SQL = "$$sql";

View File

@ -15,6 +15,7 @@
*/
package com.mybatisflex.core.datasource;
import java.lang.reflect.Method;
import java.util.function.Supplier;
/**
@ -32,8 +33,6 @@ public class DataSourceKey {
*/
private static ThreadLocal<String> manualKeyThreadLocal = new ThreadLocal<>();
public static String manualKey;
private DataSourceKey() {
}
@ -88,4 +87,9 @@ public class DataSourceKey {
public static void setManualKeyThreadLocal(ThreadLocal<String> manualKeyThreadLocal) {
DataSourceKey.manualKeyThreadLocal = manualKeyThreadLocal;
}
public static String getByShardingStrategy(String dataSource, Object mapper, Method method, Object[] args) {
String shardingDsKey = DataSourceManager.getByShardingStrategy(dataSource, mapper, method, args);
return shardingDsKey != null ? shardingDsKey : dataSource;
}
}

View File

@ -37,6 +37,15 @@ public class DataSourceManager {
DataSourceManager.decipher = decipher;
}
private static DataSourceShardingStrategy dataSourceShardingStrategy;
public static DataSourceShardingStrategy getDataSourceShardingStrategy() {
return dataSourceShardingStrategy;
}
public static void setDataSourceShardingStrategy(DataSourceShardingStrategy dataSourceShardingStrategy) {
DataSourceManager.dataSourceShardingStrategy = dataSourceShardingStrategy;
}
public static void decryptDataSource(DataSource dataSource) {
if (decipher == null) {
@ -87,4 +96,7 @@ public class DataSourceManager {
}
static String getByShardingStrategy(String dataSource, Object mapper, Method method, Object[] args) {
return dataSourceShardingStrategy != null ? dataSourceShardingStrategy.doSharding(dataSource, mapper, method, args) : null;
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mybatisflex.core.datasource;
import java.lang.reflect.Method;
public interface DataSourceShardingStrategy {
String doSharding(String currentDataSourceKey, Object mapper, Method mapperMethod, Object[] methodArgs);
}

View File

@ -29,6 +29,8 @@ public interface IDialect {
String wrap(String keyword);
String wrapColumnAlias(String keyword);
default String getRealTable(String table) {
return TableManager.getRealTable(table);
}

View File

@ -62,6 +62,10 @@ public class CommonsDialectImpl implements IDialect {
return ASTERISK.equals(keyword) ? keyword : keywordWrap.wrap(keyword);
}
@Override
public String wrapColumnAlias(String keyword) {
return ASTERISK.equals(keyword) ? keyword : keywordWrap.getPrefix() + keyword + keywordWrap.getSuffix();
}
@Override
public String forHint(String hintString) {
@ -865,7 +869,6 @@ public class CommonsDialectImpl implements IDialect {
}
@Override
public String forSelectOneEntityById(TableInfo tableInfo) {
StringBuilder sql = new StringBuilder();

View File

@ -15,6 +15,7 @@
*/
package com.mybatisflex.core.mybatis;
import com.mybatisflex.annotation.Table;
import com.mybatisflex.core.FlexConsts;
import com.mybatisflex.core.handler.CompositeEnumTypeHandler;
import com.mybatisflex.core.keygen.MultiEntityKeyGenerator;
@ -48,11 +49,13 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author michael
* @author life
*/
public class FlexConfiguration extends Configuration {
@ -177,8 +180,19 @@ public class FlexConfiguration extends Configuration {
else if (StringUtil.endsWithAny(ms.getId(), "selectOneById", "selectListByIds"
, "selectListByQuery", "selectCursorByQuery")) {
ms = replaceResultMap(ms, getTableInfo(ms));
} else {
List<ResultMap> resultMaps = ms.getResultMaps();
//根据 resultMap 里面的 class 进行判断
for (ResultMap resultMap : resultMaps) {
//获取结果的类型
Class<?> clazz = resultMap.getType();
//判断是否为表实体类
if (clazz.getDeclaredAnnotation(Table.class) != null) {
TableInfo tableInfo = TableInfoFactory.ofEntityClass(clazz);
ms = replaceResultMap(ms, tableInfo);
}
}
}
super.addMappedStatement(ms);
}

View File

@ -80,7 +80,7 @@ public class FlexWrapperFactory implements ObjectWrapperFactory {
@Override
public String findProperty(String name, boolean useCamelCaseMapping) {
return useCamelCaseMapping && name.contains("_") ? StringUtil.underlineToCamel(name) : name;
return useCamelCaseMapping && ( Character.isUpperCase(name.charAt(0)) || name.contains("_")) ? StringUtil.underlineToCamel(name) : name;
}
}

View File

@ -67,6 +67,13 @@ public class MapperInvocationHandler implements InvocationHandler {
}
}
//最终通过数据源 自定义分片 策略去获取
String shardingDataSourceKey = DataSourceKey.getByShardingStrategy(dataSourceKey, proxy, method, args);
if (shardingDataSourceKey != null && !shardingDataSourceKey.equals(dataSourceKey)) {
DataSourceKey.use(dataSourceKey);
needClearDsKey = true;
}
//优先获取用户自己配置的 dbType
DbType dbType = DialectFactory.getHintDbType();
if (dbType == null) {

View File

@ -39,7 +39,7 @@ public class DistinctQueryColumn extends QueryColumn {
String sql = SqlConsts.DISTINCT + StringUtil.join(SqlConsts.DELIMITER, queryColumns, queryColumn ->
queryColumn.toSelectSql(queryTables, dialect));
return sql + WrapperUtil.buildAlias(alias, dialect);
return sql + WrapperUtil.buildColumnAlias(alias, dialect);
}

View File

@ -68,7 +68,7 @@ public class OperatorQueryCondition extends QueryCondition {
@Override
public Object getValue() {
return WrapperUtil.getValues(childCondition);
return checkEffective() ? WrapperUtil.getValues(childCondition) : null;
}
@Override

View File

@ -71,7 +71,7 @@ public class OperatorSelectCondition extends QueryCondition {
@Override
public Object getValue() {
return queryWrapper.getAllValueArray();
return checkEffective() ? queryWrapper.getAllValueArray() : null;
}
@Override

View File

@ -946,7 +946,7 @@ public class QueryColumn implements CloneSupport<QueryColumn>, Conditional<Query
String toSelectSql(List<QueryTable> queryTables, IDialect dialect) {
return toConditionSql(queryTables, dialect) + WrapperUtil.buildAlias(alias, dialect);
return toConditionSql(queryTables, dialect) + WrapperUtil.buildColumnAlias(alias, dialect);
}

View File

@ -2406,8 +2406,8 @@ public class QueryMethods {
/**
* 构建自定义列
*/
public static QueryColumn column(String column) {
return new RawQueryColumn(column);
public static QueryColumn column(String column, Object... params) {
return new RawQueryColumn(column, params);
}
/**

View File

@ -118,6 +118,15 @@ public class QueryWrapper extends BaseQueryWrapper<QueryWrapper> {
return this;
}
public QueryWrapper select(Iterable<QueryColumn> queryColumns) {
for (QueryColumn column : queryColumns) {
if (column != null) {
addSelectColumn(column);
}
}
return this;
}
public QueryWrapper select(QueryColumn[]... queryColumns) {
for (QueryColumn[] columnArray : queryColumns) {
if (columnArray != null) {

View File

@ -77,6 +77,12 @@ public class QueryWrapperAdapter<R extends QueryWrapperAdapter<R>> extends Query
return (R) this;
}
@Override
public R select(Iterable<QueryColumn> queryColumns) {
super.select(queryColumns);
return (R) this;
}
@Override
public R select(QueryColumn[]... queryColumns) {
super.select(queryColumns);

View File

@ -18,18 +18,21 @@ package com.mybatisflex.core.query;
import com.mybatisflex.core.dialect.IDialect;
import java.util.Arrays;
import java.util.List;
/**
* 自定义字符串列用于扩展
*/
public class RawQueryColumn extends QueryColumn {
public class RawQueryColumn extends QueryColumn implements HasParamsColumn {
protected String content;
protected Object[] params;
public RawQueryColumn(Object content) {
public RawQueryColumn(Object content, Object... params) {
this.content = String.valueOf(content);
this.params = params;
}
@Override
@ -39,13 +42,14 @@ public class RawQueryColumn extends QueryColumn {
@Override
String toSelectSql(List<QueryTable> queryTables, IDialect dialect) {
return content + WrapperUtil.buildAlias(alias, dialect);
return content + WrapperUtil.buildColumnAlias(alias, dialect);
}
@Override
public String toString() {
return "RawQueryColumn{" +
"content='" + content + '\'' +
", params='" + Arrays.toString(params) + '\'' +
'}';
}
@ -54,4 +58,9 @@ public class RawQueryColumn extends QueryColumn {
return (RawQueryColumn) super.clone();
}
@Override
public Object[] getParamValues() {
return params;
}
}

View File

@ -122,7 +122,7 @@ class WrapperUtil {
if (enumWrapper.hasEnumValueAnnotation()) {
paras.add(enumWrapper.getEnumValue((Enum) value));
} else {
paras.add(((Enum<?>)value).name());
paras.add(((Enum<?>) value).name());
}
} else {
paras.add(value);
@ -148,13 +148,17 @@ class WrapperUtil {
}
static String withAlias(String sql, String alias, IDialect dialect) {
return SqlConsts.BRACKET_LEFT + sql + SqlConsts.BRACKET_RIGHT + getAsKeyWord(dialect) + dialect.wrap(alias);
return SqlConsts.BRACKET_LEFT + sql + SqlConsts.BRACKET_RIGHT + buildColumnAlias(alias, dialect);
}
static String buildAlias(String alias, IDialect dialect) {
return StringUtil.isBlank(alias) ? SqlConsts.EMPTY : getAsKeyWord(dialect) + dialect.wrap(alias);
}
static String buildColumnAlias(String alias, IDialect dialect) {
return StringUtil.isBlank(alias) ? SqlConsts.EMPTY : getAsKeyWord(dialect) + dialect.wrapColumnAlias(alias);
}
private static String getAsKeyWord(IDialect dialect) {
return dialect instanceof OracleDialect ? SqlConsts.BLANK : SqlConsts.AS;
}

View File

@ -46,6 +46,10 @@ abstract class AbstractRelation<SelfEntity> {
protected String targetSchema;
protected String targetTable;
protected Field targetField;
protected String valueField;
protected boolean onlyQueryValueField;
protected Class<?> targetEntityClass;
protected TableInfo targetTableInfo;
protected FieldWrapper targetFieldWrapper;
@ -62,7 +66,7 @@ abstract class AbstractRelation<SelfEntity> {
protected QueryColumn conditionColumn;
protected String[] selectColumns;
public AbstractRelation(String selfField, String targetSchema, String targetTable, String targetField,
public AbstractRelation(String selfField, String targetSchema, String targetTable, String targetField, String valueField,
String joinTable, String joinSelfColumn, String joinTargetColumn,
String dataSource, Class<SelfEntity> entityClass, Field relationField,
String extraCondition, String[] selectColumns
@ -82,25 +86,34 @@ abstract class AbstractRelation<SelfEntity> {
this.selfField = ClassUtil.getFirstField(entityClass, field -> field.getName().equals(selfField));
this.selfFieldWrapper = FieldWrapper.of(entityClass, selfField);
this.targetEntityClass = relationFieldWrapper.getMappingType();
//以使用者注解配置为主
this.targetTableInfo = StringUtil.isBlank(targetTable) ? TableInfoFactory.ofEntityClass(relationFieldWrapper.getMappingType()) : TableInfoFactory.ofTableName(targetTable);
this.targetEntityClass = targetTableInfo != null ? targetTableInfo.getEntityClass() : relationFieldWrapper.getMappingType();
this.targetSchema = targetSchema;
this.targetTable = targetTable;
this.targetTable = targetTableInfo != null ? targetTableInfo.getTableName() : targetTable;
this.targetField = ClassUtil.getFirstField(targetEntityClass, field -> field.getName().equals(targetField));
this.targetFieldWrapper = FieldWrapper.of(targetEntityClass, targetField);
this.targetTableInfo = TableInfoFactory.ofEntityClass(targetEntityClass);
this.valueField = valueField;
this.onlyQueryValueField = StringUtil.isNotBlank(valueField);
this.conditionColumn = column(targetTable, targetTableInfo.getColumnByProperty(this.targetField.getName()));
this.conditionColumn = targetTableInfo == null ? column(targetTable, StringUtil.camelToUnderline(this.targetField.getName()))
: column(targetTable, targetTableInfo.getColumnByProperty(this.targetField.getName()));
if (ArrayUtil.isNotEmpty(selectColumns)) {
if (ArrayUtil.contains(selectColumns, conditionColumn.getName())) {
this.selectColumns = selectColumns;
} else {
//需要追加 conditionColumn因为进行内存 join 的时候需要用到这个内容进行对比
this.selectColumns = ArrayUtil.concat(selectColumns, new String[]{conditionColumn.getName()});
if (onlyQueryValueField) {
//仅绑定字段时只需要查询关联列和该字段列即可
this.selectColumns = new String[]{conditionColumn.getName(), targetTableInfo != null ? targetTableInfo.getColumnByProperty(this.valueField) : StringUtil.camelToUnderline(this.valueField)};
} else {
if (ArrayUtil.isNotEmpty(selectColumns)) {
if (ArrayUtil.contains(selectColumns, conditionColumn.getName())) {
this.selectColumns = selectColumns;
} else {
//需要追加 conditionColumn因为进行内存 join 的时候需要用到这个内容进行对比
this.selectColumns = ArrayUtil.concat(selectColumns, new String[]{conditionColumn.getName()});
}
}
}
initExtraCondition(extraCondition);
@ -249,6 +262,22 @@ abstract class AbstractRelation<SelfEntity> {
this.targetTable = targetTable;
}
public String getValueField() {
return valueField;
}
public void setValueField(String valueField) {
this.valueField = valueField;
}
public boolean isOnlyQueryValueField() {
return onlyQueryValueField;
}
public void setOnlyQueryValueField(boolean onlyQueryValueField) {
this.onlyQueryValueField = onlyQueryValueField;
}
public String getJoinTable() {
return joinTable;
}

View File

@ -26,6 +26,7 @@ class ManyToMany<SelfEntity> extends ToManyRelation<SelfEntity> {
, annotation.targetSchema()
, annotation.targetTable()
, getDefaultPrimaryProperty(annotation.targetField(), getTargetEntityClass(entityClass, relationField), "@RelationManyToMany.targetField can not be empty in field: \"" + entityClass.getName() + "." + relationField.getName() + "\"")
, annotation.valueField()
, annotation.joinTable()
, annotation.joinSelfColumn()
, annotation.joinTargetColumn()

View File

@ -27,6 +27,7 @@ class ManyToOne<SelfEntity> extends ToOneRelation<SelfEntity> {
, annotation.targetTable()
, getDefaultPrimaryProperty(annotation.targetField(), getTargetEntityClass(entityClass, relationField)
, "@RelationManyToOne.selfField can not be empty in field: \"" + entityClass.getName() + "." + relationField.getName() + "\"")
, annotation.valueField()
, annotation.joinTable()
, annotation.joinSelfColumn()
, annotation.joinTargetColumn()

View File

@ -27,6 +27,7 @@ class OneToMany<SelfEntity> extends ToManyRelation<SelfEntity> {
, annotation.targetSchema()
, annotation.targetTable()
, annotation.targetField()
, annotation.valueField()
, annotation.joinTable()
, annotation.joinSelfColumn()
, annotation.joinTargetColumn()

View File

@ -27,6 +27,7 @@ class OneToOne<SelfEntity> extends ToOneRelation<SelfEntity> {
, annotation.targetSchema()
, annotation.targetTable()
, annotation.targetField()
, annotation.valueField()
, annotation.joinTable()
, annotation.joinSelfColumn()
, annotation.joinTargetColumn()

View File

@ -359,8 +359,9 @@ public class RelationManager {
DataSourceKey.use(configDsKey);
}
//仅绑定字段:As目标实体类 不进行字段绑定:As映射类型
QueryWrapper queryWrapper = relation.buildQueryWrapper(targetValues);
List<?> targetObjectList = mapper.selectListByQueryAs(queryWrapper, relation.getMappingType());
List<?> targetObjectList = mapper.selectListByQueryAs(queryWrapper, relation.isOnlyQueryValueField() ? relation.getTargetEntityClass() : relation.getMappingType());
if (CollectionUtil.isNotEmpty(targetObjectList)) {
//递归查询

View File

@ -31,11 +31,11 @@ class ToManyRelation<SelfEntity> extends AbstractRelation<SelfEntity> {
protected long limit = 0;
public ToManyRelation(String selfField, String targetSchema, String targetTable, String targetField,
public ToManyRelation(String selfField, String targetSchema, String targetTable, String targetField, String valueField,
String joinTable, String joinSelfColumn, String joinTargetColumn,
String dataSource, Class<SelfEntity> selfEntityClass, Field relationField,
String extraCondition, String[] selectColumns) {
super(selfField, targetSchema, targetTable, targetField,
super(selfField, targetSchema, targetTable, targetField, valueField,
joinTable, joinSelfColumn, joinTargetColumn,
dataSource, selfEntityClass, relationField,
extraCondition, selectColumns
@ -101,7 +101,12 @@ class ToManyRelation<SelfEntity> extends AbstractRelation<SelfEntity> {
for (Object targetObject : targetObjectList) {
Object targetValue = targetFieldWrapper.get(targetObject);
if (targetValue != null && targetMappingValues.contains(targetValue.toString())) {
collection.add(targetObject);
if (onlyQueryValueField) {
//仅绑定某个字段
collection.add(FieldWrapper.of(targetObject.getClass(), valueField).get(targetObject));
} else {
collection.add(targetObject);
}
}
}
relationFieldWrapper.set(collection, selfEntity);

View File

@ -16,6 +16,7 @@
package com.mybatisflex.core.relation;
import com.mybatisflex.core.row.Row;
import com.mybatisflex.core.util.FieldWrapper;
import java.lang.reflect.Field;
import java.util.List;
@ -23,10 +24,10 @@ import java.util.List;
class ToOneRelation<SelfEntity> extends AbstractRelation<SelfEntity> {
public ToOneRelation(String selfField, String targetSchema, String targetTable, String targetField,
public ToOneRelation(String selfField, String targetSchema, String targetTable, String targetField, String valueField,
String joinTable, String joinSelfColumn, String joinTargetColumn,
String dataSource, Class<SelfEntity> selfEntityClass, Field relationField, String[] selectColumns) {
super(selfField, targetSchema, targetTable, targetField,
super(selfField, targetSchema, targetTable, targetField, valueField,
joinTable, joinSelfColumn, joinTargetColumn,
dataSource, selfEntityClass, relationField,
null, selectColumns
@ -53,7 +54,12 @@ class ToOneRelation<SelfEntity> extends AbstractRelation<SelfEntity> {
for (Object targetObject : targetObjectList) {
Object targetValue = targetFieldWrapper.get(targetObject);
if (targetValue != null && targetMappingValue.equals(targetValue.toString())) {
relationFieldWrapper.set(targetObject, selfEntity);
if (onlyQueryValueField) {
//仅绑定某个字段
relationFieldWrapper.set(FieldWrapper.of(targetObject.getClass(), valueField).get(targetObject), selfEntity);
} else {
relationFieldWrapper.set(targetObject, selfEntity);
}
break;
}
}

View File

@ -15,7 +15,7 @@
*/
package com.mybatisflex.core.update;
import com.mybatisflex.core.util.FieldWrapper;
import com.mybatisflex.core.util.StringUtil;
import org.apache.ibatis.javassist.util.proxy.MethodHandler;
@ -42,7 +42,15 @@ class ModifyAttrsRecordHandler implements MethodHandler {
&& methodName.length() > 3
&& Character.isUpperCase(methodName.charAt(3))
&& originalMethod.getParameterCount() == 1) {
String property = StringUtil.firstCharToLowerCase(originalMethod.getName().substring(3));
//标识 @Column(ignore=true) 的字段不去更新
FieldWrapper fw = FieldWrapper.of(originalMethod.getDeclaringClass(), property);
if (fw != null && fw.isIgnore()) {
return proxyMethod.invoke(self, args);
}
updates.put(property, args[0]);
}
@ -51,5 +59,3 @@ class ModifyAttrsRecordHandler implements MethodHandler {
}

View File

@ -48,12 +48,20 @@ public class UpdateChain<T> extends QueryWrapperAdapter<UpdateChain<T>> implemen
return new UpdateChain<>(baseMapper);
}
public static <T> UpdateChain<T> of(BaseMapper<T> baseMapper) {
return new UpdateChain<>(baseMapper);
}
public static <T> UpdateChain<T> of(T entityObject) {
Class<T> entityClass = (Class<T>) ClassUtil.getUsefulClass(entityObject.getClass());
BaseMapper<T> baseMapper = Mappers.ofEntityClass(entityClass);
return new UpdateChain<>(baseMapper, entityObject);
}
public static <T> UpdateChain<T> of(T entityObject, BaseMapper<T> baseMapper) {
return new UpdateChain<>(baseMapper, entityObject);
}
public UpdateChain(BaseMapper<T> baseMapper) {
this.baseMapper = baseMapper;

View File

@ -15,6 +15,7 @@
*/
package com.mybatisflex.core.util;
import com.mybatisflex.annotation.Column;
import org.apache.ibatis.reflection.Reflector;
import java.lang.reflect.*;
@ -27,6 +28,7 @@ public class FieldWrapper {
public static Map<Class<?>, Map<String, FieldWrapper>> cache = new ConcurrentHashMap<>();
private Field field;
private boolean isIgnore = false;
private Class<?> fieldType;
private Class<?> mappingType;
private Class<?> keyType;
@ -69,6 +71,10 @@ public class FieldWrapper {
fieldWrapper.fieldType = findField.getType();
initMappingTypeAndKeyType(clazz, findField, fieldWrapper);
Column column = findField.getAnnotation(Column.class);
if (column != null && column.ignore()) {
fieldWrapper.isIgnore = true;
}
fieldWrapper.setterMethod = setter;
@ -138,4 +144,8 @@ public class FieldWrapper {
public Field getField() {
return field;
}
public boolean isIgnore() {
return isIgnore;
}
}

View File

@ -88,14 +88,16 @@ public class StringUtil {
if (isBlank(string)) {
return "";
}
String temp = string.toLowerCase();
int strLen = temp.length();
if(Character.isUpperCase(string.charAt(0))){
string = string.toLowerCase();
}
int strLen = string.length();
StringBuilder sb = new StringBuilder(strLen);
for (int i = 0; i < strLen; i++) {
char c = temp.charAt(i);
char c = string.charAt(i);
if (c == '_') {
if (++i < strLen) {
sb.append(Character.toUpperCase(temp.charAt(i)));
sb.append(Character.toUpperCase(string.charAt(i)));
}
} else {
sb.append(c);

View File

@ -21,13 +21,13 @@ import com.mybatisflex.core.dialect.IDialect;
import com.mybatisflex.core.dialect.KeywordWrap;
import com.mybatisflex.core.dialect.LimitOffsetProcessor;
import com.mybatisflex.core.dialect.impl.CommonsDialectImpl;
import com.mybatisflex.core.query.CPI;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.dialect.impl.DmDialect;
import com.mybatisflex.core.dialect.impl.OracleDialect;
import com.mybatisflex.core.query.*;
import com.mybatisflex.core.table.DynamicTableProcessor;
import com.mybatisflex.core.table.TableInfo;
import com.mybatisflex.core.table.TableInfoFactory;
import com.mybatisflex.core.table.TableManager;
import com.mybatisflex.core.query.SqlOperators;
import org.junit.Test;
import java.util.Arrays;
@ -107,7 +107,8 @@ public class AccountSqlTester {
public void testSelect1ColumnsSql() {
QueryWrapper query = new QueryWrapper()
.select(ACCOUNT.ID, ACCOUNT.USER_NAME,
ARTICLE.ID.as("articleId"), ARTICLE.TITLE)
ARTICLE.ID.as("articleId"), ARTICLE.TITLE,
max(ACCOUNT.AGE).as("ageMax"))
.from(ACCOUNT.as("a"), ARTICLE.as("b"))
.where(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID));
@ -119,14 +120,41 @@ public class AccountSqlTester {
@Test
public void testSelectColumnsAndFunctionsSql() {
QueryWrapper query = new QueryWrapper()
.select(ACCOUNT.ID, ACCOUNT.USER_NAME, max(ACCOUNT.BIRTHDAY), avg(ACCOUNT.SEX).as("sex_avg"))
.from(ACCOUNT);
.select(ACCOUNT.ID, ACCOUNT.USER_NAME, ACCOUNT.AGE.as("aGe"), max(ACCOUNT.BIRTHDAY).as("Max_BirthDay"), avg(ACCOUNT.SEX).as("sex_avg"))
.from(ACCOUNT.as("tableAlias"));
IDialect dialect = new CommonsDialectImpl();
IDialect dialect = new OracleDialect();
String sql = dialect.forSelectByQuery(query);
System.out.println(sql);
}
@Test
public void testSelectColumnsAndFunctionsSqlAlias() {
QueryWrapper query = new QueryWrapper()
.select(ACCOUNT.ID, ACCOUNT.USER_NAME, ACCOUNT.AGE.as("aGe"), max(ACCOUNT.BIRTHDAY).as("Max_BirthDay"), avg(ACCOUNT.SEX).as("sex_avg"))
.from(ACCOUNT.as("tableAlias"));
IDialect dialect = new DmDialect();
String sql = dialect.forSelectByQuery(query);
System.out.println(sql);
}
@Test
public void testDistinctColumnAlias() {
QueryWrapper queryWrapper = new QueryWrapper()
.select(
new DistinctQueryColumn(ACCOUNT.SEX).as("sexDis"))
.select( ACCOUNT.USER_NAME.add(ACCOUNT.AGE).as("addAlias"))
.select(new RawQueryColumn("abc").as("aBc"))
.from(ACCOUNT);
// IDialect dialect = new CommonsDialectImpl();
IDialect dialect = new OracleDialect();
// IDialect dialect = new DmDialect();
String sql = dialect.forSelectByQuery(queryWrapper);
System.out.println("sql = " + sql);
}
@Test
public void testSelectAllColumnsSql() {

View File

@ -86,4 +86,15 @@ public class FunctionSqlTest {
System.out.println(sql);
}
@Test
public void test06() {
String sql = QueryWrapper.create()
.select(column("(select role_name from tb_role where id = ?)", 1))
.select(ACCOUNT.USER_NAME)
.from(ACCOUNT)
.toSQL();
System.out.println(sql);
}
}

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.mybatis-flex</groupId>
<artifactId>parent</artifactId>
<version>1.6.4</version>
<version>1.6.6</version>
</parent>
<artifactId>mybatis-flex-dependencies</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.6.4</version>
<version>1.6.6</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -232,6 +232,10 @@ public class MybatisFlexProcessor extends AbstractProcessor {
return SourceVersion.latestSupported();
}
/**
* 通过 classElement 操作起所有字段生成 ColumnInfo 并填充 columnInfos 结合
*/
private void fillColumnInfoList(Set<ColumnInfo> columnInfos, List<String> defaultColumns, TypeElement baseElement, TypeElement classElement, boolean camelToUnderline) {
for (Element fieldElement : classElement.getEnclosedElements()) {

View File

@ -69,6 +69,9 @@ public class ContentBuilder {
content.append("import com.mybatisflex.core.table.TableDef;\n\n");
content.append("// Auto generate by mybatis-flex, do not modify it.\n");
content.append("public class ").append(tableDefClassName).append(" extends TableDef {\n\n");
//TableDef 类的属性名称
String tableDefPropertyName = null;
if (!allInTablesEnable) {
String entityComment = tableInfo.getEntityComment();
if (!StrUtil.isBlank(entityComment)) {
@ -76,9 +79,13 @@ public class ContentBuilder {
.append(" * ").append(entityComment.trim()).append("\n")
.append(" */\n");
}
content.append(" public static final ").append(tableDefClassName).append(' ').append(StrUtil.buildFieldName(tableInfo.getEntitySimpleName().concat(tableDefInstanceSuffix != null ? tableDefInstanceSuffix.trim() : ""), tableDefPropertiesNameStyle))
tableDefPropertyName = StrUtil.buildFieldName(tableInfo.getEntitySimpleName().concat(tableDefInstanceSuffix != null ? tableDefInstanceSuffix.trim() : ""), tableDefPropertiesNameStyle);
content.append(" public static final ").append(tableDefClassName).append(' ').append(tableDefPropertyName)
.append(" = new ").append(tableDefClassName).append("();\n\n");
}
String finalTableDefPropertyName = tableDefPropertyName;
columnInfos.forEach(columnInfo -> {
String comment = columnInfo.getComment();
if (!StrUtil.isBlank(comment)) {
@ -86,8 +93,16 @@ public class ContentBuilder {
.append(" * ").append(comment.trim()).append("\n")
.append(" */\n");
}
// QueryColumn 属性定义的名称
String columnPropertyName = StrUtil.buildFieldName(columnInfo.getProperty(), tableDefPropertiesNameStyle);
//当字段名称和表名一样时自动为字段添加一个小尾巴 "_"例如 account_
if (columnPropertyName.equals(finalTableDefPropertyName)) {
columnPropertyName = columnPropertyName + "_";
}
content.append(" public final QueryColumn ")
.append(StrUtil.buildFieldName(columnInfo.getProperty(), tableDefPropertiesNameStyle))
.append(columnPropertyName)
.append(" = new QueryColumn(this, \"")
.append(columnInfo.getColumn()).append("\"");
if (columnInfo.getAlias() != null && columnInfo.getAlias().length > 0) {
@ -102,7 +117,11 @@ public class ContentBuilder {
StringJoiner defaultColumnJoiner = new StringJoiner(", ");
columnInfos.forEach(columnInfo -> {
if (defaultColumns.contains(columnInfo.getColumn())) {
defaultColumnJoiner.add(StrUtil.buildFieldName(columnInfo.getProperty(), tableDefPropertiesNameStyle));
String columnPropertyName = StrUtil.buildFieldName(columnInfo.getProperty(), tableDefPropertiesNameStyle);
if (columnPropertyName.equals(finalTableDefPropertyName)) {
columnPropertyName = columnPropertyName + "_";
}
defaultColumnJoiner.add(columnPropertyName);
}
});
content.append("\n /**\n")

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.6.4</version>
<version>1.6.6</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.6.4</version>
<version>1.6.6</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.6.4</version>
<version>1.6.6</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>mybatis-flex-test</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.6.4</version>
<version>1.6.6</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mybatisflex.test;
import com.mybatisflex.annotation.Table;
import java.io.Serializable;
@Table(value = "tb_account_4")
public class Account4 extends Account2 implements Serializable {
private static final long serialVersionUID = 1L;
//字段名和类名相同
private int account4;
public int getAccount4() {
return account4;
}
public void setAccount4(int account4) {
this.account4 = account4;
}
}

View File

@ -73,11 +73,12 @@ public class ContributorsDocGen {
if (userName.contains("@")) {
userName = userName.substring(0, userName.indexOf("@"));
}
if (StringUtil.isBlank(src)) {
src = "https://api.dicebear.com/7.x/initials/svg?seed=" + userName;
}
markdown.append("|");
if (StringUtil.isNotBlank(src)) {
markdown.append("![](" + src + ")");
}
markdown.append("![](" + src + ")");
markdown.append(userName);
startIndex++;

View File

@ -4,7 +4,7 @@
<parent>
<artifactId>mybatis-flex-test</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.6.4</version>
<version>1.6.6</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>mybatis-flex-test</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.6.4</version>
<version>1.6.6</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -0,0 +1,108 @@
package com.mybatisflex.test.model;
import com.mybatisflex.annotation.*;
import java.io.Serializable;
import java.util.List;
/**
* 字段绑定测试
* @author Ice 2023/09/16
* @version 1.0
*/
@Table("tb_user")
public class UserVO5 implements Serializable {
private static final long serialVersionUID = 474700189859144273L;
@Id
private Integer userId;
private String userName;
private String password;
@RelationOneToOne(
selfField = "userId",
targetTable = "tb_id_card",
targetField = "id",
valueField = "idNumber"
)
private String idNumberCustomFieldName;
@RelationOneToMany(
selfField = "userId",
targetTable = "tb_user_order",
targetField = "userId",
valueField = "orderId"
)
private List<Integer> orderIdList;
@RelationManyToMany(
selfField = "userId",
targetTable = "tb_role",
targetField = "roleId",
valueField = "roleName",
joinTable = "tb_user_role",
joinSelfColumn = "user_id",
joinTargetColumn = "role_id"
)
private List<String> roleNameList;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getIdNumberCustomFieldName() {
return idNumberCustomFieldName;
}
public void setIdNumberCustomFieldName(String idNumberCustomFieldName) {
this.idNumberCustomFieldName = idNumberCustomFieldName;
}
public List<Integer> getOrderIdList() {
return orderIdList;
}
public void setOrderIdList(List<Integer> orderIdList) {
this.orderIdList = orderIdList;
}
public List<String> getRoleNameList() {
return roleNameList;
}
public void setRoleNameList(List<String> roleNameList) {
this.roleNameList = roleNameList;
}
@Override
public String toString() {
return "UserVO5{" +
"userId=" + userId +
", userName='" + userName + '\'' +
", password='" + password + '\'' +
", idNumberCustomFieldName='" + idNumberCustomFieldName + '\'' +
", orderIdList=" + orderIdList +
", roleNameList=" + roleNameList +
'}';
}
}

View File

@ -280,4 +280,10 @@ class UserMapperTest {
System.err.println(user);
}
@Test
public void testFieldBindRelations() {
List<UserVO5> userVO5List = userMapper.selectListWithRelationsByQueryAs(QueryWrapper.create(), UserVO5.class);
System.out.println(userVO5List);
}
}

View File

@ -4,7 +4,7 @@
<parent>
<artifactId>mybatis-flex-test</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.6.4</version>
<version>1.6.6</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>mybatis-flex-test</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.6.4</version>
<version>1.6.6</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>parent</artifactId>
<groupId>com.mybatis-flex</groupId>
<version>1.6.4</version>
<version>1.6.6</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -7,7 +7,7 @@
<groupId>com.mybatis-flex</groupId>
<artifactId>parent</artifactId>
<packaging>pom</packaging>
<version>1.6.4</version>
<version>1.6.6</version>
<name>mybatis-flex</name>
<url>https://mybatis-flex.com</url>
@ -55,7 +55,7 @@
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<mybatis-flex.version>1.6.4</mybatis-flex.version>
<mybatis-flex.version>1.6.6</mybatis-flex.version>
<mybatis.version>3.5.13</mybatis.version>
<mybatis-spring.version>2.1.0</mybatis-spring.version>