diff --git a/changes.txt b/changes.txt index c2dd2e0e..de64115c 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,14 @@ +mybatis-flex v1.3.4 20230601: +新增:添加 Spring 下的 CacheableServiceImpl 实现类,方便用户缓存。感谢 @王帅 +优化:重构 IService,使之有更好的方法服用。感谢 @王帅 +优化:重命名 QueryWrapper 的 "toDebugSQL()" 方法名为 "toSQL()" +修复:当使用 QueryWrapper 未指定 from 时,schema 出错的问题 +修复:当配置 config-location 时,出现 IllegalArgumentException 错误的问题 +修复:case when else 中出现的 column 无法转换的问题 #I7A0B0 +文档:添加关于 CacheableServiceImpl 使用的一些文档。感谢 @王帅 + + + mybatis-flex v1.3.3 20230530: 新增:添加动态表名和动态 Schema 的支持 #I6VRMF #I6WS3E #I72X21 新增:添加 @Table(schema=xxx) 配置的支持 #I6VD6U diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 569da4b2..eaf71c45 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -78,6 +78,7 @@ export default defineConfig({ {text: '乐观锁', link: '/zh/core/version'}, {text: '数据填充', link: '/zh/core/fill'}, {text: '数据脱敏', link: '/zh/core/mask'}, + {text: '数据缓存', link: '/zh/core/data-cache'}, {text: 'SQL 审计', link: '/zh/core/audit'}, {text: 'SQL 打印', link: '/zh/core/sql-print'}, {text: '多数据源', link: '/zh/core/multi-datasource'}, diff --git a/docs/zh/core/data-cache.md b/docs/zh/core/data-cache.md new file mode 100644 index 00000000..73f1c512 --- /dev/null +++ b/docs/zh/core/data-cache.md @@ -0,0 +1,170 @@ +# 数据缓存 + +MyBatis-Flex 是一个 MyBatis 增强框架,所以您可以使用 MyBatis 提供的二级缓存来作为数据缓存。但是它仍然有很多的缺点,比如不适用于分布式环境,在这里推荐使用 [Spring Cache](https://docs.spring.io/spring-framework/docs/5.2.24.RELEASE/spring-framework-reference/integration.html#cache) 模块来处理数据缓存。 + +## 使用方法 + +因为要用到 Spring Cache 模块,所以您的项目必须要使用 Spring Framework 框架,这里以 Spring Boot 项目作为例子,实现 MyBatis-Flex 项目将缓存数据存入 Redis 组件中。 + +1、引入 `spring-boot-starter-cache` 和 `spring-boot-starter-data-redis`模块 + +```xml + + + org.springframework.boot + spring-boot-starter-cache + + + org.springframework.boot + spring-boot-starter-data-redis + + +``` + +2、设置 Redis 连接信息(全是默认的话可以跳过这步) + +```yaml +spring: + redis: + port: 6379 + host: localhost +``` + +3、在 Spring Boot 配置类上启用 Spring Cache 缓存 + +```java +@EnableCaching +@Configuration +public class CacheConfig { +} +``` + +4、将 ServiceImpl 默认实现类换为 CacheableServiceImpl 实现类 + +```java +public interface AccountService extends IService { +} + +@Service +public class AccountServiceImpl extends CacheableServiceImpl implements AccountService { +} +``` + +5、最后即可使用 Spring Cache 的相关注解实现数据缓存到 Redis 中了 + +```java +// 设置统一的缓存名称 +@Service +@CacheConfig(cacheNames = "account") +public class AccountServiceImpl extends CacheableServiceImpl implements AccountService { + + // 根据主键缓存数据 + @Override + @Cacheable(key = "#id") + public Account getById(Serializable id) { + return super.getById(id); + } + + // 根据方法名加查询 SQL 语句缓存结果数据 + // 加上方法名是为了避免不同的方法使用一样的 QueryWrapper + @Override + @Cacheable(key = "#root.methodName + ':' + #query.toSQL()") + public List list(QueryWrapper query) { + return super.list(query); + } + +} +``` + +## 使用说明 + +MyBatis-Flex 在 IService 接口中做了方法调用链优化,所以您只需将缓存注解加到一些特定的方法上,即可实现所有相关的方法也可以进行数据缓存。相关方法见如下示例: + +```java +@Service +@CacheConfig(cacheNames = "account") +public class AccountServiceImpl extends CacheableServiceImpl { + + @Override + @CacheEvict(allEntries = true) + public boolean remove(QueryWrapper query) { + return super.remove(query); + } + + @Override + @CacheEvict(key = "#id") + public boolean removeById(Serializable id) { + return super.removeById(id); + } + + @Override + @CacheEvict(allEntries = true) + public boolean removeByIds(Collection ids) { + return super.removeByIds(ids); + } + + @Override + @CachePut(key = "#entity.id") + public boolean update(Account entity, QueryWrapper query) { + return super.update(entity, query); + } + + @Override + @CachePut(key = "#entity.id") + public boolean updateById(Account entity) { + return super.updateById(entity); + } + + @Override + @Cacheable(key = "#id") + public Account getById(Serializable id) { + return super.getById(id); + } + + @Override + @Cacheable(key = "#root.methodName + ':' + #query.toSQL()") + public Account getOne(QueryWrapper query) { + return super.getOne(query); + } + + @Override + @Cacheable(key = "#root.methodName + ':' + #query.toSQL()") + public R getOneAs(QueryWrapper query, Class asType) { + return super.getOneAs(query, asType); + } + + @Override + @Cacheable(key = "#root.methodName + ':' + #query.toSQL()") + public List list(QueryWrapper query) { + return super.list(query); + } + + @Override + @Cacheable(key = "#root.methodName + ':' + #query.toSQL()") + public List listAs(QueryWrapper query, Class asType) { + return super.listAs(query, asType); + } + + // 无法通过注解进行缓存操作 + @Override + @Deprecated + public List listByIds(Collection ids) { + return super.listByIds(ids); + } + + @Override + @Cacheable(key = "#root.methodName + ':' + #query.toSQL()") + public long count(QueryWrapper query) { + return super.count(query); + } + + @Override + @Cacheable(key = "#root.methodName + ':' + #query.toSQL()") + public Page page(Page page, QueryWrapper query) { + return super.page(page, query); + } + +} +``` + + diff --git a/docs/zh/intro/comparison.md b/docs/zh/intro/comparison.md index 23fbb825..7a79ce0e 100644 --- a/docs/zh/intro/comparison.md +++ b/docs/zh/intro/comparison.md @@ -37,9 +37,11 @@ MyBatis-Flex 主要是和 `MyBatis-Plus` 与 `Fluent-MyBatis` 对比,内容来 | Db + Row | ✅ | ❌ | ❌ | | Entity 监听 | ✅ | ❌ | ❌ | | 多数据源支持 | ✅ | 借助其他框架或收费 | ❌ | -| 多数据源是否支持 Spring 的事务管理,比如 `@Transactional` 注解等 | ✅ | ❌ | ❌ | +| 多数据源是否支持 Spring 的事务管理,比如 `@Transactional` 和 `TransactionTemplate` 等 | ✅ | ❌ | ❌ | | 多数据源是否支持 "非Spring" 项目 | ✅ | ❌ | ❌ | | 多租户 | ✅ | ✅ | ❌ | +| 动态表名 | ✅ | ✅ | ❌ | +| 动态 Schema | ✅ | ❌ | ❌ | > 以上内容来自第三方相关产品的官方文档或第三方平台,若有错误,欢迎纠正。 diff --git a/docs/zh/intro/getting-started.md b/docs/zh/intro/getting-started.md index b7949782..0f71dcaf 100644 --- a/docs/zh/intro/getting-started.md +++ b/docs/zh/intro/getting-started.md @@ -27,12 +27,12 @@ Maven示例: com.mybatis-flex mybatis-flex-core - 1.3.3 + 1.3.4 com.mybatis-flex mybatis-flex-processor - 1.3.3 + 1.3.4 provided ``` @@ -46,7 +46,7 @@ Gradle示例: ```groovy // file: build.gradle ext { - mybatis_flex_version = '1.3.3' + mybatis_flex_version = '1.3.4' } dependencies { implementation("com.mybatis-flex:mybatis-flex-core:${mybatis_flex_version}") diff --git a/docs/zh/intro/maven.md b/docs/zh/intro/maven.md index 250c6229..cb2bdc64 100644 --- a/docs/zh/intro/maven.md +++ b/docs/zh/intro/maven.md @@ -12,12 +12,12 @@ com.mybatis-flex mybatis-flex-core - 1.3.3 + 1.3.4 com.mybatis-flex mybatis-flex-processor - 1.3.3 + 1.3.4 provided ``` @@ -28,12 +28,12 @@ com.mybatis-flex mybatis-flex-spring - 1.3.3 + 1.3.4 com.mybatis-flex mybatis-flex-processor - 1.3.3 + 1.3.4 provided `````` @@ -44,12 +44,12 @@ com.mybatis-flex mybatis-flex-spring-boot-starter - 1.3.3 + 1.3.4 com.mybatis-flex mybatis-flex-processor - 1.3.3 + 1.3.4 provided ``` @@ -70,7 +70,7 @@ com.mybatis-flex mybatis-flex-processor - 1.3.3 + 1.3.4 diff --git a/docs/zh/others/apt.md b/docs/zh/others/apt.md index 3b464c34..3050ee9d 100644 --- a/docs/zh/others/apt.md +++ b/docs/zh/others/apt.md @@ -179,7 +179,7 @@ processor.baseMapperClass=com.domain.mapper.MyBaseMapper ``` dependencies { ... - annotationProcessor 'com.mybatis-flex:mybatis-flex-processor:1.3.3' + annotationProcessor 'com.mybatis-flex:mybatis-flex-processor:1.3.4' } ``` diff --git a/docs/zh/others/codegen.md b/docs/zh/others/codegen.md index 1c89d9c3..4ae05825 100644 --- a/docs/zh/others/codegen.md +++ b/docs/zh/others/codegen.md @@ -10,7 +10,7 @@ com.mybatis-flex mybatis-flex-codegen - 1.3.3 + 1.3.4 ``` diff --git a/mybatis-flex-annotation/pom.xml b/mybatis-flex-annotation/pom.xml index a5e5383d..4adacaf3 100644 --- a/mybatis-flex-annotation/pom.xml +++ b/mybatis-flex-annotation/pom.xml @@ -5,7 +5,7 @@ parent com.mybatis-flex - 1.3.3 + 1.3.4 4.0.0 diff --git a/mybatis-flex-codegen/pom.xml b/mybatis-flex-codegen/pom.xml index d5b2eaa1..61d6c263 100644 --- a/mybatis-flex-codegen/pom.xml +++ b/mybatis-flex-codegen/pom.xml @@ -5,7 +5,7 @@ parent com.mybatis-flex - 1.3.3 + 1.3.4 4.0.0 @@ -27,7 +27,7 @@ com.mybatis-flex mybatis-flex-spring - 1.3.3 + 1.3.4 diff --git a/mybatis-flex-core/pom.xml b/mybatis-flex-core/pom.xml index 1ac2c86b..528a8469 100644 --- a/mybatis-flex-core/pom.xml +++ b/mybatis-flex-core/pom.xml @@ -5,7 +5,7 @@ parent com.mybatis-flex - 1.3.3 + 1.3.4 4.0.0 @@ -22,13 +22,13 @@ com.mybatis-flex mybatis-flex-annotation - 1.3.3 + 1.3.4 com.mybatis-flex mybatis-flex-processor - 1.3.3 + 1.3.4 diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/FlexConsts.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/FlexConsts.java index dd584ca2..b31bd46c 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/FlexConsts.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/FlexConsts.java @@ -20,7 +20,7 @@ package com.mybatisflex.core; */ public class FlexConsts { public static final String NAME = "MyBatis-Flex"; - public static final String VERSION = "1.3.3"; + public static final String VERSION = "1.3.4"; public static final String DEFAULT_PRIMARY_FIELD = "id"; diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/CaseQueryColumn.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/CaseQueryColumn.java index 199cd774..14732f7e 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/CaseQueryColumn.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/CaseQueryColumn.java @@ -15,6 +15,7 @@ */ package com.mybatisflex.core.query; +import com.mybatisflex.core.dialect.DialectFactory; import com.mybatisflex.core.dialect.IDialect; import com.mybatisflex.core.util.ArrayUtil; import com.mybatisflex.core.util.LambdaGetter; @@ -24,7 +25,7 @@ import com.mybatisflex.core.util.StringUtil; import java.util.ArrayList; import java.util.List; -public class CaseQueryColumn extends QueryColumn implements HasParamsColumn{ +public class CaseQueryColumn extends QueryColumn implements HasParamsColumn { private List whens; private Object elseValue; @@ -83,6 +84,10 @@ public class CaseQueryColumn extends QueryColumn implements HasParamsColumn{ private String buildValue(Object value) { if (value instanceof Number || value instanceof Boolean) { return String.valueOf(value); + } else if (value instanceof RawValue) { + return ((RawValue) value).getContent(); + } else if (value instanceof QueryColumn) { + return ((QueryColumn) value).toConditionSql(null, DialectFactory.getDialect()); } else { return "'" + value + "'"; } @@ -90,10 +95,13 @@ public class CaseQueryColumn extends QueryColumn implements HasParamsColumn{ @Override public Object[] getParamValues() { - Object[] values = new Object[0]; + Object[] values = WrapperUtil.NULL_PARA_ARRAY; for (When when : whens) { values = ArrayUtil.concat(values, WrapperUtil.getValues(when.whenCondition)); } + if (elseValue instanceof HasParamsColumn){ + values = ArrayUtil.concat(values,((HasParamsColumn) elseValue).getParamValues()); + } return values; } diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/CaseSearchQueryColumn.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/CaseSearchQueryColumn.java index b170b604..77e944a7 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/CaseSearchQueryColumn.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/CaseSearchQueryColumn.java @@ -15,7 +15,9 @@ */ package com.mybatisflex.core.query; +import com.mybatisflex.core.dialect.DialectFactory; import com.mybatisflex.core.dialect.IDialect; +import com.mybatisflex.core.util.ArrayUtil; import com.mybatisflex.core.util.LambdaGetter; import com.mybatisflex.core.util.LambdaUtil; import com.mybatisflex.core.util.StringUtil; @@ -23,7 +25,7 @@ import com.mybatisflex.core.util.StringUtil; import java.util.ArrayList; import java.util.List; -public class CaseSearchQueryColumn extends QueryColumn { +public class CaseSearchQueryColumn extends QueryColumn implements HasParamsColumn { private QueryColumn queryColumn; private List whens; @@ -65,6 +67,10 @@ public class CaseSearchQueryColumn extends QueryColumn { private String buildValue(Object value) { if (value instanceof Number || value instanceof Boolean) { return String.valueOf(value); + } else if (value instanceof RawValue) { + return ((RawValue) value).getContent(); + } else if (value instanceof QueryColumn) { + return ((QueryColumn) value).toConditionSql(null, DialectFactory.getDialect()); } else { return "'" + value + "'"; } @@ -89,6 +95,17 @@ public class CaseSearchQueryColumn extends QueryColumn { return as(LambdaUtil.getFieldName(fn)); } + + @Override + public Object[] getParamValues() { + Object[] values = WrapperUtil.NULL_PARA_ARRAY; + if (elseValue instanceof HasParamsColumn) { + values = ArrayUtil.concat(values, ((HasParamsColumn) elseValue).getParamValues()); + } + return values; + } + + public static class When { private Builder builder; private Object searchValue; diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/QueryWrapper.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/QueryWrapper.java index 7413d70c..8f62ce43 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/QueryWrapper.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/QueryWrapper.java @@ -535,7 +535,7 @@ public class QueryWrapper extends BaseQueryWrapper { } - public String toDebugSQL() { + public String toSQL() { String sql = DialectFactory.getDialect().forSelectByQuery(this); return SqlUtil.replaceSqlParams(sql, getValueArray()); } diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/service/IService.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/service/IService.java index d64a4596..19652951 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/service/IService.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/service/IService.java @@ -1,26 +1,26 @@ -/** - * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). - *

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

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * 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. +/* + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * 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 + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.service; import com.mybatisflex.core.BaseMapper; +import com.mybatisflex.core.exception.FlexExceptions; import com.mybatisflex.core.paginate.Page; import com.mybatisflex.core.query.QueryCondition; import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.core.row.Db; -import com.mybatisflex.core.row.RowMapper; import com.mybatisflex.core.util.ClassUtil; import com.mybatisflex.core.util.CollectionUtil; import com.mybatisflex.core.util.SqlUtil; @@ -36,20 +36,22 @@ import java.util.*; * @author 王帅 * @since 2023-05-01 */ -@SuppressWarnings("unused") +@SuppressWarnings({"unused", "unchecked"}) public interface IService { + int DEFAULT_BATCH_SIZE = 1000; + // ===== 保存(增)操作 ===== /** - * 获取对应实体类(Entity)的基础映射类(BaseMapper)。 + *

获取对应实体类(Entity)的基础映射类(BaseMapper)。 * * @return 基础映射类(BaseMapper) */ BaseMapper getMapper(); /** - * 保存实体类对象数据。 + *

保存实体类对象数据。 * * @param entity 实体类对象 * @return {@code true} 保存成功,{@code false} 保存失败。 @@ -61,7 +63,7 @@ public interface IService { } /** - * 保存或者更新实体类对象数据。 + *

保存或者更新实体类对象数据。 * * @param entity 实体类对象 * @return {@code true} 保存或更新成功,{@code false} 保存或更新失败。 @@ -72,30 +74,30 @@ public interface IService { } /** - * 批量保存实体类对象数据。 + *

批量保存实体类对象数据。 * * @param entities 实体类对象 * @return {@code true} 保存成功,{@code false} 保存失败。 */ default boolean saveBatch(Collection entities) { - return SqlUtil.toBool(getMapper().insertBatch(new ArrayList<>(entities))); + return saveBatch(entities, DEFAULT_BATCH_SIZE); } // ===== 删除(删)操作 ===== /** - * 批量保存实体类对象数据。 + *

批量保存实体类对象数据。 * - * @param entities 实体类对象 - * @param size 每次保存切分的数量 + * @param entities 实体类对象 + * @param batchSize 每次保存切分的数量 * @return {@code true} 保存成功,{@code false} 保存失败。 */ - default boolean saveBatch(Collection entities, int size) { - return SqlUtil.toBool(getMapper().insertBatch(CollectionUtil.toList(entities), size)); + default boolean saveBatch(Collection entities, int batchSize) { + return SqlUtil.toBool(getMapper().insertBatch(CollectionUtil.toList(entities), batchSize)); } /** - * 根据查询条件删除数据。 + *

根据查询条件删除数据。 * * @param query 查询条件 * @return {@code true} 删除成功,{@code false} 删除失败。 @@ -105,17 +107,17 @@ public interface IService { } /** - * 根据查询条件删除数据。 + *

根据查询条件删除数据。 * * @param condition 查询条件 * @return {@code true} 删除成功,{@code false} 删除失败。 */ default boolean remove(QueryCondition condition) { - return SqlUtil.toBool(getMapper().deleteByCondition(condition)); + return remove(query().where(condition)); } /** - * 根据数据主键删除数据。 + *

根据数据主键删除数据。 * * @param id 数据主键 * @return {@code true} 删除成功,{@code false} 删除失败。 @@ -125,7 +127,7 @@ public interface IService { } /** - * 根据数据主键批量删除数据。 + *

根据数据主键批量删除数据。 * * @param ids 数据主键 * @return {@code true} 删除成功,{@code false} 删除失败。 @@ -140,17 +142,54 @@ public interface IService { // ===== 更新(改)操作 ===== /** - * 根据 {@link Map} 构建查询条件删除数据。 + *

根据 {@link Map} 构建查询条件删除数据。 * * @param query 查询条件 * @return {@code true} 删除成功,{@code false} 删除失败。 */ default boolean removeByMap(Map query) { - return SqlUtil.toBool(getMapper().deleteByMap(query)); + // 防止全表删除 + if (query == null || query.isEmpty()) { + throw FlexExceptions.wrap("deleteByMap is not allow empty map."); + } + return remove(query().where(query)); } /** - * 根据查询条件更新数据。 + *

根据数据主键更新数据。 + * + * @param entity 实体类对象 + * @return {@code true} 更新成功,{@code false} 更新失败。 + */ + default boolean updateById(T entity) { + return SqlUtil.toBool(getMapper().update(entity)); + } + + + /** + * 根据主键更新数据 + * + * @param entity 实体对象 + * @param ignoreNulls 是否忽略 null 值 + * @return {@code true} 更新成功,{@code false} 更新失败。 + */ + default boolean updateById(T entity, boolean ignoreNulls) { + return SqlUtil.toBool(getMapper().update(entity, ignoreNulls)); + } + + /** + *

根据 {@link Map} 构建查询条件更新数据。 + * + * @param entity 实体类对象 + * @param query 查询条件 + * @return {@code true} 更新成功,{@code false} 更新失败。 + */ + default boolean update(T entity, Map query) { + return update(entity, query().where(query)); + } + + /** + *

根据查询条件更新数据。 * * @param entity 实体类对象 * @param query 查询条件 @@ -161,69 +200,48 @@ public interface IService { } /** - * 根据查询条件更新数据。 + *

根据查询条件更新数据。 * * @param entity 实体类对象 * @param condition 查询条件 * @return {@code true} 更新成功,{@code false} 更新失败。 */ default boolean update(T entity, QueryCondition condition) { - return SqlUtil.toBool(getMapper().updateByCondition(entity, condition)); + return update(entity, query().where(condition)); } /** - * 根据 id 批量更新数据 + *

根据数据主键批量更新数据 * * @param entities 实体类对象集合 * @return boolean {@code true} 更新成功,{@code false} 更新失败。 */ default boolean updateBatch(Collection entities) { - return updateBatch(entities, RowMapper.DEFAULT_BATCH_SIZE); + return updateBatch(entities, DEFAULT_BATCH_SIZE); } /** - * 根据 id 批量更新数据 + *

根据数据主键批量更新数据 * * @param entities 实体类对象集合 * @param batchSize 每批次更新数量 - * @return boolean {@code true} 更新成功,{@code false} 更新失败。 + * @return {@code true} 更新成功,{@code false} 更新失败。 */ @SuppressWarnings("unchecked") default boolean updateBatch(Collection entities, int batchSize) { return Db.tx(() -> { final List entityList = CollectionUtil.toList(entities); - // BaseMapper 是经过 Mybatis 动态代理处理过的对象,需要获取原始 BaseMapper 类型 final Class> usefulClass = (Class>) ClassUtil.getUsefulClass(getMapper().getClass()); return SqlUtil.toBool(Arrays.stream(Db.executeBatch(entityList.size(), batchSize, usefulClass, (mapper, index) -> mapper.update(entityList.get(index)))).sum()); }); } - /** - * 根据数据主键更新数据。 - * - * @param entity 实体类对象 - * @return {@code true} 更新成功,{@code false} 更新失败。 - */ - default boolean updateById(T entity) { - return SqlUtil.toBool(getMapper().update(entity)); - } - - /** - * 根据 {@link Map} 构建查询条件更新数据。 - * - * @param entity 实体类对象 - * @param query 查询条件 - * @return {@code true} 更新成功,{@code false} 更新失败。 - */ - default boolean updateByMap(T entity, Map query) { - return SqlUtil.toBool(getMapper().updateByMap(entity, query)); - } // ===== 查询(查)操作 ===== /** - * 根据数据主键查询一条数据。 + *

根据数据主键查询一条数据。 * * @param id 数据主键 * @return 查询结果数据 @@ -233,7 +251,7 @@ public interface IService { } /** - * 根据数据主键查询一条数据。 + *

根据数据主键查询一条数据。 * * @param id 数据主键 * @return 查询结果数据 @@ -244,7 +262,7 @@ public interface IService { } /** - * 根据查询条件查询一条数据。 + *

根据查询条件查询一条数据。 * * @param query 查询条件 * @return 查询结果数据 @@ -253,9 +271,19 @@ public interface IService { return getMapper().selectOneByQuery(query); } + /** + *

根据查询条件查询一条数据。 + * + * @param query 查询条件 + * @return 查询结果数据 + * @apiNote 该方法会将查询结果封装为 {@link Optional} 类进行返回,方便链式操作。 + */ + default Optional getOneOpt(QueryWrapper query) { + return Optional.ofNullable(getOne(query)); + } /** - * 根据查询条件查询一条数据,并通过 asType 进行接收 + *

根据查询条件查询一条数据,并通过 asType 进行接收 * * @param query 查询条件 * @param asType 接收的数据类型 @@ -266,41 +294,29 @@ public interface IService { } /** - * 根据查询条件查询一条数据。 - * - * @param query 查询条件 - * @return 查询结果数据 - * @apiNote 该方法会将查询结果封装为 {@link Optional} 类进行返回,方便链式操作。 - */ - default Optional getOneOpt(QueryWrapper query) { - return Optional.ofNullable(getOne(query)); - } - - - /** - * 根据查询条件查询一条数据。 + *

根据查询条件查询一条数据。 * * @param query 查询条件 * @param asType 接收的数据类型 * @return 查询结果数据 * @apiNote 该方法会将查询结果封装为 {@link Optional} 类进行返回,方便链式操作。 */ - default Optional getOneOptAs(QueryWrapper query, Class asType) { + default Optional getOneAsOpt(QueryWrapper query, Class asType) { return Optional.ofNullable(getOneAs(query, asType)); } /** - * 根据查询条件查询一条数据。 + *

根据查询条件查询一条数据。 * * @param condition 查询条件 * @return 查询结果数据 */ default T getOne(QueryCondition condition) { - return getMapper().selectOneByCondition(condition); + return getOne(query().where(condition)); } /** - * 根据查询条件查询一条数据。 + *

根据查询条件查询一条数据。 * * @param condition 查询条件 * @return 查询结果数据 @@ -311,37 +327,16 @@ public interface IService { } /** - * 查询所有数据。 + *

查询所有数据。 * * @return 所有数据 */ default List list() { - return getMapper().selectAll(); + return list(query()); } /** - * 根据查询条件查询数据集合。 - * - * @param condition 查询条件 - * @return 数据集合 - */ - default List list(QueryCondition condition) { - return getMapper().selectListByCondition(condition); - } - - /** - * 根据查询条件查询数据集合。 - * - * @param condition 查询条件 - * @return 数据集合 - */ - default List list(QueryCondition condition, int count) { - return getMapper().selectListByCondition(condition, count); - } - - - /** - * 根据查询条件查询数据集合。 + *

根据查询条件查询数据集合。 * * @param query 查询条件 * @return 数据集合 @@ -351,7 +346,17 @@ public interface IService { } /** - * 根据查询条件查询数据集合,并通过 asType 进行接收 + *

根据查询条件查询数据集合。 + * + * @param condition 查询条件 + * @return 数据集合 + */ + default List list(QueryCondition condition) { + return list(query().where(condition)); + } + + /** + *

根据查询条件查询数据集合,并通过 asType 进行接收 * * @param query 查询条件 * @param asType 接收的数据类型 @@ -361,9 +366,8 @@ public interface IService { return getMapper().selectListByQueryAs(query, asType); } - /** - * 根据数据主键查询数据集合。 + *

根据数据主键查询数据集合。 * * @param ids 数据主键 * @return 数据集合 @@ -373,19 +377,19 @@ public interface IService { } /** - * 根据 {@link Map} 构建查询条件查询数据集合。 + *

根据 {@link Map} 构建查询条件查询数据集合。 * * @param query 查询条件 * @return 数据集合 */ default List listByMap(Map query) { - return getMapper().selectListByMap(query); + return list(query().where(query)); } // ===== 数量查询操作 ===== /** - * 根据查询条件判断数据是否存在。 + *

根据查询条件判断数据是否存在。 * * @param query 查询条件 * @return {@code true} 数据存在,{@code false} 数据不存在。 @@ -395,7 +399,7 @@ public interface IService { } /** - * 根据查询条件判断数据是否存在。 + *

根据查询条件判断数据是否存在。 * * @param condition 查询条件 * @return {@code true} 数据存在,{@code false} 数据不存在。 @@ -405,16 +409,16 @@ public interface IService { } /** - * 查询所有数据数量。 + *

查询所有数据数量。 * * @return 所有数据数量 */ default long count() { - return getMapper().selectCountByQuery(QueryWrapper.create()); + return count(query()); } /** - * 根据查询条件查询数据数量。 + *

根据查询条件查询数据数量。 * * @param query 查询条件 * @return 数据数量 @@ -424,29 +428,29 @@ public interface IService { } /** - * 根据查询条件查询数据数量。 + *

根据查询条件查询数据数量。 * * @param condition 查询条件 * @return 数据数量 */ default long count(QueryCondition condition) { - return getMapper().selectCountByCondition(condition); + return count(query().where(condition)); } // ===== 分页查询操作 ===== /** - * 分页查询所有数据。 + *

分页查询所有数据。 * * @param page 分页对象 * @return 分页对象 */ default Page page(Page page) { - return getMapper().paginate(page, QueryWrapper.create()); + return page(page, query()); } /** - * 根据查询条件分页查询数据。 + *

根据查询条件分页查询数据。 * * @param page 分页对象 * @param query 查询条件 @@ -457,14 +461,18 @@ public interface IService { } /** - * 根据查询条件分页查询数据。 + *

根据查询条件分页查询数据。 * * @param page 分页对象 * @param condition 查询条件 * @return 分页对象 */ default Page page(Page page, QueryCondition condition) { - return getMapper().paginate(page, QueryWrapper.create().where(condition)); + return page(page, query().where(condition)); + } + + default QueryWrapper query() { + return QueryWrapper.create(); } } \ No newline at end of file diff --git a/mybatis-flex-core/src/test/java/com/mybatisflex/coretest/AccountSqlTester.java b/mybatis-flex-core/src/test/java/com/mybatisflex/coretest/AccountSqlTester.java index 9af5cb83..14670ddc 100644 --- a/mybatis-flex-core/src/test/java/com/mybatisflex/coretest/AccountSqlTester.java +++ b/mybatis-flex-core/src/test/java/com/mybatisflex/coretest/AccountSqlTester.java @@ -60,7 +60,7 @@ public class AccountSqlTester { }); TableManager.setDynamicTableProcessor(original -> original+"_01"); - System.out.println(query.toDebugSQL()); + System.out.println(query.toSQL()); } @@ -75,7 +75,7 @@ public class AccountSqlTester { TableManager.setDynamicTableProcessor(original -> original+"_01"); TableManager.setDynamicTableProcessor(original -> original+"_01"); - System.out.println(query.toDebugSQL()); + System.out.println(query.toSQL()); } @@ -409,6 +409,25 @@ public class AccountSqlTester { } + @Test + public void testCase3() { + IDialect dialect = new CommonsDialectImpl(); + + QueryWrapper queryWrapper = QueryWrapper.create() + .select(ACCOUNT.ALL_COLUMNS, + case_(ACCOUNT.ID) + .when(100).then(100) + .when(200).then(200) + .else_(convert("varchar", "GETDATE()", "126")) + .end().as("result")) + .from(ACCOUNT) + .and(ACCOUNT.USER_NAME.like("michael")); + + String sql = dialect.forSelectByQuery(queryWrapper); + System.out.println(sql); + } + + @Test public void testLimitOffset() { diff --git a/mybatis-flex-processor/pom.xml b/mybatis-flex-processor/pom.xml index 066e5c22..fb9dd229 100644 --- a/mybatis-flex-processor/pom.xml +++ b/mybatis-flex-processor/pom.xml @@ -5,7 +5,7 @@ parent com.mybatis-flex - 1.3.3 + 1.3.4 4.0.0 @@ -20,7 +20,7 @@ com.mybatis-flex mybatis-flex-annotation - 1.3.3 + 1.3.4 diff --git a/mybatis-flex-solon-plugin/pom.xml b/mybatis-flex-solon-plugin/pom.xml index 68b3ea07..2f4ea5ec 100644 --- a/mybatis-flex-solon-plugin/pom.xml +++ b/mybatis-flex-solon-plugin/pom.xml @@ -5,7 +5,7 @@ parent com.mybatis-flex - 1.3.3 + 1.3.4 4.0.0 @@ -21,7 +21,7 @@ com.mybatis-flex mybatis-flex-core - 1.3.3 + 1.3.4 diff --git a/mybatis-flex-spring-boot-starter/pom.xml b/mybatis-flex-spring-boot-starter/pom.xml index fe486abb..28affc54 100644 --- a/mybatis-flex-spring-boot-starter/pom.xml +++ b/mybatis-flex-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ parent com.mybatis-flex - 1.3.3 + 1.3.4 4.0.0 @@ -21,7 +21,7 @@ com.mybatis-flex mybatis-flex-spring - 1.3.3 + 1.3.4 diff --git a/mybatis-flex-spring/pom.xml b/mybatis-flex-spring/pom.xml index 39e5342b..c919b577 100644 --- a/mybatis-flex-spring/pom.xml +++ b/mybatis-flex-spring/pom.xml @@ -5,7 +5,7 @@ parent com.mybatis-flex - 1.3.3 + 1.3.4 4.0.0 @@ -20,7 +20,7 @@ com.mybatis-flex mybatis-flex-core - 1.3.3 + 1.3.4 diff --git a/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/FlexSqlSessionFactoryBean.java b/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/FlexSqlSessionFactoryBean.java index b0336c84..48e7697e 100644 --- a/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/FlexSqlSessionFactoryBean.java +++ b/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/FlexSqlSessionFactoryBean.java @@ -338,7 +338,7 @@ public class FlexSqlSessionFactoryBean extends SqlSessionFactoryBean */ @Override public void setConfiguration(Configuration configuration) { - if (!(configuration instanceof FlexConfiguration)) { + if (configuration != null && !(configuration instanceof FlexConfiguration)) { throw new IllegalArgumentException("Only support FlexConfiguration."); } this.configuration = configuration; diff --git a/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/service/impl/CacheableServiceImpl.java b/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/service/impl/CacheableServiceImpl.java new file mode 100644 index 00000000..2191149b --- /dev/null +++ b/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/service/impl/CacheableServiceImpl.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * 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 + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.spring.service.impl; + +import com.mybatisflex.core.BaseMapper; +import com.mybatisflex.core.exception.FlexExceptions; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.service.IService; +import com.mybatisflex.core.table.TableInfo; +import com.mybatisflex.core.table.TableInfoFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Collection; + +/** + *

可缓存数据的 Service 实现类。 + * + *

该实现类对缓存做了以下处理: + * + *

    + *
  • 重写 {@link #saveOrUpdate(Object)} 方法,分别调用 {@link #save(Object)} 和 {@link #updateById(Object)} + * 方法,避免缓存无法更新造成数据不一致。 + *
  • 重写{@link #updateBatch(Collection, int)} 方法,默认抛出异常,不支持批量更新操作, + * 防止批量更新数据,缓存不一致。 + *
  • 重写 {@link #query()} 方法,解决使用 {@link QueryWrapper#toSQL()} 作为缓存 + * 的主键时,"SELECT * FROM" 后面没有表名的问题。 + *
+ * + * @author 王帅 + * @since 2023-05-30 + */ +public class CacheableServiceImpl, T> implements IService { + + @Autowired + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") + private M mapper; + + /** + * {@inheritDoc} + */ + @Override + public BaseMapper getMapper() { + return mapper; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean saveOrUpdate(T entity) { + TableInfo tableInfo = TableInfoFactory.ofEntityClass(entity.getClass()); + Object[] pkArgs = tableInfo.buildPkSqlArgs(entity); + if (pkArgs.length == 0 || pkArgs[0] == null) { + return save(entity); + } else { + return updateById(entity); + } + } + + /** + *

不支持批量更新操作。 + */ + @Override + public boolean updateBatch(Collection entities, int batchSize) { + throw FlexExceptions.wrap("Batch update do not support caching operation."); + } + + /** + *

获取默认的 {@link QueryWrapper}。 + * + *

使用 {@link QueryWrapper#create()} 构建默认查询条件的时候, + * 要使用 {@link QueryWrapper#from(String...)} 方法指定从哪个表 + * 查询数据,不然使用 {@link QueryWrapper#toSQL()} 生成的 + * SQL 语句就是 {@code "SELECT * FROM"},没有表名信息。 + * + *

默认通过反射获取表名,建议重写,根据情况设置默认表名,以提升效率。 + * + *

例如: + * + *

{@code
+     * @Override
+     * public QueryWrapper query() {
+     *     return QueryWrapper.create().from(ACCOUNT);
+     * }
+     * }
+ * + * @return 默认的 {@link QueryWrapper} + */ + @Override + public QueryWrapper query() { + String tableName = TableInfoFactory.ofMapperClass(getMapper().getClass()).getTableName(); + return QueryWrapper.create().from(tableName); + } + +} \ No newline at end of file diff --git a/mybatis-flex-test/mybatis-flex-native-test/pom.xml b/mybatis-flex-test/mybatis-flex-native-test/pom.xml index c3ccc95c..66d020ff 100644 --- a/mybatis-flex-test/mybatis-flex-native-test/pom.xml +++ b/mybatis-flex-test/mybatis-flex-native-test/pom.xml @@ -5,7 +5,7 @@ mybatis-flex-test com.mybatis-flex - 1.3.3 + 1.3.4 4.0.0 @@ -20,7 +20,7 @@ com.mybatis-flex mybatis-flex-core - 1.3.3 + 1.3.4 diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-test/pom.xml b/mybatis-flex-test/mybatis-flex-spring-boot-test/pom.xml index f12f9ebb..ed6bd4b4 100644 --- a/mybatis-flex-test/mybatis-flex-spring-boot-test/pom.xml +++ b/mybatis-flex-test/mybatis-flex-spring-boot-test/pom.xml @@ -5,7 +5,7 @@ mybatis-flex-test com.mybatis-flex - 1.3.3 + 1.3.4 4.0.0 @@ -21,7 +21,7 @@ com.mybatis-flex mybatis-flex-spring-boot-starter - 1.3.3 + 1.3.4 diff --git a/mybatis-flex-test/mybatis-flex-spring-test/pom.xml b/mybatis-flex-test/mybatis-flex-spring-test/pom.xml index 4a213199..ae60b0e3 100644 --- a/mybatis-flex-test/mybatis-flex-spring-test/pom.xml +++ b/mybatis-flex-test/mybatis-flex-spring-test/pom.xml @@ -5,7 +5,7 @@ mybatis-flex-test com.mybatis-flex - 1.3.3 + 1.3.4 4.0.0 @@ -20,13 +20,13 @@ com.mybatis-flex mybatis-flex-core - 1.3.3 + 1.3.4 com.mybatis-flex mybatis-flex-spring - 1.3.3 + 1.3.4 diff --git a/mybatis-flex-test/pom.xml b/mybatis-flex-test/pom.xml index ac08c048..c288d25b 100644 --- a/mybatis-flex-test/pom.xml +++ b/mybatis-flex-test/pom.xml @@ -5,7 +5,7 @@ parent com.mybatis-flex - 1.3.3 + 1.3.4 4.0.0 @@ -59,7 +59,7 @@ com.mybatis-flex mybatis-flex-processor - 1.3.3 + 1.3.4 diff --git a/pom.xml b/pom.xml index 58dd5705..fbcec9be 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.mybatis-flex parent pom - 1.3.3 + 1.3.4 mybatis-flex https://mybatis-flex.com