mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-07 00:58:24 +08:00
Merge branch 'mybatis-flex:main' into main
This commit is contained in:
commit
b50f59480a
32
changes.txt
32
changes.txt
@ -1,3 +1,35 @@
|
||||
mybatis-flex v1.3.2 20230528:
|
||||
新增:select (field1 * field2 * 100) as xxx from ... 的 SQL 构建场景
|
||||
优化:ClassUtil.wrap 方法修改为 getWrapType
|
||||
优化:重构 BaseMapper.selectListByQueryAs() 方法,使其更加通用
|
||||
优化:重构 EnumWrapper.java,使之方法和变量更加明确易读
|
||||
优化:WrapperUtil.buildAsAlias 方法,使之在多种场景下有统一行为
|
||||
优化:优化 Row,添加 prepareAttrs() 等方法
|
||||
修复:EnumWrapper 对其 getter 方法判断错误的问题
|
||||
文档:更新一对多、多对一的子查询相关文档
|
||||
|
||||
|
||||
|
||||
mybatis-flex v1.3.1 20230526:
|
||||
新增:分页查询添加关联字段查询功能;
|
||||
新增:Mapper 添加 updateNumberAddByQuery 方法,用于 update table set xxx = xxx + 1 的场景;
|
||||
优化:添加 FieldWrapper 使得关联字段查询拥有更高的性能
|
||||
优化:优化 EnumWrapper 使之逻辑更加清晰简单
|
||||
优化:字段子查询不在需要配置 type
|
||||
优化:代码生成器 remarks 修改为 comment; 感谢 @王帅
|
||||
优化:代码生成器 GlobalConfig 拆分,使之更加直观; 感谢 @王帅
|
||||
优化:代码生成器新增注释生成配置; 感谢 @王帅
|
||||
优化:代码生成器新增 mapper xml 生成功能; 感谢 @王帅
|
||||
优化:代码生成器新增 package-info.java 生成功能; 感谢 @王帅
|
||||
优化:代码生成器新增 Controller 生成功能; 感谢 @王帅
|
||||
优化:代码生成器每个生成的文件,单独支持是否覆盖已生成的文件; 感谢 @王帅
|
||||
优化:代码生成器新增设置模板文件位置,可以使用指定的模板生成文件; 感谢 @王帅
|
||||
修复:select max(select(...)) 等函数内部有参数时,无法获取的问题;
|
||||
文档:更新代码生成器文档。感谢 @王帅
|
||||
文档:优化 QueryWrapper 的相关文档
|
||||
|
||||
|
||||
|
||||
mybatis-flex v1.3.0 20230525:
|
||||
新增:新增 一对多、多对一 查询功能
|
||||
新增:为 SqlServer 添加独立的 LimitOffset 处理器
|
||||
|
||||
@ -11,6 +11,7 @@ export default defineConfig({
|
||||
// logo: '/assets/images/logo02.png',
|
||||
|
||||
themeConfig: {
|
||||
outlineTitle:'章节',
|
||||
search: {
|
||||
provider: 'local'
|
||||
},
|
||||
|
||||
@ -1,16 +1,19 @@
|
||||
<!--.vitepress/theme/MyLayout.vue-->
|
||||
<script setup>
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
|
||||
const {Layout} = DefaultTheme
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Layout>
|
||||
<!-- <template #doc-footer-before>-->
|
||||
<!-- <div class="tip custom-block" style="margin-bottom: 24px">-->
|
||||
<!-- <p class="custom-block-title">广告</p>-->
|
||||
<!-- <p>此次广告位招租。</p>-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
<!--docs: https://vitepress.dev/guide/extending-default-theme#layout-slots-->
|
||||
<template #doc-footer-before>
|
||||
<div class="info custom-block" style="margin-bottom: 14px">
|
||||
<!-- <p class="custom-block-title">对这篇文章还有疑问?</p>-->
|
||||
<p>对这篇文章还有疑问? 点击 <a href="https://gitee.com/mybatis-flex/mybatis-flex/issues/new" target="_blank">这里</a> 发起提问。
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</Layout>
|
||||
</template>
|
||||
@ -64,6 +64,7 @@ delete from tb_account where id >= 100;
|
||||
- **updateByQuery(entity, queryWrapper)**:根据 queryWrapper 组成的条件更新到 entity 到数据库,entity 可以没有主键(如果有也会被忽略), entity 的 null 属性,会自动被忽略。
|
||||
- **updateByQuery(entity, ignoreNulls, queryWrapper)**:据 queryWrapper 组成的条件更新到 entity 到数据库,entity 可以没有主键(如果有也会被忽略)。 ignoreNulls 用于是否忽略 entity 的 null 属性
|
||||
, 若 ignoreNulls 为 false,entity 的所有 null 属性都会被更新到数据库。
|
||||
- **updateNumberAddByQuery(fieldName,value,queryWrapper)**:通过 `update table set field = field + 1 where ... ` 的这种方向更新数据库某个字段内容。
|
||||
|
||||
|
||||
## 部分字段更新
|
||||
|
||||
@ -21,7 +21,7 @@ public class Article {
|
||||
|
||||
查询代码如下:
|
||||
|
||||
```java {10-14}
|
||||
```java {9-13}
|
||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
.select().form(ARTICLE)
|
||||
.where(ARTICLE.id.ge(100));
|
||||
@ -29,7 +29,6 @@ QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
List<Article> articles = mapper.selectListByQuery(queryWrapper
|
||||
, fieldQueryBuilder -> fieldQueryBuilder
|
||||
.field(Article::getCategories) // 或者 .field("categories")
|
||||
.type(Category.class) //非集合,自动读取 type,可以不指定 type
|
||||
.queryWrapper(article -> QueryWrapper.create()
|
||||
.select().from(CATEGORY)
|
||||
.where(CATEGORY.id.in(
|
||||
@ -42,7 +41,7 @@ List<Article> articles = mapper.selectListByQuery(queryWrapper
|
||||
|
||||
通过以上代码可以看出,`Article.categories` 字段的结果,来源于 `queryWrapper()` 方法构建的 `QueryWrapper`。
|
||||
|
||||
其原理是:MyBatis-Flex 的内部逻辑是先查询出 `Article` 的数据,然后再根据 `Article` 构建出新的 SQL,查询分类,并赋值给 `Article.categories`,
|
||||
其原理是:MyBatis-Flex 的内部逻辑是先查询出 `Article` 的数据,然后再根据 `Article` 构建出新的 SQL,查询分类,并赋值给 `Article.categories` 属性,
|
||||
假设 `Article` 有 10 条数据,那么最终会进行 11 次数据库查询。
|
||||
|
||||
查询的 SQL 大概如下:
|
||||
@ -82,7 +81,27 @@ select * from tb_category where id in
|
||||
(select category_id from article_category_mapping where article_id = 109);
|
||||
```
|
||||
|
||||
## 其他场景
|
||||
## 知识点
|
||||
MyBatis-Flex 的关联子查询,和 JPA 等其他第三方框架有很大的差异,比如 JPA 是通过配置来构建查询 SQL,其构建生成的 SQL 对于用户来说是不透明的。
|
||||
因此,用户几乎无法对 JPA 的注解生成 SQL 优化。
|
||||
|
||||
而 MyBatis-Flex 关联子查询的 SQL 完全是由用户构建的,因此会更加灵活,更加有利于我们进行 SQL 优化。在子查询中,有很多的场景, JPA 对一对一、
|
||||
一对多等等不同的场景给出了不同的注解、以及参数,导致用户的学习成本非常高。
|
||||
|
||||
对 MyBatis-Flex 来说,学习成本是非常低的,在构建子查询时,只需要明白为哪个字段、通过什么样的 SQL 查询就可以了,以下是示例:
|
||||
|
||||
```java 3,4
|
||||
List<Article> articles = mapper.selectListByQuery(query
|
||||
, fieldQueryBuilder -> fieldQueryBuilder
|
||||
.field(...) // 为哪个字段查询的?
|
||||
.queryWrapper(...) // 通过什么样的 SQL 查询的?
|
||||
);
|
||||
```
|
||||
|
||||
因此,在 MyBatis-Flex 的设计中,无论是一对多、多对一、多对多... 还是其他任何一种场景,其逻辑都是一样的。
|
||||
|
||||
|
||||
## 更多场景
|
||||
|
||||
通过以上内容看出,`Article` 的任何属性,都是可以通过传入 `FieldQueryBuilder` 来构建 `QueryWrapper` 进行再次查询,
|
||||
这些不仅仅只适用于 `一对多`、`一对一`、`多对一`、`多对多`等场景。任何 `Article` 对象里的属性,需要二次查询赋值的,都是可以通过这种方式进行。
|
||||
@ -125,11 +125,14 @@ FROM tb_account
|
||||
|
||||
```java
|
||||
QueryWrapper wrapper = QueryWrapper.create()
|
||||
.select(ACCOUNT.ID
|
||||
,case_().when(ACCOUNT.ID.ge(2)).then("x2")
|
||||
.select(
|
||||
ACCOUNT.ID
|
||||
,case_()
|
||||
.when(ACCOUNT.ID.ge(2)).then("x2")
|
||||
.when(ACCOUNT.ID.ge(1)).then("x1")
|
||||
.else_("x100")
|
||||
.end().as("xName")
|
||||
)
|
||||
```
|
||||
|
||||
其查询生成的 Sql 如下:
|
||||
@ -156,11 +159,13 @@ SQL 执行的结果如下:
|
||||
|
||||
```java
|
||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
.select(ACCOUNT.ALL_COLUMNS,
|
||||
.select(
|
||||
ACCOUNT.ALL_COLUMNS,
|
||||
case_(ACCOUNT.ID)
|
||||
.when(100).then(100)
|
||||
.when(200).then(200)
|
||||
.else_(300).end().as("result"))
|
||||
.else_(300).end().as("result")
|
||||
)
|
||||
.from(ACCOUNT)
|
||||
.where(ACCOUNT.USER_NAME.like("michael"));
|
||||
```
|
||||
@ -173,7 +178,8 @@ SELECT *,
|
||||
WHEN 100 THEN 100
|
||||
WHEN 200 THEN 200
|
||||
ELSE 300 END) AS `result`
|
||||
FROM `tb_account` WHERE `user_name` LIKE ?
|
||||
FROM `tb_account`
|
||||
WHERE `user_name` LIKE ?
|
||||
```
|
||||
|
||||
::: tip 提示
|
||||
|
||||
@ -142,7 +142,7 @@ mybatis-flex:
|
||||
同时,项目若使用到了多个数据源类型,则也需要添加 `type` 来指定当前数据源的类型。
|
||||
|
||||
|
||||
除了 `type`、`url`、`username`、`password` 的配置以为,MyBatis-Flex 支持该 `DataSource` 类型的所有参数配置,
|
||||
除了 `type`、`url`、`username`、`password` 的配置以外,MyBatis-Flex 支持该 `DataSource` 类型的所有参数配置,
|
||||
例如,在 `DruidDataSource` 类中存在 `setAsyncInit` 方法,我们就可以添加 `asyncInit` 的配置,如下所示:
|
||||
|
||||
```yaml 8
|
||||
|
||||
@ -27,12 +27,12 @@ Maven示例:
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-core</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-processor</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
```
|
||||
@ -46,7 +46,7 @@ Gradle示例:
|
||||
```groovy
|
||||
// file: build.gradle
|
||||
ext {
|
||||
mybatis_flex_version = '1.3.0'
|
||||
mybatis_flex_version = '1.3.2'
|
||||
}
|
||||
dependencies {
|
||||
implementation("com.mybatis-flex:mybatis-flex-core:${mybatis_flex_version}")
|
||||
|
||||
@ -12,12 +12,12 @@
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-core</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-processor</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
```
|
||||
@ -28,12 +28,12 @@
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-spring</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-processor</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
``````
|
||||
@ -44,12 +44,12 @@
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-spring-boot-starter</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-processor</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
```
|
||||
@ -70,7 +70,7 @@
|
||||
<path>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-processor</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
|
||||
@ -179,7 +179,7 @@ processor.baseMapperClass=com.domain.mapper.MyBaseMapper
|
||||
```
|
||||
dependencies {
|
||||
...
|
||||
annotationProcessor 'com.mybatis-flex:mybatis-flex-processor:1.3.0'
|
||||
annotationProcessor 'com.mybatis-flex:mybatis-flex-processor:1.3.2'
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-codegen</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-spring</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
@ -19,7 +19,6 @@ package com.mybatisflex.codegen.test;
|
||||
import com.mybatisflex.codegen.Generator;
|
||||
import com.mybatisflex.codegen.config.GlobalConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
@ -82,7 +81,7 @@ public class GeneratorTest {
|
||||
generator.generate();
|
||||
}
|
||||
|
||||
@Test
|
||||
// @Test
|
||||
public void testCodeGen2() {
|
||||
//配置数据源
|
||||
HikariDataSource dataSource = new HikariDataSource();
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -22,13 +22,13 @@
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-annotation</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-processor</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@ -24,14 +24,9 @@ import com.mybatisflex.core.provider.EntitySqlProvider;
|
||||
import com.mybatisflex.core.query.*;
|
||||
import com.mybatisflex.core.table.TableInfo;
|
||||
import com.mybatisflex.core.table.TableInfoFactory;
|
||||
import com.mybatisflex.core.util.CollectionUtil;
|
||||
import com.mybatisflex.core.util.ConvertUtil;
|
||||
import com.mybatisflex.core.util.ObjectUtil;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
import com.mybatisflex.core.util.*;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
import org.apache.ibatis.builder.annotation.ProviderContext;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.apache.ibatis.reflection.SystemMetaObject;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
@ -283,6 +278,33 @@ public interface BaseMapper<T> {
|
||||
int updateByQuery(@Param(FlexConsts.ENTITY) T entity, @Param(FlexConsts.IGNORE_NULLS) boolean ignoreNulls, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
||||
|
||||
|
||||
/**
|
||||
* 执行类似 update table set field=field+1 where ... 的场景
|
||||
*
|
||||
* @param fieldName 字段名
|
||||
* @param value 值( >=0 加,小于 0 减)
|
||||
* @param queryWrapper 条件
|
||||
* @see EntitySqlProvider#updateNumberAddByQuery(Map, ProviderContext)
|
||||
*/
|
||||
@UpdateProvider(type = EntitySqlProvider.class, method = "updateNumberAddByQuery")
|
||||
int updateNumberAddByQuery(@Param(FlexConsts.FIELD_NAME) String fieldName, @Param(FlexConsts.VALUE) Number value, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
||||
|
||||
|
||||
/**
|
||||
* 执行类似 update table set field=field+1 where ... 的场景
|
||||
*
|
||||
* @param fn 字段名
|
||||
* @param value 值( >=0 加,小于 0 减)
|
||||
* @param queryWrapper 条件
|
||||
* @see EntitySqlProvider#updateNumberAddByQuery(Map, ProviderContext)
|
||||
*/
|
||||
default int updateNumberAddByQuery(LambdaGetter<T> fn, Number value, QueryWrapper queryWrapper) {
|
||||
TableInfo tableInfo = TableInfoFactory.ofMapperClass(ClassUtil.getUsefulClass(getClass()));
|
||||
String column = tableInfo.getColumnByProperty(LambdaUtil.getFieldName(fn));
|
||||
return updateNumberAddByQuery(column, value, queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据主键来选择数据
|
||||
*
|
||||
@ -336,13 +358,8 @@ public interface BaseMapper<T> {
|
||||
* @return 数据内容
|
||||
*/
|
||||
default <R> R selectOneByQueryAs(QueryWrapper queryWrapper, Class<R> asType) {
|
||||
try {
|
||||
MappedStatementTypes.setCurrentType(asType);
|
||||
List<R> entities = selectListByQueryAs(queryWrapper.limit(1), asType);
|
||||
return (entities == null || entities.isEmpty()) ? null : entities.get(0);
|
||||
} finally {
|
||||
MappedStatementTypes.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -412,7 +429,7 @@ public interface BaseMapper<T> {
|
||||
List<T> selectListByQuery(@Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
||||
|
||||
|
||||
default List<T> selectListByQuery(@Param(FlexConsts.QUERY) QueryWrapper queryWrapper
|
||||
default List<T> selectListByQuery(QueryWrapper queryWrapper
|
||||
, Consumer<FieldQueryBuilder<T>>... consumers) {
|
||||
|
||||
List<T> list = selectListByQuery(queryWrapper);
|
||||
@ -420,46 +437,7 @@ public interface BaseMapper<T> {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
list.forEach(entity -> {
|
||||
for (Consumer<FieldQueryBuilder<T>> consumer : consumers) {
|
||||
FieldQueryBuilder<T> fieldQueryBuilder = new FieldQueryBuilder<>(entity);
|
||||
consumer.accept(fieldQueryBuilder);
|
||||
FieldQuery fieldQuery = fieldQueryBuilder.build();
|
||||
QueryWrapper childQuery = fieldQuery.getQueryWrapper();
|
||||
MetaObject entityMetaObject = SystemMetaObject.forObject(entity);
|
||||
Class<?> setterType = entityMetaObject.getSetterType(fieldQuery.getField());
|
||||
|
||||
Class<?> mappingType = fieldQuery.getMappingType();
|
||||
if (mappingType == null) {
|
||||
if (setterType.isAssignableFrom(Collection.class)) {
|
||||
throw new IllegalStateException("Mapping Type can not be null for query Many.");
|
||||
} else if (setterType.isArray()) {
|
||||
mappingType = setterType.getComponentType();
|
||||
} else {
|
||||
mappingType = setterType;
|
||||
}
|
||||
}
|
||||
|
||||
Object value;
|
||||
try {
|
||||
MappedStatementTypes.setCurrentType(mappingType);
|
||||
if (setterType.isAssignableFrom(List.class)) {
|
||||
value = selectListByQueryAs(childQuery, mappingType);
|
||||
} else if (setterType.isAssignableFrom(Set.class)) {
|
||||
value = selectListByQueryAs(childQuery, mappingType);
|
||||
value = new HashSet<>((Collection<?>) value);
|
||||
} else if (setterType.isArray()) {
|
||||
value = selectListByQueryAs(childQuery, mappingType);
|
||||
value = ((List<?>) value).toArray();
|
||||
} else {
|
||||
value = selectOneByQueryAs(childQuery, mappingType);
|
||||
}
|
||||
} finally {
|
||||
MappedStatementTypes.clear();
|
||||
}
|
||||
entityMetaObject.setValue(fieldQuery.getField(), value);
|
||||
}
|
||||
});
|
||||
__queryFields(list, consumers);
|
||||
|
||||
return list;
|
||||
}
|
||||
@ -473,70 +451,26 @@ public interface BaseMapper<T> {
|
||||
* @param asType 接收数据类型
|
||||
* @return 数据列表
|
||||
*/
|
||||
@SelectProvider(type = EntitySqlProvider.class, method = "selectListByQuery")
|
||||
<R> List<R> selectListByQueryAs(@Param(FlexConsts.QUERY) QueryWrapper queryWrapper, Class<R> asType);
|
||||
|
||||
|
||||
default <R> List<R> selectListByQueryAs(@Param(FlexConsts.QUERY) QueryWrapper queryWrapper, Class<R> asType
|
||||
, Consumer<FieldQueryBuilder<R>>... consumers) {
|
||||
List<R> list;
|
||||
default <R> List<R> selectListByQueryAs(QueryWrapper queryWrapper, Class<R> asType) {
|
||||
try {
|
||||
MappedStatementTypes.setCurrentType(asType);
|
||||
list = selectListByQueryAs(queryWrapper, asType);
|
||||
return (List<R>) selectListByQuery(queryWrapper);
|
||||
} finally {
|
||||
MappedStatementTypes.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
default <R> List<R> selectListByQueryAs(QueryWrapper queryWrapper, Class<R> asType
|
||||
, Consumer<FieldQueryBuilder<R>>... consumers) {
|
||||
List<R> list = selectListByQueryAs(queryWrapper, asType);
|
||||
if (list == null || list.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
list.forEach(entity -> {
|
||||
for (Consumer<FieldQueryBuilder<R>> consumer : consumers) {
|
||||
FieldQueryBuilder<R> fieldQueryBuilder = new FieldQueryBuilder<>(entity);
|
||||
consumer.accept(fieldQueryBuilder);
|
||||
FieldQuery fieldQuery = fieldQueryBuilder.build();
|
||||
QueryWrapper childQuery = fieldQuery.getQueryWrapper();
|
||||
|
||||
MetaObject entityMetaObject = SystemMetaObject.forObject(entity);
|
||||
Class<?> setterType = entityMetaObject.getSetterType(fieldQuery.getField());
|
||||
|
||||
Class<?> mappingType = fieldQuery.getMappingType();
|
||||
if (mappingType == null) {
|
||||
if (setterType.isAssignableFrom(Collection.class)) {
|
||||
throw new IllegalStateException("Mapping Type can not be null for query Many.");
|
||||
} else if (setterType.isArray()) {
|
||||
mappingType = setterType.getComponentType();
|
||||
} else {
|
||||
mappingType = setterType;
|
||||
}
|
||||
}
|
||||
|
||||
Object value;
|
||||
try {
|
||||
MappedStatementTypes.setCurrentType(mappingType);
|
||||
if (setterType.isAssignableFrom(List.class)) {
|
||||
value = selectListByQueryAs(childQuery, mappingType);
|
||||
} else if (setterType.isAssignableFrom(Set.class)) {
|
||||
value = selectListByQueryAs(childQuery, mappingType);
|
||||
value = new HashSet<>((Collection<?>) value);
|
||||
} else if (setterType.isArray()) {
|
||||
value = selectListByQueryAs(childQuery, mappingType);
|
||||
value = ((List<?>) value).toArray();
|
||||
} else {
|
||||
value = selectOneByQueryAs(childQuery, mappingType);
|
||||
}
|
||||
} finally {
|
||||
MappedStatementTypes.clear();
|
||||
}
|
||||
|
||||
|
||||
entityMetaObject.setValue(fieldQuery.getField(), value);
|
||||
}
|
||||
});
|
||||
|
||||
__queryFields(list, consumers);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -694,12 +628,12 @@ public interface BaseMapper<T> {
|
||||
* @param queryWrapper 查询条件
|
||||
* @return page 数据
|
||||
*/
|
||||
default Page<T> paginate(Page<T> page, QueryWrapper queryWrapper) {
|
||||
return paginateAs(page, queryWrapper, null);
|
||||
default Page<T> paginate(Page<T> page, QueryWrapper queryWrapper, Consumer<FieldQueryBuilder<T>>... consumers) {
|
||||
return paginateAs(page, queryWrapper, null, consumers);
|
||||
}
|
||||
|
||||
|
||||
default <R> Page<R> paginateAs(Page<R> page, QueryWrapper queryWrapper, Class<R> asType) {
|
||||
default <R> Page<R> paginateAs(Page<R> page, QueryWrapper queryWrapper, Class<R> asType, Consumer<FieldQueryBuilder<R>>... consumers) {
|
||||
List<QueryColumn> selectColumns = CPI.getSelectColumns(queryWrapper);
|
||||
List<QueryOrderBy> orderBys = CPI.getOrderBys(queryWrapper);
|
||||
|
||||
@ -776,19 +710,48 @@ public interface BaseMapper<T> {
|
||||
queryWrapper.limit(offset, page.getPageSize());
|
||||
|
||||
if (asType != null) {
|
||||
try {
|
||||
// 调用内部方法,不走代理,需要主动设置 MappedStatementType
|
||||
// fixed https://gitee.com/mybatis-flex/mybatis-flex/issues/I73BP6
|
||||
MappedStatementTypes.setCurrentType(asType);
|
||||
List<R> records = selectListByQueryAs(queryWrapper, asType);
|
||||
__queryFields(records, consumers);
|
||||
page.setRecords(records);
|
||||
} finally {
|
||||
MappedStatementTypes.clear();
|
||||
}
|
||||
} else {
|
||||
List<R> records = (List<R>) selectListByQuery(queryWrapper);
|
||||
__queryFields(records, consumers);
|
||||
page.setRecords(records);
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
|
||||
default <R> void __queryFields(List<R> list, Consumer<FieldQueryBuilder<R>>[] consumers) {
|
||||
if (CollectionUtil.isEmpty(list) || ArrayUtil.isEmpty(consumers) || consumers[0] == null) {
|
||||
return;
|
||||
}
|
||||
list.forEach(entity -> {
|
||||
for (Consumer<FieldQueryBuilder<R>> consumer : consumers) {
|
||||
FieldQueryBuilder<R> fieldQueryBuilder = new FieldQueryBuilder<>(entity);
|
||||
consumer.accept(fieldQueryBuilder);
|
||||
FieldQuery fieldQuery = fieldQueryBuilder.build();
|
||||
QueryWrapper childQuery = fieldQuery.getQueryWrapper();
|
||||
|
||||
FieldWrapper fieldWrapper = FieldWrapper.of(entity.getClass(), fieldQuery.getField());
|
||||
|
||||
Class<?> fieldType = fieldWrapper.getFieldType();
|
||||
Class<?> mappingType = fieldWrapper.getMappingType();
|
||||
|
||||
Object value;
|
||||
if (fieldType.isAssignableFrom(List.class)) {
|
||||
value = selectListByQueryAs(childQuery, mappingType);
|
||||
} else if (fieldType.isAssignableFrom(Set.class)) {
|
||||
value = selectListByQueryAs(childQuery, mappingType);
|
||||
value = new HashSet<>((Collection<?>) value);
|
||||
} else if (fieldType.isArray()) {
|
||||
value = selectListByQueryAs(childQuery, mappingType);
|
||||
value = ((List<?>) value).toArray();
|
||||
} else {
|
||||
value = selectOneByQueryAs(childQuery, mappingType);
|
||||
}
|
||||
fieldWrapper.set(value, entity);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,15 +20,17 @@ package com.mybatisflex.core;
|
||||
*/
|
||||
public class FlexConsts {
|
||||
public static final String NAME = "MyBatis-Flex";
|
||||
public static final String VERSION = "1.3.0";
|
||||
public static final String VERSION = "1.3.2";
|
||||
|
||||
public static final String DEFAULT_PRIMARY_FIELD = "id";
|
||||
|
||||
public static final String SQL = "$$sql";
|
||||
public static final String SQL_ARGS = "$$sql_args";
|
||||
public static final String TABLE_NAME = "$$tableName";
|
||||
public static final String FIELD_NAME = "$$fieldName";
|
||||
public static final String PRIMARY_KEY = "$$primaryKey";
|
||||
public static final String PRIMARY_VALUE = "$$primaryValue";
|
||||
public static final String VALUE = "$$value";
|
||||
|
||||
public static final String QUERY = "$$query";
|
||||
public static final String ROW = "$$row";
|
||||
@ -39,7 +41,6 @@ public class FlexConsts {
|
||||
public static final String IGNORE_NULLS = "$$ignoreNulls";
|
||||
|
||||
public static final String METHOD_INSERT_BATCH = "insertBatch";
|
||||
public static final String METHOD_SELECT_LIST_BY_QUERY_AS = "selectListByQueryAs";
|
||||
|
||||
/**
|
||||
* 当 entity 使用逻辑删除时,0 为 entity 的正常状态
|
||||
|
||||
@ -70,6 +70,8 @@ public interface IDialect {
|
||||
|
||||
String forUpdateEntityByQuery(TableInfo tableInfo, Object entity, boolean ignoreNulls, QueryWrapper queryWrapper);
|
||||
|
||||
String forUpdateNumberAddByQuery(String tableName, String fieldName, Number value, QueryWrapper queryWrapper);
|
||||
|
||||
String forSelectOneEntityById(TableInfo tableInfo);
|
||||
|
||||
String forSelectEntityListByIds(TableInfo tableInfo, Object[] primaryValues);
|
||||
|
||||
@ -667,6 +667,33 @@ public class CommonsDialectImpl implements IDialect {
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String forUpdateNumberAddByQuery(String tableName, String fieldName, Number value, QueryWrapper queryWrapper) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
sql.append("UPDATE ").append(forHint(CPI.getHint(queryWrapper))).append(wrap(tableName)).append(" SET ");
|
||||
sql.append(wrap(fieldName)).append("=").append(wrap(fieldName)).append(value.intValue() >= 0 ? " + " : " - ").append(Math.abs(value.longValue()));
|
||||
|
||||
String whereConditionSql = buildWhereConditionSql(queryWrapper);
|
||||
|
||||
//不允许全量更新
|
||||
if (StringUtil.isBlank(whereConditionSql)) {
|
||||
throw new IllegalArgumentException("Not allowed UPDATE a table without where condition.");
|
||||
}
|
||||
|
||||
sql.append(" WHERE ").append(whereConditionSql);
|
||||
|
||||
List<String> endFragments = CPI.getEndFragments(queryWrapper);
|
||||
if (CollectionUtil.isNotEmpty(endFragments)) {
|
||||
for (String endFragment : endFragments) {
|
||||
sql.append(" ").append(endFragment);
|
||||
}
|
||||
}
|
||||
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String forSelectOneEntityById(TableInfo tableInfo) {
|
||||
StringBuilder sql = buildSelectColumnSql(null, null, null);
|
||||
|
||||
@ -22,7 +22,7 @@ import java.io.Serializable;
|
||||
public class FieldQuery implements Serializable {
|
||||
|
||||
private String field;
|
||||
private Class<?> mappingType;
|
||||
// private Class<?> mappingType;
|
||||
private QueryWrapper queryWrapper;
|
||||
|
||||
public String getField() {
|
||||
@ -33,13 +33,13 @@ public class FieldQuery implements Serializable {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public Class<?> getMappingType() {
|
||||
return mappingType;
|
||||
}
|
||||
|
||||
public void setMappingType(Class<?> mappingType) {
|
||||
this.mappingType = mappingType;
|
||||
}
|
||||
// public Class<?> getMappingType() {
|
||||
// return mappingType;
|
||||
// }
|
||||
//
|
||||
// public void setMappingType(Class<?> mappingType) {
|
||||
// this.mappingType = mappingType;
|
||||
// }
|
||||
|
||||
public QueryWrapper getQueryWrapper() {
|
||||
return queryWrapper;
|
||||
|
||||
@ -38,10 +38,10 @@ public class FieldQueryBuilder<T> implements Serializable {
|
||||
return field(LambdaUtil.getFieldName(fn));
|
||||
}
|
||||
|
||||
public FieldQueryBuilder<T> type(Class<?> mappingType){
|
||||
fieldQuery.setMappingType(mappingType);
|
||||
return this;
|
||||
}
|
||||
// public FieldQueryBuilder<T> type(Class<?> mappingType){
|
||||
// fieldQuery.setMappingType(mappingType);
|
||||
// return this;
|
||||
// }
|
||||
|
||||
public FieldQueryBuilder<T> queryWrapper(QueryBuilder<T> fun){
|
||||
fieldQuery.setQueryWrapper(fun.build(entity));
|
||||
|
||||
@ -45,31 +45,31 @@ public class FlexEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
|
||||
|
||||
@Override
|
||||
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||
Object value = rs.getObject(columnName, enumWrapper.getEnumPropertyType());
|
||||
Object value = rs.getObject(columnName, enumWrapper.getPropertyType());
|
||||
if (null == value && rs.wasNull()) {
|
||||
return null;
|
||||
}
|
||||
return enumWrapper.toEnum(value);
|
||||
return enumWrapper.getEnum(value);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
Object value = rs.getObject(columnIndex, enumWrapper.getEnumPropertyType());
|
||||
Object value = rs.getObject(columnIndex, enumWrapper.getPropertyType());
|
||||
if (null == value && rs.wasNull()) {
|
||||
return null;
|
||||
}
|
||||
return enumWrapper.toEnum(value);
|
||||
return enumWrapper.getEnum(value);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
Object value = cs.getObject(columnIndex, enumWrapper.getEnumPropertyType());
|
||||
Object value = cs.getObject(columnIndex, enumWrapper.getPropertyType());
|
||||
if (null == value && cs.wasNull()) {
|
||||
return null;
|
||||
}
|
||||
return enumWrapper.toEnum(value);
|
||||
return enumWrapper.getEnum(value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -133,9 +133,9 @@ public class FlexConfiguration extends Configuration {
|
||||
public MappedStatement getMappedStatement(String id) {
|
||||
MappedStatement ms = super.getMappedStatement(id);
|
||||
|
||||
//动态 resultsMap
|
||||
if (id.endsWith(FlexConsts.METHOD_SELECT_LIST_BY_QUERY_AS)) {
|
||||
//动态 resultsMap,方法名称为:selectListByQuery
|
||||
Class<?> asType = MappedStatementTypes.getCurrentType();
|
||||
if (asType != null) {
|
||||
return MapUtil.computeIfAbsent(dynamicMappedStatementCache, asType,
|
||||
aClass -> replaceResultMap(ms, TableInfoFactory.ofEntityClass(asType))
|
||||
);
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
package com.mybatisflex.core.mybatis;
|
||||
|
||||
import com.mybatisflex.annotation.UseDataSource;
|
||||
import com.mybatisflex.core.FlexConsts;
|
||||
import com.mybatisflex.core.FlexGlobalConfig;
|
||||
import com.mybatisflex.core.datasource.DataSourceKey;
|
||||
import com.mybatisflex.core.datasource.FlexDataSource;
|
||||
@ -46,11 +45,7 @@ public class MapperInvocationHandler implements InvocationHandler {
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
boolean clearDsKey = false;
|
||||
boolean clearDbType = false;
|
||||
boolean isSelectListByQueryAsMethod = FlexConsts.METHOD_SELECT_LIST_BY_QUERY_AS.equals(method.getName());
|
||||
try {
|
||||
if (isSelectListByQueryAsMethod){
|
||||
MappedStatementTypes.setCurrentType((Class<?>) args[1]);
|
||||
}
|
||||
//获取用户动态指定,由用户指定数据源,则应该有用户清除
|
||||
String dataSourceKey = DataSourceKey.get();
|
||||
|
||||
@ -78,9 +73,6 @@ public class MapperInvocationHandler implements InvocationHandler {
|
||||
}
|
||||
return method.invoke(mapper, args);
|
||||
} finally {
|
||||
if (isSelectListByQueryAsMethod){
|
||||
MappedStatementTypes.clear();
|
||||
}
|
||||
if (clearDbType) {
|
||||
DialectFactory.clearHintDbType();
|
||||
}
|
||||
|
||||
@ -247,6 +247,32 @@ public class EntitySqlProvider {
|
||||
return DialectFactory.getDialect().forUpdateEntityByQuery(tableInfo, entity, ignoreNulls, queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* updateNumberByQuery 的 sql 构建
|
||||
*
|
||||
* @param params
|
||||
* @param context
|
||||
* @return sql
|
||||
* @see com.mybatisflex.core.BaseMapper#updateNumberAddByQuery(String, Number, QueryWrapper)
|
||||
*/
|
||||
public static String updateNumberAddByQuery(Map params, ProviderContext context) {
|
||||
QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
|
||||
|
||||
String fieldName = ProviderUtil.getFieldName(params);
|
||||
Number value = (Number) ProviderUtil.getValue(params);
|
||||
|
||||
TableInfo tableInfo = ProviderUtil.getTableInfo(context);
|
||||
|
||||
//处理逻辑删除 和 多租户等
|
||||
tableInfo.appendConditions(null, queryWrapper);
|
||||
|
||||
Object[] queryParams = CPI.getValueArray(queryWrapper);
|
||||
|
||||
ProviderUtil.setSqlArgs(params, queryParams);
|
||||
|
||||
return DialectFactory.getDialect().forUpdateNumberAddByQuery(tableInfo.getTableName(), fieldName, value, queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* selectOneById 的 sql 构建
|
||||
@ -350,10 +376,6 @@ public class EntitySqlProvider {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private static List<TableInfo> getTableInfos(ProviderContext context, QueryWrapper queryWrapper) {
|
||||
List<TableInfo> tableInfos;
|
||||
List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
|
||||
|
||||
@ -91,6 +91,13 @@ class ProviderUtil {
|
||||
return params.get(FlexConsts.ENTITY);
|
||||
}
|
||||
|
||||
public static String getFieldName(Map params) {
|
||||
return (String) params.get(FlexConsts.FIELD_NAME);
|
||||
}
|
||||
|
||||
public static Object getValue(Map params) {
|
||||
return params.get(FlexConsts.VALUE);
|
||||
}
|
||||
|
||||
public static List<Object> getEntities(Map params) {
|
||||
return (List<Object>) params.get(FlexConsts.ENTITIES);
|
||||
|
||||
@ -85,7 +85,7 @@ public class RowSqlProvider {
|
||||
// 让所有 row 的列顺序和值的数量与第条数据保持一致
|
||||
// 这个必须 new 一个 LinkedHashSet,因为 keepModifyAttrs 会清除 row 所有的 modifyAttrs
|
||||
Set<String> modifyAttrs = new LinkedHashSet<>(rows.get(0).obtainModifyAttrs());
|
||||
rows.forEach(row -> RowCPI.keepModifyAttrs(row, modifyAttrs));
|
||||
rows.forEach(row -> row.prepareAttrs(modifyAttrs));
|
||||
|
||||
|
||||
Object[] values = new Object[]{};
|
||||
@ -245,6 +245,29 @@ public class RowSqlProvider {
|
||||
return DialectFactory.getDialect().forUpdateEntity(tableInfo, entity, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行类似 update table set field=field+1 where ... 的场景
|
||||
*
|
||||
* @param params
|
||||
* @return sql
|
||||
* @see RowMapper#updateNumberAddByQuery(String, String, Number, QueryWrapper)
|
||||
*/
|
||||
public static String updateNumberAddByQuery(Map params) {
|
||||
|
||||
QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
|
||||
|
||||
String tableName = ProviderUtil.getTableName(params);
|
||||
String fieldName = ProviderUtil.getFieldName(params);
|
||||
Number value = (Number) ProviderUtil.getValue(params);
|
||||
|
||||
|
||||
Object[] queryParams = CPI.getValueArray(queryWrapper);
|
||||
|
||||
ProviderUtil.setSqlArgs(params, queryParams);
|
||||
|
||||
return DialectFactory.getDialect().forUpdateNumberAddByQuery(tableName, fieldName, value, queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* selectOneById 的 sql 构建
|
||||
|
||||
@ -0,0 +1,133 @@
|
||||
/**
|
||||
* 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.query;
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
import com.mybatisflex.core.util.StringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ArithmeticQueryColumn extends QueryColumn {
|
||||
|
||||
private List<ArithmeticInfo> arithmeticInfos;
|
||||
|
||||
public ArithmeticQueryColumn(Object value) {
|
||||
arithmeticInfos = new ArrayList<>();
|
||||
arithmeticInfos.add(new ArithmeticInfo(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryColumn add(QueryColumn queryColumn) {
|
||||
arithmeticInfos.add(new ArithmeticInfo(" + ", queryColumn));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryColumn add(Number number) {
|
||||
arithmeticInfos.add(new ArithmeticInfo(" + ", number));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryColumn subtract(QueryColumn queryColumn) {
|
||||
arithmeticInfos.add(new ArithmeticInfo(" - ", queryColumn));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryColumn subtract(Number number) {
|
||||
arithmeticInfos.add(new ArithmeticInfo(" - ", number));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryColumn multiply(QueryColumn queryColumn) {
|
||||
arithmeticInfos.add(new ArithmeticInfo(" * ", queryColumn));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryColumn multiply(Number number) {
|
||||
arithmeticInfos.add(new ArithmeticInfo(" * ", number));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryColumn divide(QueryColumn queryColumn) {
|
||||
arithmeticInfos.add(new ArithmeticInfo(" / ", queryColumn));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryColumn divide(Number number) {
|
||||
arithmeticInfos.add(new ArithmeticInfo(" / ", number));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryColumn as(String alias) {
|
||||
this.alias = alias;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
String toSelectSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
for (int i = 0; i < arithmeticInfos.size(); i++) {
|
||||
sql.append(arithmeticInfos.get(i).toSql(queryTables, dialect, i));
|
||||
}
|
||||
if (StringUtil.isNotBlank(alias)) {
|
||||
return "(" + sql + ") AS " + dialect.wrap(alias);
|
||||
}
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
String toConditionSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
for (int i = 0; i < arithmeticInfos.size(); i++) {
|
||||
sql.append(arithmeticInfos.get(i).toSql(queryTables, dialect, i));
|
||||
}
|
||||
return "(" + sql + ")";
|
||||
}
|
||||
|
||||
|
||||
static class ArithmeticInfo {
|
||||
private String symbol;
|
||||
private Object value;
|
||||
|
||||
public ArithmeticInfo(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public ArithmeticInfo(String symbol, Object value) {
|
||||
this.symbol = symbol;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
private String toSql(List<QueryTable> queryTables, IDialect dialect, int index) {
|
||||
String valueSql;
|
||||
if (value instanceof QueryColumn) {
|
||||
valueSql = ((QueryColumn) value).toConditionSql(queryTables, dialect);
|
||||
} else {
|
||||
valueSql = String.valueOf(value);
|
||||
}
|
||||
return index == 0 ? valueSql : symbol + valueSql;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -69,7 +69,7 @@ public class FunctionQueryColumn extends QueryColumn implements HasParamsColumn
|
||||
@Override
|
||||
public String toSelectSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
String sql = column.toSelectSql(queryTables, dialect);
|
||||
return StringUtil.isBlank(sql) ? "" : fnName + "(" + sql + ")" + WrapperUtil.buildAsAlias(alias);
|
||||
return StringUtil.isBlank(sql) ? "" : fnName + "(" + sql + ")" + WrapperUtil.buildAsAlias(alias, dialect);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -324,6 +324,40 @@ public class QueryColumn implements Serializable {
|
||||
}
|
||||
|
||||
|
||||
// 运算 加减乘除 + - * /
|
||||
public QueryColumn add(QueryColumn queryColumn) {
|
||||
return new ArithmeticQueryColumn(this).add(queryColumn);
|
||||
}
|
||||
|
||||
public QueryColumn add(Number number) {
|
||||
return new ArithmeticQueryColumn(this).add(number);
|
||||
}
|
||||
|
||||
public QueryColumn subtract(QueryColumn queryColumn) {
|
||||
return new ArithmeticQueryColumn(this).subtract(queryColumn);
|
||||
}
|
||||
|
||||
public QueryColumn subtract(Number number) {
|
||||
return new ArithmeticQueryColumn(this).subtract(number);
|
||||
}
|
||||
|
||||
public QueryColumn multiply(QueryColumn queryColumn) {
|
||||
return new ArithmeticQueryColumn(this).multiply(queryColumn);
|
||||
}
|
||||
|
||||
public QueryColumn multiply(Number number) {
|
||||
return new ArithmeticQueryColumn(this).multiply(number);
|
||||
}
|
||||
|
||||
public QueryColumn divide(QueryColumn queryColumn) {
|
||||
return new ArithmeticQueryColumn(this).divide(queryColumn);
|
||||
}
|
||||
|
||||
public QueryColumn divide(Number number) {
|
||||
return new ArithmeticQueryColumn(this).divide(number);
|
||||
}
|
||||
|
||||
|
||||
protected String wrap(IDialect dialect, String table, String column) {
|
||||
if (StringUtil.isNotBlank(table)) {
|
||||
return dialect.wrap(table) + "." + dialect.wrap(column);
|
||||
@ -340,7 +374,7 @@ public class QueryColumn implements Serializable {
|
||||
|
||||
String toSelectSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
String tableName = WrapperUtil.getColumnTableName(queryTables, table);
|
||||
return wrap(dialect, tableName, name) + WrapperUtil.buildAsAlias(dialect.wrap(alias));
|
||||
return wrap(dialect, tableName, name) + WrapperUtil.buildAsAlias(alias, dialect);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -63,7 +63,7 @@ public class QueryTable implements Serializable {
|
||||
}
|
||||
|
||||
public String toSql(IDialect dialect) {
|
||||
return dialect.wrap(name) + WrapperUtil.buildAsAlias(dialect.wrap(alias));
|
||||
return dialect.wrap(name) + WrapperUtil.buildAsAlias(alias, dialect);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -57,7 +57,7 @@ public class StringFunctionQueryColumn extends QueryColumn {
|
||||
@Override
|
||||
public String toSelectSql(List<QueryTable> queryTables, IDialect dialect) {
|
||||
String sql = StringUtil.join(", ", params);
|
||||
return StringUtil.isBlank(sql) ? "" : fnName + "(" + sql + ")" + WrapperUtil.buildAsAlias(alias);
|
||||
return StringUtil.isBlank(sql) ? "" : fnName + "(" + sql + ")" + WrapperUtil.buildAsAlias(alias, dialect);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
package com.mybatisflex.core.query;
|
||||
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
import com.mybatisflex.core.util.ClassUtil;
|
||||
import com.mybatisflex.core.util.CollectionUtil;
|
||||
import com.mybatisflex.core.util.EnumWrapper;
|
||||
@ -30,8 +31,8 @@ import java.util.List;
|
||||
class WrapperUtil {
|
||||
|
||||
|
||||
static String buildAsAlias(String alias) {
|
||||
return StringUtil.isBlank(alias) ? "" : " AS " + alias;
|
||||
static String buildAsAlias(String alias, IDialect dialect) {
|
||||
return StringUtil.isBlank(alias) ? "" : " AS " + dialect.wrap(alias);
|
||||
}
|
||||
|
||||
static final Object[] NULL_PARA_ARRAY = new Object[0];
|
||||
|
||||
@ -286,6 +286,7 @@ public class Db {
|
||||
})).sum();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据主键来批量更新数据
|
||||
*
|
||||
@ -297,6 +298,11 @@ public class Db {
|
||||
}
|
||||
|
||||
|
||||
public static int updateNumberAddByQuery(String tableName, String fieldName, Number value, QueryWrapper queryWrapper){
|
||||
return invoker().updateNumberAddByQuery(tableName, fieldName, value, queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 批量执行工具方法
|
||||
*
|
||||
|
||||
@ -316,8 +316,29 @@ public class Row extends LinkedHashMap<String, Object> implements ModifyAttrsRec
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void prepareAttrsByKeySet(){
|
||||
this.modifyAttrs.clear();
|
||||
this.modifyAttrs.addAll(keySet());
|
||||
|
||||
void keepModifyAttrs(Collection<String> attrs) {
|
||||
if (this.primaryKeys != null){
|
||||
for (RowKey primaryKey : primaryKeys) {
|
||||
this.modifyAttrs.removeIf(s -> s.equalsIgnoreCase(primaryKey.getKeyColumn()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void prepareAttrsByKeySet(RowKey ... primaryKeys){
|
||||
this.modifyAttrs.clear();
|
||||
this.modifyAttrs.addAll(keySet());
|
||||
this.primaryKeys = primaryKeys;
|
||||
|
||||
for (RowKey primaryKey : primaryKeys) {
|
||||
this.modifyAttrs.removeIf(s -> s.equalsIgnoreCase(primaryKey.getKeyColumn()));
|
||||
}
|
||||
}
|
||||
|
||||
public void prepareAttrs(Collection<String> attrs) {
|
||||
if (attrs == null) {
|
||||
throw new NullPointerException("attrs is null.");
|
||||
}
|
||||
@ -325,6 +346,17 @@ public class Row extends LinkedHashMap<String, Object> implements ModifyAttrsRec
|
||||
modifyAttrs.addAll(attrs);
|
||||
}
|
||||
|
||||
public RowKey[] getPrimaryKeys() {
|
||||
return primaryKeys;
|
||||
}
|
||||
|
||||
public void setPrimaryKeys(RowKey... primaryKeys) {
|
||||
this.primaryKeys = primaryKeys;
|
||||
for (RowKey primaryKey : primaryKeys) {
|
||||
this.modifyAttrs.removeIf(s -> s.equalsIgnoreCase(primaryKey.getKeyColumn()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取修改的值,值需要保持顺序,返回的内容不包含主键的值
|
||||
*/
|
||||
|
||||
@ -15,17 +15,11 @@
|
||||
*/
|
||||
package com.mybatisflex.core.row;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* cross package invoker
|
||||
*/
|
||||
public class RowCPI {
|
||||
|
||||
public static void keepModifyAttrs(Row row, Collection<String> attrs) {
|
||||
row.keepModifyAttrs(attrs);
|
||||
}
|
||||
|
||||
public static Object[] obtainModifyValues(Row row) {
|
||||
return row.obtainModifyValues();
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
package com.mybatisflex.core.row;
|
||||
|
||||
import com.mybatisflex.annotation.KeyType;
|
||||
import com.mybatisflex.core.keygen.KeyGenerators;
|
||||
import com.mybatisflex.core.util.SqlUtil;
|
||||
|
||||
/**
|
||||
@ -26,12 +27,22 @@ public class RowKey {
|
||||
/**
|
||||
* 自增 ID
|
||||
*/
|
||||
public static final RowKey ID_AUTO = RowKey.of("id", KeyType.Auto, null, false);
|
||||
public static final RowKey AUTO = RowKey.of("id", KeyType.Auto, null, false);
|
||||
|
||||
/**
|
||||
* UUID 的 ID
|
||||
*/
|
||||
public static final RowKey ID_UUID = RowKey.of("id", KeyType.Generator, "uuid", true);
|
||||
public static final RowKey UUID = RowKey.of("id", KeyType.Generator, KeyGenerators.uuid, true);
|
||||
|
||||
/**
|
||||
* flexId
|
||||
*/
|
||||
public static final RowKey FLEX_ID = RowKey.of("id", KeyType.Generator, KeyGenerators.flexId, true);
|
||||
|
||||
/**
|
||||
* snowFlakeId
|
||||
*/
|
||||
public static final RowKey SNOW_FLAKE_ID = RowKey.of("id", KeyType.Generator, KeyGenerators.snowFlakeId, true);
|
||||
|
||||
|
||||
public static RowKey of(String keyColumn) {
|
||||
|
||||
@ -198,6 +198,20 @@ public interface RowMapper {
|
||||
@UpdateProvider(value = RowSqlProvider.class, method = "updateEntity")
|
||||
int updateEntity(@Param(FlexConsts.ENTITY) Object entity);
|
||||
|
||||
|
||||
/**
|
||||
* 执行类似 update table set field=field+1 where ... 的场景
|
||||
*
|
||||
* @param fieldName 字段名
|
||||
* @param value 值( >=0 加,小于 0 减)
|
||||
* @param queryWrapper 条件
|
||||
* @see RowSqlProvider#updateNumberAddByQuery(Map)
|
||||
*/
|
||||
@UpdateProvider(type = RowSqlProvider.class, method = "updateNumberAddByQuery")
|
||||
int updateNumberAddByQuery(@Param(FlexConsts.TABLE_NAME) String tableName, @Param(FlexConsts.FIELD_NAME) String fieldName
|
||||
, @Param(FlexConsts.VALUE) Number value, @Param(FlexConsts.QUERY) QueryWrapper queryWrapper);
|
||||
|
||||
|
||||
///////select /////
|
||||
|
||||
/**
|
||||
|
||||
@ -185,4 +185,7 @@ public class RowMapperInvoker {
|
||||
}
|
||||
|
||||
|
||||
public int updateNumberAddByQuery(String tableName, String fieldName, Number value, QueryWrapper queryWrapper) {
|
||||
return execute(mapper -> mapper.updateNumberAddByQuery(tableName, fieldName, value, queryWrapper));
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,6 +133,9 @@ public class RowUtil {
|
||||
|
||||
|
||||
public static void printPretty(Row row) {
|
||||
if (row == null) {
|
||||
return;
|
||||
}
|
||||
printPretty(Collections.singletonList(row));
|
||||
}
|
||||
|
||||
|
||||
@ -266,6 +266,10 @@ public class TableInfo {
|
||||
return columnInfoList;
|
||||
}
|
||||
|
||||
public String getColumnByProperty(String property) {
|
||||
return propertyColumnMapping.get(property);
|
||||
}
|
||||
|
||||
|
||||
void setColumnInfoList(List<ColumnInfo> columnInfoList) {
|
||||
this.columnInfoList = columnInfoList;
|
||||
|
||||
@ -67,7 +67,7 @@ public class ClassUtil {
|
||||
}
|
||||
|
||||
|
||||
public static Class<?> wrap(Class<?> clazz) {
|
||||
public static Class<?> getWrapType(Class<?> clazz) {
|
||||
if (clazz == null || !clazz.isPrimitive()) {
|
||||
return clazz;
|
||||
}
|
||||
@ -181,17 +181,23 @@ public class ClassUtil {
|
||||
|
||||
public static List<Field> getAllFields(Class<?> cl) {
|
||||
List<Field> fields = new ArrayList<>();
|
||||
doGetFields(cl, fields, null);
|
||||
doGetFields(cl, fields, null, false);
|
||||
return fields;
|
||||
}
|
||||
|
||||
public static List<Field> getAllFields(Class<?> cl, Predicate<Field> predicate) {
|
||||
List<Field> fields = new ArrayList<>();
|
||||
doGetFields(cl, fields, predicate);
|
||||
doGetFields(cl, fields, predicate, false);
|
||||
return fields;
|
||||
}
|
||||
|
||||
private static void doGetFields(Class<?> cl, List<Field> fields, Predicate<Field> predicate) {
|
||||
public static Field getFirstField(Class<?> cl, Predicate<Field> predicate) {
|
||||
List<Field> fields = new ArrayList<>();
|
||||
doGetFields(cl, fields, predicate, true);
|
||||
return fields.isEmpty() ? null : fields.get(0);
|
||||
}
|
||||
|
||||
private static void doGetFields(Class<?> cl, List<Field> fields, Predicate<Field> predicate, boolean firstOnly) {
|
||||
if (cl == null || cl == Object.class) {
|
||||
return;
|
||||
}
|
||||
@ -200,26 +206,39 @@ public class ClassUtil {
|
||||
for (Field declaredField : declaredFields) {
|
||||
if (predicate == null || predicate.test(declaredField)) {
|
||||
fields.add(declaredField);
|
||||
if (firstOnly) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doGetFields(cl.getSuperclass(), fields, predicate);
|
||||
if (firstOnly && !fields.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
doGetFields(cl.getSuperclass(), fields, predicate, firstOnly);
|
||||
}
|
||||
|
||||
public static List<Method> getAllMethods(Class<?> cl) {
|
||||
List<Method> methods = new ArrayList<>();
|
||||
doGetMethods(cl, methods, null);
|
||||
doGetMethods(cl, methods, null, false);
|
||||
return methods;
|
||||
}
|
||||
|
||||
public static List<Method> getAllMethods(Class<?> cl, Predicate<Method> predicate) {
|
||||
List<Method> methods = new ArrayList<>();
|
||||
doGetMethods(cl, methods, predicate);
|
||||
doGetMethods(cl, methods, predicate, false);
|
||||
return methods;
|
||||
}
|
||||
|
||||
public static Method getFirstMethod(Class<?> cl, Predicate<Method> predicate) {
|
||||
List<Method> methods = new ArrayList<>();
|
||||
doGetMethods(cl, methods, predicate, true);
|
||||
return methods.isEmpty() ? null : methods.get(0);
|
||||
}
|
||||
|
||||
private static void doGetMethods(Class<?> cl, List<Method> methods, Predicate<Method> predicate) {
|
||||
|
||||
private static void doGetMethods(Class<?> cl, List<Method> methods, Predicate<Method> predicate, boolean firstOnly) {
|
||||
if (cl == null || cl == Object.class) {
|
||||
return;
|
||||
}
|
||||
@ -228,10 +247,17 @@ public class ClassUtil {
|
||||
for (Method method : declaredMethods) {
|
||||
if (predicate == null || predicate.test(method)) {
|
||||
methods.add(method);
|
||||
if (firstOnly) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doGetMethods(cl.getSuperclass(), methods, predicate);
|
||||
if (firstOnly && !methods.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
doGetMethods(cl.getSuperclass(), methods, predicate, firstOnly);
|
||||
}
|
||||
|
||||
private static <T> Class<T> getJdkProxySuperClass(Class<T> clazz) {
|
||||
|
||||
@ -94,7 +94,7 @@ public class ConvertUtil {
|
||||
} else if (targetClass.isEnum()) {
|
||||
EnumWrapper enumWrapper = EnumWrapper.of(targetClass);
|
||||
if (enumWrapper.hasEnumValueAnnotation()) {
|
||||
return enumWrapper.toEnum(value);
|
||||
return enumWrapper.getEnum(value);
|
||||
} else if (value instanceof String) {
|
||||
return Enum.valueOf(targetClass, value.toString());
|
||||
}
|
||||
|
||||
@ -22,7 +22,6 @@ import org.apache.ibatis.util.MapUtil;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@ -30,13 +29,13 @@ public class EnumWrapper<E extends Enum<E>> {
|
||||
|
||||
private static final Map<Class, EnumWrapper> cache = new ConcurrentHashMap<>();
|
||||
|
||||
private Class<?> enumClass;
|
||||
private boolean hasEnumValueAnnotation = false;
|
||||
|
||||
private Class<?> enumPropertyType;
|
||||
private Class<?> enumClass;
|
||||
private E[] enums;
|
||||
private Field property;
|
||||
private Method getter;
|
||||
private boolean hasEnumValueAnnotation = false;
|
||||
private Class<?> propertyType;
|
||||
private Method getterMethod;
|
||||
|
||||
public static <R extends Enum<R>> EnumWrapper<R> of(Class<?> enumClass) {
|
||||
return MapUtil.computeIfAbsent(cache, enumClass, EnumWrapper::new);
|
||||
@ -44,32 +43,31 @@ public class EnumWrapper<E extends Enum<E>> {
|
||||
|
||||
public EnumWrapper(Class<E> enumClass) {
|
||||
this.enumClass = enumClass;
|
||||
this.enums = enumClass.getEnumConstants();
|
||||
|
||||
List<Field> allFields = ClassUtil.getAllFields(enumClass, field -> field.getAnnotation(EnumValue.class) != null);
|
||||
if (!allFields.isEmpty()) {
|
||||
Field enumValueField = ClassUtil.getFirstField(enumClass, field -> field.getAnnotation(EnumValue.class) != null);
|
||||
if (enumValueField != null) {
|
||||
hasEnumValueAnnotation = true;
|
||||
}
|
||||
|
||||
if (hasEnumValueAnnotation) {
|
||||
Field field = allFields.get(0);
|
||||
String getterMethodName = "get" + StringUtil.firstCharToUpperCase(enumValueField.getName());
|
||||
|
||||
String fieldGetterName = "get" + StringUtil.firstCharToUpperCase(field.getName());
|
||||
List<Method> allMethods = ClassUtil.getAllMethods(enumClass, method -> {
|
||||
Method getter = ClassUtil.getFirstMethod(enumClass, method -> {
|
||||
String methodName = method.getName();
|
||||
return methodName.equals(fieldGetterName);
|
||||
return methodName.equals(getterMethodName) && Modifier.isPublic(method.getModifiers());
|
||||
});
|
||||
|
||||
enumPropertyType = ClassUtil.wrap(field.getType());
|
||||
enums = enumClass.getEnumConstants();
|
||||
propertyType = ClassUtil.getWrapType(enumValueField.getType());
|
||||
|
||||
if (allMethods.isEmpty()) {
|
||||
if (Modifier.isPublic(field.getModifiers())) {
|
||||
property = field;
|
||||
if (getter == null) {
|
||||
if (Modifier.isPublic(enumValueField.getModifiers())) {
|
||||
property = enumValueField;
|
||||
} else {
|
||||
throw new IllegalStateException("Can not find \"" + fieldGetterName + "()\" method in enum: " + enumClass.getName());
|
||||
throw new IllegalStateException("Can not find method \"" + getterMethodName + "()\" in enum: " + enumClass.getName());
|
||||
}
|
||||
} else {
|
||||
getter = allMethods.get(0);
|
||||
this.getterMethod = getter;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -77,16 +75,14 @@ public class EnumWrapper<E extends Enum<E>> {
|
||||
|
||||
public Object getEnumValue(E object) {
|
||||
try {
|
||||
return getter != null
|
||||
? getter.invoke(object)
|
||||
: property.get(object);
|
||||
return getterMethod != null ? getterMethod.invoke(object) : property.get(object);
|
||||
} catch (Exception e) {
|
||||
throw FlexExceptions.wrap(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public E toEnum(Object value) {
|
||||
public E getEnum(Object value) {
|
||||
for (E e : enums) {
|
||||
if (value.equals(getEnumValue(e))) {
|
||||
return e;
|
||||
@ -95,12 +91,12 @@ public class EnumWrapper<E extends Enum<E>> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Class<?> getEnumClass() {
|
||||
return enumClass;
|
||||
public boolean hasEnumValueAnnotation() {
|
||||
return hasEnumValueAnnotation;
|
||||
}
|
||||
|
||||
public Class<?> getEnumPropertyType() {
|
||||
return enumPropertyType;
|
||||
public Class<?> getEnumClass() {
|
||||
return enumClass;
|
||||
}
|
||||
|
||||
public E[] getEnums() {
|
||||
@ -111,11 +107,11 @@ public class EnumWrapper<E extends Enum<E>> {
|
||||
return property;
|
||||
}
|
||||
|
||||
public Method getGetter() {
|
||||
return getter;
|
||||
public Class<?> getPropertyType() {
|
||||
return propertyType;
|
||||
}
|
||||
|
||||
public boolean hasEnumValueAnnotation() {
|
||||
return hasEnumValueAnnotation;
|
||||
public Method getGetterMethod() {
|
||||
return getterMethod;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,107 @@
|
||||
/**
|
||||
* 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.util;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class FieldWrapper {
|
||||
|
||||
public static Map<Class<?>, Map<String, FieldWrapper>> cache = new ConcurrentHashMap<>();
|
||||
|
||||
private Class<?> fieldType;
|
||||
private Class<?> mappingType;
|
||||
private Method setterMethod;
|
||||
|
||||
public static FieldWrapper of(Class<?> clazz, String fieldName) {
|
||||
Map<String, FieldWrapper> wrapperMap = cache.get(clazz);
|
||||
if (wrapperMap == null) {
|
||||
synchronized (clazz) {
|
||||
if (wrapperMap == null) {
|
||||
wrapperMap = new ConcurrentHashMap<>();
|
||||
cache.put(clazz, wrapperMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FieldWrapper fieldWrapper = wrapperMap.get(fieldName);
|
||||
if (fieldWrapper == null) {
|
||||
synchronized (clazz) {
|
||||
fieldWrapper = wrapperMap.get(fieldName);
|
||||
if (fieldWrapper == null) {
|
||||
Field findField = ClassUtil.getFirstField(clazz, field -> field.getName().equals(fieldName));
|
||||
if (findField == null) {
|
||||
throw new IllegalStateException("Can not find field \"" + fieldName + "\" in class: " + clazz);
|
||||
}
|
||||
|
||||
Method setter = ClassUtil.getFirstMethod(clazz, method ->
|
||||
method.getParameterCount() == 1
|
||||
&& Modifier.isPublic(method.getModifiers())
|
||||
&& method.getName().equals("set" + StringUtil.firstCharToUpperCase(fieldName)));
|
||||
|
||||
if (setter == null) {
|
||||
throw new IllegalStateException("Can not find method \"set" + StringUtil.firstCharToUpperCase(fieldName) + "\" in class: " + clazz);
|
||||
}
|
||||
|
||||
fieldWrapper = new FieldWrapper();
|
||||
fieldWrapper.fieldType = findField.getType();
|
||||
fieldWrapper.mappingType = parseMappingType(findField);
|
||||
fieldWrapper.setterMethod = setter;
|
||||
|
||||
wrapperMap.put(fieldName, fieldWrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fieldWrapper;
|
||||
}
|
||||
|
||||
private static Class<?> parseMappingType(Field field) {
|
||||
Class<?> fieldType = field.getType();
|
||||
if (Collection.class.isAssignableFrom(fieldType)) {
|
||||
Type genericType = field.getGenericType();
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
Type actualTypeArgument = ((ParameterizedType) genericType).getActualTypeArguments()[0];
|
||||
return (Class<?>) actualTypeArgument;
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldType.isArray()) {
|
||||
return field.getType().getComponentType();
|
||||
}
|
||||
|
||||
return fieldType;
|
||||
}
|
||||
|
||||
|
||||
public void set(Object value, Object to) {
|
||||
try {
|
||||
setterMethod.invoke(to, value);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Class<?> getFieldType() {
|
||||
return fieldType;
|
||||
}
|
||||
|
||||
public Class<?> getMappingType() {
|
||||
return mappingType;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,148 @@
|
||||
/**
|
||||
* 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.coretest.query;
|
||||
|
||||
import com.mybatisflex.core.dialect.IDialect;
|
||||
import com.mybatisflex.core.dialect.impl.CommonsDialectImpl;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.mybatisflex.core.query.QueryMethods.sum;
|
||||
import static com.mybatisflex.coretest.table.AccountTableDef.ACCOUNT;
|
||||
|
||||
public class ArithmeticQueryColumnTest {
|
||||
|
||||
private static String toSql(QueryWrapper queryWrapper){
|
||||
IDialect dialect = new CommonsDialectImpl();
|
||||
return dialect.forSelectByQuery(queryWrapper);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testAdd() {
|
||||
QueryWrapper query = new QueryWrapper()
|
||||
.select(ACCOUNT.ID.add(100).as("x100"))
|
||||
.from(ACCOUNT);
|
||||
|
||||
String sql = toSql(query);
|
||||
System.out.println(sql);
|
||||
|
||||
Assert.assertEquals(sql,"SELECT (`id` + 100) AS `x100` FROM `tb_account`");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd1() {
|
||||
QueryWrapper query = new QueryWrapper()
|
||||
.select(ACCOUNT.ID.add(100).add(200).add(300).as("x100"))
|
||||
.from(ACCOUNT);
|
||||
|
||||
String sql = toSql(query);
|
||||
System.out.println(sql);
|
||||
|
||||
Assert.assertEquals(sql,"SELECT (`id` + 100 + 200 + 300) AS `x100` FROM `tb_account`");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd2() {
|
||||
QueryWrapper query = new QueryWrapper()
|
||||
.select(ACCOUNT.ID.add(ACCOUNT.ID).as("x100"))
|
||||
.from(ACCOUNT);
|
||||
|
||||
String sql = toSql(query);
|
||||
System.out.println(sql);
|
||||
|
||||
Assert.assertEquals(sql,"SELECT (`id` + `id`) AS `x100` FROM `tb_account`");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd3() {
|
||||
QueryWrapper query = new QueryWrapper()
|
||||
.select(ACCOUNT.ID.add(ACCOUNT.ID.add(100)).as("x100"))
|
||||
.from(ACCOUNT);
|
||||
|
||||
String sql = toSql(query);
|
||||
System.out.println(sql);
|
||||
|
||||
Assert.assertEquals(sql,"SELECT (`id` + (`id` + 100)) AS `x100` FROM `tb_account`");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd4() {
|
||||
QueryWrapper query = new QueryWrapper()
|
||||
.select(ACCOUNT.ID.add(ACCOUNT.ID.add(100)).multiply(100).as("x100"))
|
||||
.from(ACCOUNT);
|
||||
|
||||
String sql = toSql(query);
|
||||
System.out.println(sql);
|
||||
|
||||
Assert.assertEquals(sql,"SELECT (`id` + (`id` + 100) * 100) AS `x100` FROM `tb_account`");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd5() {
|
||||
QueryWrapper query = new QueryWrapper()
|
||||
.select(sum(ACCOUNT.ID.multiply(ACCOUNT.AGE)).as("total_x"))
|
||||
.from(ACCOUNT);
|
||||
|
||||
String sql = toSql(query);
|
||||
System.out.println(sql);
|
||||
|
||||
Assert.assertEquals(sql,"SELECT SUM(`id` * `age`) AS `total_x` FROM `tb_account`");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubtract() {
|
||||
QueryWrapper query = new QueryWrapper()
|
||||
.select(ACCOUNT.ID.subtract(100).as("x100"))
|
||||
.from(ACCOUNT);
|
||||
|
||||
String sql = toSql(query);
|
||||
System.out.println(sql);
|
||||
|
||||
Assert.assertEquals(sql,"SELECT (`id` - 100) AS `x100` FROM `tb_account`");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMultiply() {
|
||||
QueryWrapper query = new QueryWrapper()
|
||||
.select(ACCOUNT.ID.multiply(100).as("x100"))
|
||||
.from(ACCOUNT);
|
||||
|
||||
String sql = toSql(query);
|
||||
System.out.println(sql);
|
||||
|
||||
Assert.assertEquals(sql,"SELECT (`id` * 100) AS `x100` FROM `tb_account`");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDivide() {
|
||||
QueryWrapper query = new QueryWrapper()
|
||||
.select(ACCOUNT.ID.divide(100).as("x100"))
|
||||
.from(ACCOUNT);
|
||||
|
||||
String sql = toSql(query);
|
||||
System.out.println(sql);
|
||||
|
||||
Assert.assertEquals(sql,"SELECT (`id` / 100) AS `x100` FROM `tb_account`");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-annotation</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-core</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-spring</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-core</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>mybatis-flex-test</artifactId>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-core</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@ -16,10 +16,7 @@
|
||||
package com.mybatisflex.test;
|
||||
|
||||
import com.mybatisflex.core.MybatisFlexBootstrap;
|
||||
import com.mybatisflex.core.row.BatchArgsSetter;
|
||||
import com.mybatisflex.core.row.Db;
|
||||
import com.mybatisflex.core.row.Row;
|
||||
import com.mybatisflex.core.row.RowKey;
|
||||
import com.mybatisflex.core.row.*;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||
|
||||
@ -33,7 +30,7 @@ public class DbTestStarter {
|
||||
DataSource dataSource = new EmbeddedDatabaseBuilder()
|
||||
.setType(EmbeddedDatabaseType.H2)
|
||||
.addScript("schema.sql")
|
||||
.addScript("data.sql")
|
||||
// .addScript("data.sql")
|
||||
.build();
|
||||
|
||||
MybatisFlexBootstrap.getInstance()
|
||||
@ -41,15 +38,18 @@ public class DbTestStarter {
|
||||
.start();
|
||||
|
||||
Row row1 = Db.selectOneById("tb_account", "id", 1);
|
||||
System.out.println(row1);
|
||||
RowUtil.printPretty(row1);
|
||||
|
||||
//查询全部
|
||||
List<Row> rows = Db.selectAll("tb_account");
|
||||
System.out.println(rows);
|
||||
RowUtil.printPretty(rows);
|
||||
|
||||
|
||||
//插入 1 条数据
|
||||
Row row = Row.ofKey(RowKey.ID_AUTO);
|
||||
|
||||
Row row = Row.ofKey(RowKey.AUTO);
|
||||
// Row row = new Row();
|
||||
// row.set("id", 3);
|
||||
row.set("user_name", "michael yang");
|
||||
row.set("age", 18);
|
||||
row.set("birthday", new Date());
|
||||
@ -80,6 +80,26 @@ public class DbTestStarter {
|
||||
|
||||
//再次查询全部数据
|
||||
rows = Db.selectAll("tb_account");
|
||||
System.out.println(rows);
|
||||
RowUtil.printPretty(rows);
|
||||
|
||||
// for (Row row2 : rows) {
|
||||
//// for (String s : row2.keySet()) {
|
||||
//// if (!s.equalsIgnoreCase("id")) {
|
||||
//// row2.set(s, row2.get(s));
|
||||
//// }
|
||||
//// }
|
||||
// rows.remove("id");
|
||||
// }
|
||||
|
||||
// rows.forEach(row2 -> row2.setPrimaryKeys(RowKey.AUTO));
|
||||
rows.forEach(r -> {
|
||||
r.prepareAttrsByKeySet();
|
||||
r.setPrimaryKeys(RowKey.AUTO);
|
||||
});
|
||||
Db.insertBatch("tb_account", rows, 100);
|
||||
|
||||
//再次查询全部数据
|
||||
rows = Db.selectAll("tb_account");
|
||||
RowUtil.printPretty(rows);
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,15 +71,22 @@ public class EntityTestStarter {
|
||||
RowUtil.printPretty(rowList);
|
||||
|
||||
|
||||
// List<Account> accounts1 = accountMapper.selectListByQuery(QueryWrapper.create()
|
||||
// , accountFieldQueryBuilder -> accountFieldQueryBuilder
|
||||
// .field(Account::getArticles)
|
||||
// .type(Article.class)
|
||||
// .queryWrapper(entity ->
|
||||
// select().from(ARTICLE).where(ARTICLE.ACCOUNT_ID.eq(entity.getId()))
|
||||
// )
|
||||
// );
|
||||
// System.out.println(accounts1);
|
||||
accountMapper.updateNumberAddByQuery("age", 100, QueryWrapper.create().where(ACCOUNT.ID.eq(1)));
|
||||
accountMapper.updateNumberAddByQuery(Account::getAge, -50, QueryWrapper.create().where(ACCOUNT.ID.eq(1)));
|
||||
|
||||
|
||||
Db.updateNumberAddByQuery("tb_account", "age", 30, QueryWrapper.create().where(ACCOUNT.ID.eq(1)));
|
||||
Db.updateNumberAddByQuery("tb_account", "age", -20, QueryWrapper.create().where(ACCOUNT.ID.eq(1)));
|
||||
|
||||
|
||||
List<Account> accounts1 = accountMapper.selectListByQuery(QueryWrapper.create()
|
||||
, accountFieldQueryBuilder -> accountFieldQueryBuilder
|
||||
.field(Account::getArticles)
|
||||
.queryWrapper(entity ->
|
||||
select().from(ARTICLE).where(ARTICLE.ACCOUNT_ID.eq(entity.getId()))
|
||||
)
|
||||
);
|
||||
System.out.println(accounts1);
|
||||
|
||||
// MyAccountMapper myAccountMapper = bootstrap.getMapper(MyAccountMapper.class);
|
||||
|
||||
|
||||
@ -75,7 +75,7 @@ public class RowTestStarter {
|
||||
|
||||
List<Row> rowList = new ArrayList<>();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Row row = Row.ofKey(RowKey.ID_AUTO);
|
||||
Row row = Row.ofKey(RowKey.AUTO);
|
||||
row.set(ACCOUNT.USER_NAME,"zhang" + i);
|
||||
row.set(ACCOUNT.AGE,18);
|
||||
// row.set(ACCOUNT.BIRTHDAY,new Date());
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>mybatis-flex-test</artifactId>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-spring-boot-starter</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>mybatis-flex-test</artifactId>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -20,13 +20,13 @@
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-core</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-spring</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -59,7 +59,7 @@
|
||||
<path>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-processor</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.3.2</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user