Merge branch 'mybatis-flex:main' into main

This commit is contained in:
Jiabin Zhuang 2023-05-29 08:42:43 +08:00 committed by GitHub
commit b50f59480a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 936 additions and 299 deletions

View File

@ -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 处理器

View File

@ -11,6 +11,7 @@ export default defineConfig({
// logo: '/assets/images/logo02.png',
themeConfig: {
outlineTitle:'章节',
search: {
provider: 'local'
},

View File

@ -1,16 +1,19 @@
<!--.vitepress/theme/MyLayout.vue-->
<script setup>
import DefaultTheme from 'vitepress/theme'
const { Layout } = DefaultTheme
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>

View File

@ -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 为 falseentity 的所有 null 属性都会被更新到数据库。
- **updateNumberAddByQuery(fieldName,value,queryWrapper)**:通过 `update table set field = field + 1 where ... ` 的这种方向更新数据库某个字段内容。
## 部分字段更新

View File

@ -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` 对象里的属性,需要二次查询赋值的,都是可以通过这种方式进行。

View File

@ -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 提示

View File

@ -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

View File

@ -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}")

View File

@ -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>

View File

@ -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'
}
```

View File

@ -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>
```

View File

@ -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>

View File

@ -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>

View File

@ -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();

View File

@ -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>

View File

@ -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);
}
});
}
}

View File

@ -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 的正常状态

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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));

View File

@ -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);
}

View File

@ -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))
);

View File

@ -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();
}

View File

@ -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);

View File

@ -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);

View File

@ -82,10 +82,10 @@ public class RowSqlProvider {
throw FlexExceptions.wrap("rows can not be null or empty.");
}
//让所有 row 的列顺序和值的数量与第条数据保持一致
//这个必须 new 一个 LinkedHashSet因为 keepModifyAttrs 会清除 row 所有的 modifyAttrs
// 让所有 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 构建

View File

@ -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;
}
}
}

View File

@ -60,7 +60,7 @@ public class FunctionQueryColumn extends QueryColumn implements HasParamsColumn
@Override
public Object[] getParamValues() {
if (column instanceof HasParamsColumn){
if (column instanceof HasParamsColumn) {
return ((HasParamsColumn) column).getParamValues();
}
return WrapperUtil.NULL_PARA_ARRAY;
@ -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

View File

@ -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

View File

@ -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

View File

@ -30,7 +30,7 @@ public class StringFunctionQueryColumn extends QueryColumn {
protected String fnName;
protected List<String> params;
public StringFunctionQueryColumn(String fnName, String ...params) {
public StringFunctionQueryColumn(String fnName, String... params) {
SqlUtil.keepColumnSafely(fnName);
this.fnName = fnName;
this.params = Arrays.asList(params);
@ -56,13 +56,13 @@ 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);
String sql = StringUtil.join(", ", params);
return StringUtil.isBlank(sql) ? "" : fnName + "(" + sql + ")" + WrapperUtil.buildAsAlias(alias, dialect);
}
@Override
String toConditionSql(List<QueryTable> queryTables, IDialect dialect) {
String sql = StringUtil.join(", ",params);
String sql = StringUtil.join(", ", params);
return StringUtil.isBlank(sql) ? "" : fnName + "(" + sql + ")";
}

View File

@ -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];

View File

@ -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);
}
/**
* 批量执行工具方法
*

View File

@ -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()));
}
}
/**
* 获取修改的值值需要保持顺序返回的内容不包含主键的值
*/

View File

@ -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();
}

View File

@ -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) {

View File

@ -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 /////
/**

View File

@ -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));
}
}

View File

@ -133,6 +133,9 @@ public class RowUtil {
public static void printPretty(Row row) {
if (row == null) {
return;
}
printPretty(Collections.singletonList(row));
}

View File

@ -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;

View File

@ -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) {

View File

@ -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());
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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`");
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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());

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -7,7 +7,7 @@
<groupId>com.mybatis-flex</groupId>
<artifactId>parent</artifactId>
<packaging>pom</packaging>
<version>1.3.0</version>
<version>1.3.2</version>
<name>mybatis-flex</name>
<url>https://mybatis-flex.com</url>