diff --git a/docs/zh/awesome-things.md b/docs/zh/awesome-things.md index 19340be9..d6812ffe 100644 --- a/docs/zh/awesome-things.md +++ b/docs/zh/awesome-things.md @@ -84,6 +84,9 @@ - [MyBatis-Flex 视频教程 - 30 数据脱敏的简单使用](https://www.bilibili.com/video/BV1gz4y1s7Wg) - [MyBatis-Flex 视频教程 - 31 枚举属性的使用](https://www.bilibili.com/video/BV1mm4y1W7SD) - [MyBatis-Flex 视频教程 - 32 关联查询(Join Query)](https://www.bilibili.com/video/BV1B8411d7iC) +- [MyBatis-Flex 视频教程 - 33 关联查询(Field Query)](https://www.bilibili.com/video/BV17k4y1g7vt) +- [MyBatis-Flex 视频教程 - 34 关联查询(Relation Query)](https://www.bilibili.com/video/BV1bj411r7A4) +- [MyBatis-Flex 视频教程 - 35 关联查询对比](https://www.bilibili.com/video/BV1oF411f7dr) diff --git a/docs/zh/core/tx.md b/docs/zh/core/tx.md index c41f77ba..084370e0 100644 --- a/docs/zh/core/tx.md +++ b/docs/zh/core/tx.md @@ -87,14 +87,12 @@ MyBatis-Flex 已支持 Spring 框架的 `@Transactional`,在使用 SpringBoot > 注意:若项目未使用 SpringBoot,只用到了 Spring,需要参考 MyBatis-Flex 的 [FlexTransactionAutoConfiguration](https://gitee.com/mybatis-flex/mybatis-flex/blob/main/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/FlexTransactionAutoConfiguration.java) > 进行事务配置,才能正常使用 `@Transactional` 注解。 -## 特征 +## 多数据源注意事项 -- 1、支持嵌套事务 -- 2、支持多数据源 +注意:在多数据源的情况下,所有数据源的数据库请求(Connection)会执行相同的 `commit` 或者 `rollback`,MyBatis-Flex 只保证了程序端的原子操作, +但并不能保证多个数据源之间的原子操作。例如: -> 注意:在多数据源的情况下,所有数据源的数据库请求(Connection)会执行相同的 commit 或者 rollback,但并非原子操作。例如: - -```java +```java 1,6,13,19 @Transactional public void doSomething(){ @@ -117,7 +115,7 @@ public void doSomething(){ } ``` -在以上的例子中,两次 `Db.update(...)` 虽然是两个不同的数据源,但它们都在同一个事务 `@Transactional` 里,因此,当抛出异常的时候, +在以上的例子中,执行了两次 `Db.updateBySql(...)`,它们是两个不同的数据源,但它们都在同一个事务 `@Transactional` 里,因此,当抛出异常的时候, 它们都会进行回滚(rollback)。 以上提到的 `并非原子操作`,指的是: @@ -127,26 +125,112 @@ public void doSomething(){ ## Seata 分布式事务 -Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。 +Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。 +Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。 官方网站:https://seata.io/zh-cn/index.html -1. 首先,先了解事务的基础(自行百度), -2. 在了解seata事务的项目 官网地址:https://seata.io/zh-cn/docs -3. 然后根据官方的[快速开始](https://seata.io/zh-cn/docs/user/quickstart.html)在下载最新版的seata-server并在本地跑起一个 -4. seata-server服务 -5. 然后使用[mybatis-flex-test](https://gitee.com/mybatis-flex/mybatis-flex/tree/main/mybatis-flex-test)模块 下面的mybatis-flex-spring-boot-seata进行测试 ->此demo只是一个纯演示的demo,模仿的是[官方事例](https://github.com/seata/seata-samples/tree/master/springboot-mybatis) -进行整合,微服务合并成一个多数据源进行测试,当然,这种方法是不提倡的,seata事务并且本地多数据源的方式可能本身就存在设计思路问题,可能存在过度设计,此demo只是作为一个演示。 + +### 开始使用 + +**第 1 步:在 `application.yml` 配置开启 Seata 分布式事务功能:** + +```yaml +mybatis-flex: + seata-config: + enable: true + seata-mode: XA # 支持 xa 或者 ta +``` +- XA:指的是: 分布式事务协议(X/Open Distributed Transaction Processing),它是一种由 X/Open 组织制定的分布式事务标准, +XA 使用两阶段提交(2PC,Two-Phase Commit)来保证所有资源同时提交或回滚任何特定的事务。 +目前,几乎所有主流的数据库都对 XA 规范 提供了支持,是 Seata 默认使用的模式。 + +- AT: 是一种无侵入的分布式事务解决方案。在 AT 模式下,用户只需关注自己的 “`业务SQL`”, +用户的 “`业务SQL`” 作为一阶段,Seata 会根据 SQL 内容,自动生成事务的二阶段提交和回滚操作。 + +**第 2 步:在 `application.yml` 添加 Seata 的相关配置:** + +```yaml +seata: + enabled: true + application-id: business-service + tx-service-group: my_test_tx_group + enable-auto-data-source-proxy: false #必须 +# use-jdk-proxy: false + client: + rm: + async-commit-buffer-limit: 1000 + report-retry-count: 5 + table-meta-check-enable: false + report-success-enable: false + lock: + retry-interval: 10 + retry-times: 30 + retry-policy-branch-rollback-on-conflict: true + tm: + commit-retry-count: 5 + rollback-retry-count: 5 + undo: + data-validation: true + log-serialization: jackson + log-table: undo_log + log: + exceptionRate: 100 + service: + vgroup-mapping: + my_test_tx_group: default + grouplist: + default: 127.0.0.1:8091 + #enable-degrade: false + #disable-global-transaction: false + transport: + shutdown: + wait: 3 + thread-factory: + boss-thread-prefix: NettyBoss + worker-thread-prefix: NettyServerNIOWorker + server-executor-thread-prefix: NettyServerBizHandler + share-boss-worker: false + client-selector-thread-prefix: NettyClientSelector + client-selector-thread-size: 1 + client-worker-thread-prefix: NettyClientWorkerThread + worker-thread-size: default + boss-thread-size: 1 + type: TCP + server: NIO + heartbeat: true + serialization: seata + compressor: none + enable-client-batch-send-request: true + config: + type: file + registry: + type: file +``` + +> 以上配置的含义,请参考 Seata 官方网站:https://seata.io/zh-cn/docs/user/configurations.html + +**3、通过使用 `@GloabalTransactional` 开始 Seata 分布式事务。** + +```java 1 +@GlobalTransactional +public void purchase(String userId, String commodityCode, int orderCount) { + LOGGER.info("purchase begin ... xid: " + RootContext.getXID()); + stockClient.deduct(commodityCode, orderCount); + orderClient.create(userId, commodityCode, orderCount); +} +``` + +> 更多关于 Seata 的知识,请异步 Seata 官方网站了解:https://seata.io/zh-cn/docs ,也可以参考 Seata +> 的官方示例快速开始:https://seata.io/zh-cn/docs/user/quickstart.html + ### 注意事项 ->使用seata的时候必须数据源代理 -`seata.enable-auto-data-source-proxy: false` -`pom`自行引入[seata-spring-boot-starter](https://mvnrepository.com/artifact/io.seata/seata-spring-boot-starter)依赖, +在使用 Seata 分布式事务时,请注意添加 Seata 的相关 Maven 依赖,例如: -application.yml需要配置如下参数 -1. `mybatis-flex.seata-config.enable` - - 配置含义:seata事务是否开启,true开启,默认false关闭 -2. `mybatis-flex.seata-config.seata-mode` - - 默认启动的事务类型,目前只支持XA或者AT,默认AT +```xml + + io.seata + seata-spring-boot-starter + 1.7.0 + +``` diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/activerecord/Model.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/activerecord/Model.java index dc58aa56..d6d2bd11 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/activerecord/Model.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/activerecord/Model.java @@ -16,15 +16,15 @@ package com.mybatisflex.core.activerecord; +import com.mybatisflex.core.BaseMapper; import com.mybatisflex.core.activerecord.query.FieldsQuery; import com.mybatisflex.core.activerecord.query.QueryModel; import com.mybatisflex.core.activerecord.query.RelationsQuery; -import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.MapperQueryChain; +import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.core.util.SqlUtil; import java.io.Serializable; -import java.util.List; -import java.util.Optional; /** * Active Record 模型。 @@ -36,7 +36,7 @@ import java.util.Optional; @SuppressWarnings({"unused", "unchecked"}) public abstract class Model> extends QueryModel - implements MapperModel, Serializable { + implements MapperModel, MapperQueryChain, Serializable { /** * 根据实体类构建的条件删除数据。 @@ -66,175 +66,22 @@ public abstract class Model> return SqlUtil.toBool(baseMapper().updateByQuery((T) this, ignoreNulls, queryWrapper())); } - /** - * 根据实体类构建的条件查询数据数量。 - * - * @return 数据数量 - */ - public long count() { - return baseMapper().selectCountByQuery(queryWrapper()); + @Override + public BaseMapper baseMapper() { + return MapperModel.super.baseMapper(); } - /** - * 根据实体类构建的条件判断数据是否存在。 - * - * @return {@code true} 数据存在,{@code false} 数据不存在 - */ - public boolean exists() { - return SqlUtil.toBool(count()); + @Override + public QueryWrapper toQueryWrapper() { + return queryWrapper(); } - /** - * 根据实体类构建的条件获取一条数据。 - * - * @return 数据 - */ - public T one() { - return baseMapper().selectOneByQuery(queryWrapper()); - } - - /** - * 根据实体类构建的条件获取一条数据,返回的数据为 asType 类型。 - * - * @param asType 接收数据类型 - * @return 数据 - */ - public R oneAs(Class asType) { - return baseMapper().selectOneByQueryAs(queryWrapper(), asType); - } - - - /** - * 根据实体类构建的条件获取一条数据,并封装为 {@link Optional} 返回。 - * - * @return 数据 - */ - public Optional oneOpt() { - return Optional.ofNullable(one()); - } - - /** - * 根据实体类构建的条件获取一条数据,返回的数据为 asType 类型,并封装为 {@link Optional} 返回。 - * - * @param asType 接收数据类型 - * @return 数据 - */ - public Optional oneAsOpt(Class asType) { - return Optional.ofNullable(oneAs(asType)); - } - - /** - * 根据实体类构建的条件获取第一列,且第一条数据。 - * - * @return 第一列数据 - */ - public Object obj() { - return baseMapper().selectObjectByQuery(queryWrapper()); - } - - /** - * 根据实体类构建的条件获取第一列,且第一条数据并转换为指定类型,比如 {@code Long}, {@code String} 等。 - * - * @param asType 接收数据类型 - * @return 第一列数据 - */ - public R objAs(Class asType) { - return baseMapper().selectObjectByQueryAs(queryWrapper(), asType); - } - - /** - * 根据实体类构建的条件获取第一列,且第一条数据,并封装为 {@link Optional} 返回。 - * - * @return 第一列数据 - */ - public Optional objOpt() { - return Optional.ofNullable(obj()); - } - - /** - * 根据实体类构建的条件获取第一列,且第一条数据并转换为指定类型,比如 {@code Long}, {@code String} - * 等,封装为 {@link Optional} 返回。 - * - * @param asType 接收数据类型 - * @return 第一列数据 - */ - public Optional objAsOpt(Class asType) { - return Optional.ofNullable(objAs(asType)); - } - - /** - * 根据实体类构建的条件获取第一列的所有数据。 - * - * @return 第一列数据 - */ - public List objList() { - return baseMapper().selectObjectListByQuery(queryWrapper()); - } - - /** - * 根据实体类构建的条件获取第一列的所有数据,并转换为指定类型,比如 {@code Long}, {@code String} 等。 - * - * @param asType 接收数据类型 - * @return 第一列数据 - */ - public List objListAs(Class asType) { - return baseMapper().selectObjectListByQueryAs(queryWrapper(), asType); - } - - /** - * 根据实体类构建的条件获取多条数据。 - * - * @return 数据列表 - */ - public List list() { - return baseMapper().selectListByQuery(queryWrapper()); - } - - /** - * 根据实体类构建的条件获取多条数据,返回的数据为 asType 类型。 - * - * @param asType 接收数据类型 - * @return 数据列表 - */ - public List listAs(Class asType) { - return baseMapper().selectListByQueryAs(queryWrapper(), asType); - } - - /** - * 根据实体类构建的条件获取分页数据。 - * - * @param page 分页对象 - * @return 分页数据 - */ - public Page page(Page page) { - return baseMapper().paginate(page, queryWrapper()); - } - - /** - * 根据实体类构建的条件获取分页数据,返回的数据为 asType 类型。 - * - * @param page 分页对象 - * @param asType 接收数据类型 - * @return 分页数据 - */ - public Page pageAs(Page page, Class asType) { - return baseMapper().paginateAs(page, queryWrapper(), asType); - } - - /** - * 使用 {@code Fields Query} 的方式进行关联查询。 - * - * @return {@code Fields Query} 查询 - */ + @Override public FieldsQuery withFields() { return new FieldsQuery<>(this); } - /** - * 使用 {@code Relations Query} 的方式进行关联查询。 - * - * @return {@code Relations Query} 查询 - */ + @Override public RelationsQuery withRelations() { return new RelationsQuery<>(this); } diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/activerecord/query/AbstractQuery.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/activerecord/query/AbstractQuery.java deleted file mode 100644 index 3610dc87..00000000 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/activerecord/query/AbstractQuery.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * 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.activerecord.query; - -import com.mybatisflex.core.BaseMapper; -import com.mybatisflex.core.activerecord.Model; -import com.mybatisflex.core.paginate.Page; -import com.mybatisflex.core.query.QueryWrapper; - -import java.util.List; -import java.util.Optional; - -/** - * 抽象关联查询。 - * - * @author 王帅 - * @since 2023-07-30 - */ -public abstract class AbstractQuery> { - - protected final Model model; - - protected AbstractQuery(Model model) { - this.model = model; - } - - /** - * @return 主键 - */ - protected Object[] pkValues() { - return model.pkValues(); - } - - /** - * @return BaseMapper - */ - protected BaseMapper baseMapper() { - return model.baseMapper(); - } - - /** - * @return QueryWrapper - */ - protected QueryWrapper queryWrapper() { - return model.queryWrapper(); - } - - /** - * 根据实体类主键获取一条数据。 - * - * @return 数据 - */ - public abstract T oneById(); - - /** - * 根据实体类主键获取一条数据,并封装为 {@link Optional} 返回。 - * - * @return 数据 - */ - public Optional oneByIdOpt() { - return Optional.ofNullable(oneById()); - } - - /** - * 根据实体类主键获取一条数据。 - * - * @return 数据 - */ - public abstract R oneByIdAs(Class asType); - - /** - * 根据实体类主键获取一条数据,并封装为 {@link Optional} 返回。 - * - * @return 数据 - */ - public Optional oneByIdAsOpt(Class asType) { - return Optional.ofNullable(oneByIdAs(asType)); - } - - /** - * 根据实体类构建的条件获取一条数据。 - * - * @return 数据 - */ - public abstract T one(); - - /** - * 根据实体类构建的条件获取一条数据,返回的数据为 asType 类型。 - * - * @param asType 接收数据类型 - * @return 数据 - */ - public abstract R oneAs(Class asType); - - /** - * 根据实体类构建的条件获取一条数据,并封装为 {@link Optional} 返回。 - * - * @return 数据 - */ - public Optional oneOpt() { - return Optional.ofNullable(one()); - } - - /** - * 根据实体类构建的条件获取一条数据,返回的数据为 asType 类型,并封装为 {@link Optional} 返回。 - * - * @param asType 接收数据类型 - * @return 数据 - */ - public Optional oneOptAs(Class asType) { - return Optional.ofNullable(oneAs(asType)); - } - - /** - * 根据实体类构建的条件获取多条数据。 - * - * @return 数据列表 - */ - public abstract List list(); - - /** - * 根据实体类构建的条件获取多条数据,返回的数据为 asType 类型。 - * - * @param asType 接收数据类型 - * @return 数据列表 - */ - public abstract List listAs(Class asType); - - /** - * 根据实体类构建的条件获取分页数据。 - * - * @param page 分页对象 - * @return 分页数据 - */ - public abstract Page page(Page page); - - /** - * 根据实体类构建的条件获取分页数据,返回的数据为 asType 类型。 - * - * @param page 分页对象 - * @param asType 接收数据类型 - * @return 分页数据 - */ - public abstract Page pageAs(Page page, Class asType); - -} diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/activerecord/query/FieldsQuery.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/activerecord/query/FieldsQuery.java index 0958d3af..3bc69815 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/activerecord/query/FieldsQuery.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/activerecord/query/FieldsQuery.java @@ -17,19 +17,14 @@ package com.mybatisflex.core.activerecord.query; import com.mybatisflex.core.activerecord.Model; -import com.mybatisflex.core.field.FieldQuery; import com.mybatisflex.core.field.FieldQueryManager; import com.mybatisflex.core.field.QueryBuilder; import com.mybatisflex.core.mybatis.MappedStatementTypes; -import com.mybatisflex.core.paginate.Page; -import com.mybatisflex.core.util.FieldWrapper; +import com.mybatisflex.core.query.FieldsBuilder; import com.mybatisflex.core.util.LambdaGetter; -import com.mybatisflex.core.util.LambdaUtil; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** * 使用 {@code Fields Query} 的方式进行关联查询。 @@ -37,53 +32,34 @@ import java.util.Map; * @author 王帅 * @since 2023-07-30 */ -public class FieldsQuery> extends AbstractQuery { - - private final Map fieldQueryMap; +public class FieldsQuery> extends FieldsBuilder { public FieldsQuery(Model model) { super(model); - this.fieldQueryMap = new HashMap<>(); } - /** - * 设置属性对应的 {@code QueryWrapper} 查询。 - * - * @param field 属性 - * @param builder {@code QueryWrapper} 构建 - * @param 属性类型 - * @return 属性查询构建 - */ + @Override public FieldsQuery fieldMapping(LambdaGetter field, QueryBuilder builder) { - return fieldMapping(field, false, builder); - } - - /** - * 设置属性对应的 {@code QueryWrapper} 查询。 - * - * @param field 属性 - * @param prevent 阻止对嵌套类属性的查询 - * @param builder {@code QueryWrapper} 构建 - * @param 属性类型 - * @return 属性查询构建 - */ - public FieldsQuery fieldMapping(LambdaGetter field, boolean prevent, QueryBuilder builder) { - String fieldName = LambdaUtil.getFieldName(field); - Class entityClass = LambdaUtil.getImplClass(field); - FieldQuery fieldQuery = new FieldQuery(); - fieldQuery.setPrevent(prevent); - fieldQuery.setFieldName(fieldName); - fieldQuery.setQueryBuilder(builder); - fieldQuery.setEntityClass(entityClass); - fieldQuery.setFieldWrapper(FieldWrapper.of(entityClass, fieldName)); - this.fieldQueryMap.put(entityClass.getName() + '#' + fieldName, fieldQuery); + super.fieldMapping(field, builder); return this; } - /** - * {@inheritDoc} - */ @Override + public FieldsBuilder fieldMapping(LambdaGetter field, boolean prevent, QueryBuilder builder) { + super.fieldMapping(field, prevent, builder); + return this; + } + + protected Object[] pkValues() { + // 懒加载,实际用到的时候才会生成 主键值 + return ((Model) delegate).pkValues(); + } + + /** + * 根据主键查询一条数据。 + * + * @return 一条数据 + */ public T oneById() { List entities = Collections.singletonList(baseMapper().selectOneById(pkValues())); FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap); @@ -91,9 +67,12 @@ public class FieldsQuery> extends AbstractQuery { } /** - * {@inheritDoc} + * 根据主键查询一条数据,返回的数据为 asType 类型。 + * + * @param asType 接收数据类型 + * @param 接收数据类型 + * @return 一条数据 */ - @Override @SuppressWarnings("unchecked") public R oneByIdAs(Class asType) { try { @@ -106,64 +85,4 @@ public class FieldsQuery> extends AbstractQuery { } } - /** - * {@inheritDoc} - */ - @Override - public T one() { - List entities = Collections.singletonList(baseMapper().selectOneByQuery(queryWrapper())); - FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap); - return entities.get(0); - } - - /** - * {@inheritDoc} - */ - @Override - public R oneAs(Class asType) { - List entities = Collections.singletonList(baseMapper().selectOneByQueryAs(queryWrapper(), asType)); - FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap); - return entities.get(0); - } - - /** - * {@inheritDoc} - */ - @Override - public List list() { - List entities = baseMapper().selectListByQuery(queryWrapper()); - FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap); - return entities; - } - - /** - * {@inheritDoc} - */ - @Override - public List listAs(Class asType) { - List entities = baseMapper().selectListByQueryAs(queryWrapper(), asType); - FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap); - return entities; - } - - /** - * {@inheritDoc} - */ - @Override - public Page page(Page page) { - baseMapper().paginate(page, queryWrapper()); - FieldQueryManager.queryFields(baseMapper(), page.getRecords(), fieldQueryMap); - return page; - } - - /** - * {@inheritDoc} - */ - @Override - public Page pageAs(Page page, Class asType) { - baseMapper().paginateAs(page, queryWrapper(), asType); - FieldQueryManager.queryFields(baseMapper(), page.getRecords(), fieldQueryMap); - return page; - } - } diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/activerecord/query/RelationsQuery.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/activerecord/query/RelationsQuery.java index fe11e913..4f7a4b25 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/activerecord/query/RelationsQuery.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/activerecord/query/RelationsQuery.java @@ -17,10 +17,8 @@ package com.mybatisflex.core.activerecord.query; import com.mybatisflex.core.activerecord.Model; -import com.mybatisflex.core.paginate.Page; -import com.mybatisflex.core.relation.RelationManager; - -import java.util.List; +import com.mybatisflex.core.query.RelationsBuilder; +import com.mybatisflex.core.util.LambdaGetter; /** * 使用 {@code Relations Query} 的方式进行关联查询。 @@ -28,102 +26,59 @@ import java.util.List; * @author 王帅 * @since 2023-07-30 */ -public class RelationsQuery> extends AbstractQuery { +public class RelationsQuery> extends RelationsBuilder { public RelationsQuery(Model model) { super(model); } - /** - * 忽略查询部分 {@code Relations} 注解标记的属性。 - * - * @param fields 属性 - * @return {@code Relations} 查询构建 - */ + @Override public RelationsQuery ignoreRelations(String... fields) { - RelationManager.addIgnoreRelations(fields); - return this; - } - - /** - * 设置父子关系查询中,默认的递归查询深度。 - * - * @param maxDepth 查询深度 - * @return {@code Relations} 查询构建 - */ - public RelationsQuery maxDepth(int maxDepth) { - RelationManager.setMaxDepth(maxDepth); - return this; - } - - /** - * 添加额外的 {@code Relations} 查询条件。 - * - * @param key 键 - * @param value 值 - * @return {@code Relations} 查询构建 - */ - public RelationsQuery extraConditionParam(String key, Object value) { - RelationManager.addExtraConditionParam(key, value); + super.ignoreRelations(fields); return this; } @Override + public RelationsQuery ignoreRelations(LambdaGetter... fields) { + super.ignoreRelations(fields); + return this; + } + + @Override + public RelationsQuery maxDepth(int maxDepth) { + super.maxDepth(maxDepth); + return this; + } + + @Override + public RelationsQuery extraConditionParam(String key, Object value) { + super.extraConditionParam(key, value); + return this; + } + + protected Object[] pkValues() { + // 懒加载,实际用到的时候才会生成 主键值 + return ((Model) delegate).pkValues(); + } + + /** + * 根据主键查询一条数据。 + * + * @return 一条数据 + */ public T oneById() { return baseMapper().selectOneWithRelationsById(pkValues()); } - @Override + /** + * 根据主键查询一条数据,返回的数据为 asType 类型。 + * + * @param asType 接收数据类型 + * @param 接收数据类型 + * @return 一条数据 + */ public R oneByIdAs(Class asType) { return baseMapper().selectOneWithRelationsByIdAs(pkValues(), asType); } - /** - * {@inheritDoc} - */ - @Override - public T one() { - return baseMapper().selectOneWithRelationsByQuery(queryWrapper()); - } - - /** - * {@inheritDoc} - */ - @Override - public R oneAs(Class asType) { - return baseMapper().selectOneWithRelationsByQueryAs(queryWrapper(), asType); - } - - /** - * {@inheritDoc} - */ - @Override - public List list() { - return baseMapper().selectListWithRelationsByQuery(queryWrapper()); - } - - /** - * {@inheritDoc} - */ - @Override - public List listAs(Class asType) { - return baseMapper().selectListWithRelationsByQueryAs(queryWrapper(), asType); - } - - /** - * {@inheritDoc} - */ - @Override - public Page page(Page page) { - return baseMapper().paginateWithRelations(page, queryWrapper()); - } - - /** - * {@inheritDoc} - */ - @Override - public Page pageAs(Page page, Class asType) { - return baseMapper().paginateWithRelationsAs(page, queryWrapper(), asType); - } - } diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/DataSourceManager.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/DataSourceManager.java index 1e3e9c47..eb3ee12a 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/DataSourceManager.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/DataSourceManager.java @@ -21,6 +21,9 @@ import org.apache.ibatis.logging.LogFactory; import javax.sql.DataSource; import java.lang.reflect.Method; +/** + * @author michael + */ public class DataSourceManager { private static DataSourceDecipher decipher; diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/FlexDataSource.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/FlexDataSource.java index 8dd60a95..2886d8cd 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/FlexDataSource.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/datasource/FlexDataSource.java @@ -49,8 +49,13 @@ public class FlexDataSource extends AbstractDataSource { private final DataSource defaultDataSource; public FlexDataSource(String dataSourceKey, DataSource dataSource) { + this(dataSourceKey, dataSource, true); + } - DataSourceManager.decryptDataSource(dataSource); + public FlexDataSource(String dataSourceKey, DataSource dataSource, boolean needDecryptDataSource) { + if (needDecryptDataSource) { + DataSourceManager.decryptDataSource(dataSource); + } this.defaultDataSourceKey = dataSourceKey; this.defaultDataSource = dataSource; @@ -61,11 +66,19 @@ public class FlexDataSource extends AbstractDataSource { } public void addDataSource(String dataSourceKey, DataSource dataSource) { - DataSourceManager.decryptDataSource(dataSource); + addDataSource(dataSourceKey, dataSource, true); + } + + + public void addDataSource(String dataSourceKey, DataSource dataSource, boolean needDecryptDataSource) { + if (needDecryptDataSource) { + DataSourceManager.decryptDataSource(dataSource); + } dataSourceMap.put(dataSourceKey, dataSource); dbTypeHashMap.put(dataSourceKey, DbTypeUtil.getDbType(dataSource)); } + public void removeDatasource(String dataSourceKey) { dataSourceMap.remove(dataSourceKey); dbTypeHashMap.remove(dataSourceKey); diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/AbstractQueryBuilder.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/AbstractQueryBuilder.java new file mode 100644 index 00000000..eacfc83a --- /dev/null +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/AbstractQueryBuilder.java @@ -0,0 +1,49 @@ +/* + * 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.query; + +import com.mybatisflex.core.BaseMapper; + +/** + * 抽象关联查询。 + * + * @author 王帅 + * @since 2023-08-08 + */ +public abstract class AbstractQueryBuilder implements ChainQuery { + + protected final MapperQueryChain delegate; + + protected AbstractQueryBuilder(MapperQueryChain delegate) { + this.delegate = delegate; + } + + /** + * @return BaseMapper + */ + protected BaseMapper baseMapper() { + return delegate.baseMapper(); + } + + /** + * @return QueryWrapper + */ + protected QueryWrapper queryWrapper() { + return delegate.toQueryWrapper(); + } + +} diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/ChainQuery.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/ChainQuery.java new file mode 100644 index 00000000..35e367e8 --- /dev/null +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/ChainQuery.java @@ -0,0 +1,110 @@ +/* + * 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.query; + +import com.mybatisflex.core.paginate.Page; + +import java.util.List; +import java.util.Optional; + +/** + *

链式查询接口。 + * + *

该接口定义了通用的链式查询方法: + *

    + *
  • one: 查询一条数据。 + *
  • list: 查询多条数据。 + *
  • page: 分页查询数据。 + *
+ * + * @param 实体类类型 + * @author 王帅 + * @since 2023-08-08 + */ +public interface ChainQuery { + + /** + * 获取一条数据。 + * + * @return 一条数据 + */ + T one(); + + /** + * 获取一条数据,返回的数据为 asType 类型。 + * + * @param asType 接收数据类型 + * @param 接收数据类型 + * @return 一条数据 + */ + R oneAs(Class asType); + + /** + * 获取一条数据,并封装为 {@link Optional} 返回。 + * + * @return 一条数据 + */ + default Optional oneOpt() { + return Optional.ofNullable(one()); + } + + /** + * 获取一条数据,返回的数据为 asType 类型,并封装为 {@link Optional} 返回。 + * + * @param asType 接收数据类型 + * @param 接收数据类型 + * @return 一条数据 + */ + default Optional oneAsOpt(Class asType) { + return Optional.ofNullable(oneAs(asType)); + } + + /** + * 获取多条数据。 + * + * @return 数据列表 + */ + List list(); + + /** + * 获取多条数据,返回的数据为 asType 类型。 + * + * @param asType 接收数据类型 + * @param 接收数据类型 + * @return 数据列表 + */ + List listAs(Class asType); + + /** + * 获取分页数据。 + * + * @param page 分页对象 + * @return 分页数据 + */ + Page page(Page page); + + /** + * 获取分页数据,返回的数据为 asType 类型。 + * + * @param page 分页对象 + * @param asType 接收数据类型 + * @param 接收数据类型 + * @return 分页数据 + */ + Page pageAs(Page page, Class asType); + +} diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/FieldsBuilder.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/FieldsBuilder.java new file mode 100644 index 00000000..1eb980da --- /dev/null +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/FieldsBuilder.java @@ -0,0 +1,141 @@ +/* + * 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.query; + +import com.mybatisflex.core.field.FieldQuery; +import com.mybatisflex.core.field.FieldQueryManager; +import com.mybatisflex.core.field.QueryBuilder; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.util.FieldWrapper; +import com.mybatisflex.core.util.LambdaGetter; +import com.mybatisflex.core.util.LambdaUtil; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 使用 {@code Fields Query} 的方式进行关联查询。 + * + * @author 王帅 + * @since 2023-08-08 + */ +public class FieldsBuilder extends AbstractQueryBuilder { + + protected final Map fieldQueryMap; + + public FieldsBuilder(MapperQueryChain delegate) { + super(delegate); + this.fieldQueryMap = new HashMap<>(); + } + + /** + * 设置属性对应的 {@code QueryWrapper} 查询。 + * + * @param field 属性 + * @param builder {@code QueryWrapper} 构建 + * @param 属性类型 + * @return 属性查询构建 + */ + public FieldsBuilder fieldMapping(LambdaGetter field, QueryBuilder builder) { + return fieldMapping(field, false, builder); + } + + /** + * 设置属性对应的 {@code QueryWrapper} 查询。 + * + * @param field 属性 + * @param prevent 阻止对嵌套类属性的查询 + * @param builder {@code QueryWrapper} 构建 + * @param 属性类型 + * @return 属性查询构建 + */ + public FieldsBuilder fieldMapping(LambdaGetter field, boolean prevent, QueryBuilder builder) { + String fieldName = LambdaUtil.getFieldName(field); + Class entityClass = LambdaUtil.getImplClass(field); + FieldQuery fieldQuery = new FieldQuery(); + fieldQuery.setPrevent(prevent); + fieldQuery.setFieldName(fieldName); + fieldQuery.setQueryBuilder(builder); + fieldQuery.setEntityClass(entityClass); + fieldQuery.setFieldWrapper(FieldWrapper.of(entityClass, fieldName)); + this.fieldQueryMap.put(entityClass.getName() + '#' + fieldName, fieldQuery); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public T one() { + List entities = Collections.singletonList(baseMapper().selectOneByQuery(queryWrapper())); + FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap); + return entities.get(0); + } + + /** + * {@inheritDoc} + */ + @Override + public R oneAs(Class asType) { + List entities = Collections.singletonList(baseMapper().selectOneByQueryAs(queryWrapper(), asType)); + FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap); + return entities.get(0); + } + + /** + * {@inheritDoc} + */ + @Override + public List list() { + List entities = baseMapper().selectListByQuery(queryWrapper()); + FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap); + return entities; + } + + /** + * {@inheritDoc} + */ + @Override + public List listAs(Class asType) { + List entities = baseMapper().selectListByQueryAs(queryWrapper(), asType); + FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap); + return entities; + } + + /** + * {@inheritDoc} + */ + @Override + public Page page(Page page) { + baseMapper().paginate(page, queryWrapper()); + FieldQueryManager.queryFields(baseMapper(), page.getRecords(), fieldQueryMap); + return page; + } + + /** + * {@inheritDoc} + */ + @Override + public Page pageAs(Page page, Class asType) { + baseMapper().paginateAs(page, queryWrapper(), asType); + FieldQueryManager.queryFields(baseMapper(), page.getRecords(), fieldQueryMap); + return page; + } + +} diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/MapperQueryChain.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/MapperQueryChain.java new file mode 100644 index 00000000..31886606 --- /dev/null +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/MapperQueryChain.java @@ -0,0 +1,195 @@ +/* + * 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.query; + +import com.mybatisflex.core.BaseMapper; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.util.SqlUtil; + +import java.util.List; +import java.util.Optional; + +/** + *

链式 {@link BaseMapper} 查询。 + * + *

要求实现类除了包含有 {@link BaseMapper} 接口的引用外,还必须具有 {@link QueryWrapper} + * 的查询条件构建功能。在使用时: + *

    + *
  • 通过 {@link #baseMapper()} 获取该实现类对应的 {@link BaseMapper} 引用。 + *
  • 通过 {@link #toQueryWrapper()} 将该实现类转换为 {@link QueryWrapper} 对象。 + *
+ * + * @param 实体类类型 + * @author 王帅 + * @since 2023-08-08 + */ +public interface MapperQueryChain extends ChainQuery { + + /** + * 该实现类对应的 {@link BaseMapper} 对象。 + * + * @return {@link BaseMapper} + */ + BaseMapper baseMapper(); + + /** + * 将该实现类转换为 {@link QueryWrapper} 对象。 + * + * @return {@link QueryWrapper} + */ + QueryWrapper toQueryWrapper(); + + /** + * 查询数据数量。 + * + * @return 数据数量 + */ + default long count() { + return baseMapper().selectCountByQuery(toQueryWrapper()); + } + + /** + * 判断数据是否存在。 + * + * @return {@code true} 数据存在,{@code false} 数据不存在 + */ + default boolean exists() { + return SqlUtil.toBool(count()); + } + + /** + * {@inheritDoc} + */ + default T one() { + return baseMapper().selectOneByQuery(toQueryWrapper()); + } + + /** + * {@inheritDoc} + */ + default R oneAs(Class asType) { + return baseMapper().selectOneByQueryAs(toQueryWrapper(), asType); + } + + /** + * 获取第一列,且第一条数据。 + * + * @return 第一列数据 + */ + default Object obj() { + return baseMapper().selectObjectByQuery(toQueryWrapper()); + } + + /** + * 获取第一列,且第一条数据并转换为指定类型,比如 {@code Long}, {@code String} 等。 + * + * @param asType 接收数据类型 + * @param 接收数据类型 + * @return 第一列数据 + */ + default R objAs(Class asType) { + return baseMapper().selectObjectByQueryAs(toQueryWrapper(), asType); + } + + /** + * 获取第一列,且第一条数据,并封装为 {@link Optional} 返回。 + * + * @return 第一列数据 + */ + default Optional objOpt() { + return Optional.ofNullable(obj()); + } + + /** + * 获取第一列,且第一条数据并转换为指定类型,比如 {@code Long}, {@code String} + * 等,封装为 {@link Optional} 返回。 + * + * @param asType 接收数据类型 + * @param 接收数据类型 + * @return 第一列数据 + */ + default Optional objAsOpt(Class asType) { + return Optional.ofNullable(objAs(asType)); + } + + /** + * 获取第一列的所有数据。 + * + * @return 第一列数据 + */ + default List objList() { + return baseMapper().selectObjectListByQuery(toQueryWrapper()); + } + + /** + * 获取第一列的所有数据,并转换为指定类型,比如 {@code Long}, {@code String} 等。 + * + * @param asType 接收数据类型 + * @param 接收数据类型 + * @return 第一列数据 + */ + default List objListAs(Class asType) { + return baseMapper().selectObjectListByQueryAs(toQueryWrapper(), asType); + } + + /** + * {@inheritDoc} + */ + default List list() { + return baseMapper().selectListByQuery(toQueryWrapper()); + } + + /** + * {@inheritDoc} + */ + default List listAs(Class asType) { + return baseMapper().selectListByQueryAs(toQueryWrapper(), asType); + } + + /** + * {@inheritDoc} + */ + default Page page(Page page) { + return baseMapper().paginate(page, toQueryWrapper()); + } + + /** + * {@inheritDoc} + */ + default Page pageAs(Page page, Class asType) { + return baseMapper().paginateAs(page, toQueryWrapper(), asType); + } + + /** + * 使用 {@code Fields Query} 的方式进行关联查询。 + * + * @return {@code Fields Query} 查询 + */ + default FieldsBuilder withFields() { + return new FieldsBuilder<>(this); + } + + /** + * 使用 {@code Relations Query} 的方式进行关联查询。 + * + * @return {@code Relations Query} 查询 + */ + default RelationsBuilder withRelations() { + return new RelationsBuilder<>(this); + } + +} diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/QueryChain.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/QueryChain.java index ded14920..5b14a0ee 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/QueryChain.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/QueryChain.java @@ -20,18 +20,17 @@ import com.mybatisflex.core.BaseMapper; import com.mybatisflex.core.paginate.Page; import com.mybatisflex.core.table.TableInfo; import com.mybatisflex.core.table.TableInfoFactory; -import com.mybatisflex.core.util.SqlUtil; import java.util.List; import java.util.Optional; /** - * {@link QueryWrapper}链式调用。 + * {@link QueryWrapper} 链式调用。 * * @author 王帅 * @since 2023-07-22 */ -public class QueryChain extends QueryWrapperAdapter> { +public class QueryChain extends QueryWrapperAdapter> implements MapperQueryChain { private final BaseMapper baseMapper; @@ -43,98 +42,76 @@ public class QueryChain extends QueryWrapperAdapter> { return new QueryChain<>(baseMapper); } - public long count() { - return baseMapper.selectCountByQuery(this); + @Override + public BaseMapper baseMapper() { + return baseMapper; } - public boolean exists() { - return SqlUtil.toBool(count()); - } - - public T one() { - return baseMapper.selectOneByQuery(this); - } - - public R oneAs(Class asType) { - return baseMapper.selectOneByQueryAs(this, asType); + @Override + public QueryWrapper toQueryWrapper() { + return this; } + /** + * @deprecated 该方法将在 1.6.0 版本移除 + */ + @Deprecated public T oneWithRelations() { return baseMapper.selectOneWithRelationsByQuery(this); } + /** + * @deprecated 该方法将在 1.6.0 版本移除 + */ + @Deprecated public R oneWithRelationsAs(Class asType) { return baseMapper.selectOneWithRelationsByQueryAs(this, asType); } - public Optional oneOpt() { - return Optional.ofNullable(baseMapper.selectOneByQuery(this)); - } - - public Optional oneAsOpt(Class asType) { - return Optional.ofNullable(baseMapper.selectOneByQueryAs(this, asType)); - } - + /** + * @deprecated 该方法将在 1.6.0 版本移除 + */ + @Deprecated public Optional oneWithRelationsOpt() { return Optional.ofNullable(baseMapper.selectOneWithRelationsByQuery(this)); } + /** + * @deprecated 该方法将在 1.6.0 版本移除 + */ + @Deprecated public Optional oneWithRelationsAsOpt(Class asType) { return Optional.ofNullable(baseMapper.selectOneWithRelationsByQueryAs(this, asType)); } - public Object obj() { - return baseMapper.selectObjectByQuery(this); - } - - public R objAs(Class asType) { - return baseMapper.selectObjectByQueryAs(this, asType); - } - - public Optional objOpt() { - return Optional.ofNullable(baseMapper.selectObjectByQuery(this)); - } - - public Optional objAsOpt(Class asType) { - return Optional.ofNullable(baseMapper.selectObjectByQueryAs(this, asType)); - } - - public List objList() { - return baseMapper.selectObjectListByQuery(this); - } - - public List objListAs(Class asType) { - return baseMapper.selectObjectListByQueryAs(this, asType); - } - - public List list() { - return baseMapper.selectListByQuery(this); - } - + /** + * @deprecated 该方法将在 1.6.0 版本移除 + */ + @Deprecated public List listWithRelations() { return baseMapper.selectListWithRelationsByQuery(this); } - public List listAs(Class asType) { - return baseMapper.selectListByQueryAs(this, asType); - } - + /** + * @deprecated 该方法将在 1.6.0 版本移除 + */ + @Deprecated public List listWithRelationsAs(Class asType) { return baseMapper.selectListWithRelationsByQueryAs(this, asType); } - public Page page(Page page) { - return baseMapper.paginate(page, this); - } - + /** + * @deprecated 该方法将在 1.6.0 版本移除 + */ + @Deprecated public Page pageWithRelations(Page page) { return baseMapper.paginateWithRelations(page, this); } - public Page pageAs(Page page, Class asType) { - return baseMapper.paginateAs(page, this, asType); - } - + /** + * @deprecated 该方法将在 1.6.0 版本移除 + */ + @Deprecated public Page pageWithRelationsAs(Page page, Class asType) { return baseMapper.paginateWithRelationsAs(page, this, asType); } @@ -145,4 +122,5 @@ public class QueryChain extends QueryWrapperAdapter> { CPI.setFromIfNecessary(this, tableInfo.getSchema(), tableInfo.getTableName()); return super.toSQL(); } + } diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/RelationsBuilder.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/RelationsBuilder.java new file mode 100644 index 00000000..d159501e --- /dev/null +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/query/RelationsBuilder.java @@ -0,0 +1,130 @@ +/* + * 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.query; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.relation.RelationManager; +import com.mybatisflex.core.util.LambdaGetter; + +import java.util.List; + +/** + * 使用 {@code Relations Query} 的方式进行关联查询。 + * + * @author 王帅 + * @since 2023-08-08 + */ +public class RelationsBuilder extends AbstractQueryBuilder { + + public RelationsBuilder(MapperQueryChain delegate) { + super(delegate); + } + + /** + * 忽略查询部分 {@code Relations} 注解标记的属性。 + * + * @param fields 属性 + * @return {@code Relations} 查询构建 + */ + public RelationsBuilder ignoreRelations(String... fields) { + RelationManager.addIgnoreRelations(fields); + return this; + } + + /** + * 忽略查询部分 {@code Relations} 注解标记的属性。 + * + * @param fields 属性 + * @return {@code Relations} 查询构建 + */ + public RelationsBuilder ignoreRelations(LambdaGetter... fields) { + RelationManager.addIgnoreRelations(fields); + return this; + } + + /** + * 设置父子关系查询中,默认的递归查询深度。 + * + * @param maxDepth 查询深度 + * @return {@code Relations} 查询构建 + */ + public RelationsBuilder maxDepth(int maxDepth) { + RelationManager.setMaxDepth(maxDepth); + return this; + } + + /** + * 添加额外的 {@code Relations} 查询条件。 + * + * @param key 键 + * @param value 值 + * @return {@code Relations} 查询构建 + */ + public RelationsBuilder extraConditionParam(String key, Object value) { + RelationManager.addExtraConditionParam(key, value); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public T one() { + return baseMapper().selectOneWithRelationsByQuery(queryWrapper()); + } + + /** + * {@inheritDoc} + */ + @Override + public R oneAs(Class asType) { + return baseMapper().selectOneWithRelationsByQueryAs(queryWrapper(), asType); + } + + /** + * {@inheritDoc} + */ + @Override + public List list() { + return baseMapper().selectListWithRelationsByQuery(queryWrapper()); + } + + /** + * {@inheritDoc} + */ + @Override + public List listAs(Class asType) { + return baseMapper().selectListWithRelationsByQueryAs(queryWrapper(), asType); + } + + /** + * {@inheritDoc} + */ + @Override + public Page page(Page page) { + return baseMapper().paginateWithRelations(page, queryWrapper()); + } + + /** + * {@inheritDoc} + */ + @Override + public Page pageAs(Page page, Class asType) { + return baseMapper().paginateWithRelationsAs(page, queryWrapper(), asType); + } + +} diff --git a/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MultiDataSourceAutoConfiguration.java b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MultiDataSourceAutoConfiguration.java index ffdddc98..f8b155ea 100644 --- a/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MultiDataSourceAutoConfiguration.java +++ b/mybatis-flex-spring-boot-starter/src/main/java/com/mybatisflex/spring/boot/MultiDataSourceAutoConfiguration.java @@ -81,18 +81,22 @@ public class MultiDataSourceAutoConfiguration { DataSourceManager.setDecipher(dataSourceDecipher); for (Map.Entry> entry : dataSourceProperties.entrySet()) { + DataSource dataSource = new DataSourceBuilder(entry.getValue()).build(); - if (seataConfig !=null &&seataConfig.isEnable()){ - if (seataConfig.getSeataMode() ==SeataMode.XA){ + DataSourceManager.decryptDataSource(dataSource); + + if (seataConfig != null && seataConfig.isEnable()) { + if (seataConfig.getSeataMode() == SeataMode.XA) { dataSource = new DataSourceProxyXA(dataSource); - }else { + } else { dataSource = new DataSourceProxy(dataSource); } } + if (flexDataSource == null) { - flexDataSource = new FlexDataSource(entry.getKey(), dataSource); + flexDataSource = new FlexDataSource(entry.getKey(), dataSource, false); } else { - flexDataSource.addDataSource(entry.getKey(), dataSource); + flexDataSource.addDataSource(entry.getKey(), dataSource, false); } } } diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-test/src/main/resources/application.yml b/mybatis-flex-test/mybatis-flex-spring-boot-test/src/main/resources/application.yml index 4dca4a42..51c5d0e4 100644 --- a/mybatis-flex-test/mybatis-flex-spring-boot-test/src/main/resources/application.yml +++ b/mybatis-flex-test/mybatis-flex-spring-boot-test/src/main/resources/application.yml @@ -3,20 +3,20 @@ spring: # h2: # console: # enabled: true -# datasource: -## driver-class-name: com.mysql.cj.jdbc.Driver -# url: jdbc:mysql://localhost:3306/flex_test -# username: root -# password: 131496 + datasource: +# driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/flex_test + username: root + password: 12345678 # driver-class-name: # datasource: # driver-class-name: org.h2.Driver # username: root # password: test - sql: - init: - schema-locations: classpath:schema.sql - data-locations: classpath:data.sql +# sql: +# init: +# schema-locations: classpath:schema.sql +# data-locations: classpath:data.sql mybatis-flex: admin-config: enable: true @@ -37,12 +37,12 @@ mybatis-flex: # username: root # password: 12345678 #mybatis-flex: - datasource: - ds3333: - url: jdbc:mysql://127.0.0.1:3306/flex_test - username: root - password: 131496 - ds2: - url: jdbc:mysql://127.0.0.1:3306/flex_test1 - username: root - password: 131496 +# datasource: +# ds3333: +# url: jdbc:mysql://127.0.0.1:3306/flex_test +# username: root +# password: 131496 +# ds2: +# url: jdbc:mysql://127.0.0.1:3306/flex_test1 +# username: root +# password: 131496 diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-test/src/test/java/com/mybatisflex/test/mapper/QueryChainTest.java b/mybatis-flex-test/mybatis-flex-spring-boot-test/src/test/java/com/mybatisflex/test/mapper/QueryChainTest.java new file mode 100644 index 00000000..1fcf639c --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-test/src/test/java/com/mybatisflex/test/mapper/QueryChainTest.java @@ -0,0 +1,85 @@ +/* + * 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.test.mapper; + +import com.mybatisflex.core.field.QueryBuilder; +import com.mybatisflex.core.query.QueryChain; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.test.model.User; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import static com.mybatisflex.test.model.table.RoleTableDef.ROLE; +import static com.mybatisflex.test.model.table.UserRoleTableDef.USER_ROLE; +import static com.mybatisflex.test.model.table.UserTableDef.USER; + +/** + * @author 王帅 + * @since 2023-08-08 + */ +@SpringBootTest +class QueryChainTest { + + @Autowired + UserMapper userMapper; + + @Test + void testFields() { + QueryBuilder builder = user -> + QueryWrapper.create() + .select() + .from(ROLE) + .where(ROLE.ROLE_ID.in( + QueryWrapper.create() + .select(USER_ROLE.ROLE_ID) + .from(USER_ROLE) + .where(USER_ROLE.USER_ID.eq(user.getUserId())) + )); + + User user1 = QueryChain.of(userMapper) + .where(USER.USER_ID.eq(1)) + .withFields() + .fieldMapping(User::getRoleList, builder) + .one(); + + User user2 = User.create() + .where(USER.USER_ID.eq(1)) + .withFields() + .fieldMapping(User::getRoleList, builder) + .one(); + + Assertions.assertEquals(user1.toString(), user2.toString()); + } + + @Test + void testRelations() { + User user1 = QueryChain.of(userMapper) + .where(USER.USER_ID.eq(2)) + .withRelations() + .one(); + + User user2 = User.create() + .where(USER.USER_ID.eq(2)) + .withRelations() + .one(); + + Assertions.assertEquals(user1.toString(), user2.toString()); + } + +}