mirror of
https://gitee.com/mybatis-flex/mybatis-flex.git
synced 2025-12-07 00:58:24 +08:00
Merge remote-tracking branch 'origin/main' into main
This commit is contained in:
commit
ecd6ec879e
@ -84,6 +84,9 @@
|
|||||||
- [MyBatis-Flex 视频教程 - 30 数据脱敏的简单使用](https://www.bilibili.com/video/BV1gz4y1s7Wg)
|
- [MyBatis-Flex 视频教程 - 30 数据脱敏的简单使用](https://www.bilibili.com/video/BV1gz4y1s7Wg)
|
||||||
- [MyBatis-Flex 视频教程 - 31 枚举属性的使用](https://www.bilibili.com/video/BV1mm4y1W7SD)
|
- [MyBatis-Flex 视频教程 - 31 枚举属性的使用](https://www.bilibili.com/video/BV1mm4y1W7SD)
|
||||||
- [MyBatis-Flex 视频教程 - 32 关联查询(Join Query)](https://www.bilibili.com/video/BV1B8411d7iC)
|
- [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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
> 注意:若项目未使用 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` 注解。
|
> 进行事务配置,才能正常使用 `@Transactional` 注解。
|
||||||
|
|
||||||
## 特征
|
## 多数据源注意事项
|
||||||
|
|
||||||
- 1、支持嵌套事务
|
注意:在多数据源的情况下,所有数据源的数据库请求(Connection)会执行相同的 `commit` 或者 `rollback`,MyBatis-Flex 只保证了程序端的原子操作,
|
||||||
- 2、支持多数据源
|
但并不能保证多个数据源之间的原子操作。例如:
|
||||||
|
|
||||||
> 注意:在多数据源的情况下,所有数据源的数据库请求(Connection)会执行相同的 commit 或者 rollback,但并非原子操作。例如:
|
```java 1,6,13,19
|
||||||
|
|
||||||
```java
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void doSomething(){
|
public void doSomething(){
|
||||||
|
|
||||||
@ -117,7 +115,7 @@ public void doSomething(){
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
在以上的例子中,两次 `Db.update(...)` 虽然是两个不同的数据源,但它们都在同一个事务 `@Transactional` 里,因此,当抛出异常的时候,
|
在以上的例子中,执行了两次 `Db.updateBySql(...)`,它们是两个不同的数据源,但它们都在同一个事务 `@Transactional` 里,因此,当抛出异常的时候,
|
||||||
它们都会进行回滚(rollback)。
|
它们都会进行回滚(rollback)。
|
||||||
|
|
||||||
以上提到的 `并非原子操作`,指的是:
|
以上提到的 `并非原子操作`,指的是:
|
||||||
@ -127,26 +125,112 @@ public void doSomething(){
|
|||||||
|
|
||||||
## Seata 分布式事务
|
## Seata 分布式事务
|
||||||
|
|
||||||
Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
|
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。
|
||||||
|
Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
|
||||||
官方网站:https://seata.io/zh-cn/index.html
|
官方网站: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服务
|
**第 1 步:在 `application.yml` 配置开启 Seata 分布式事务功能:**
|
||||||
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)
|
```yaml
|
||||||
进行整合,微服务合并成一个多数据源进行测试,当然,这种方法是不提倡的,seata事务并且本地多数据源的方式可能本身就存在设计思路问题,可能存在过度设计,此demo只是作为一个演示。
|
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需要配置如下参数
|
```xml
|
||||||
1. `mybatis-flex.seata-config.enable`
|
<dependency>
|
||||||
|
<groupId>io.seata</groupId>
|
||||||
配置含义:seata事务是否开启,true开启,默认false关闭
|
<artifactId>seata-spring-boot-starter</artifactId>
|
||||||
2. `mybatis-flex.seata-config.seata-mode`
|
<version>1.7.0</version>
|
||||||
|
</dependency>
|
||||||
默认启动的事务类型,目前只支持XA或者AT,默认AT
|
```
|
||||||
|
|||||||
@ -16,15 +16,15 @@
|
|||||||
|
|
||||||
package com.mybatisflex.core.activerecord;
|
package com.mybatisflex.core.activerecord;
|
||||||
|
|
||||||
|
import com.mybatisflex.core.BaseMapper;
|
||||||
import com.mybatisflex.core.activerecord.query.FieldsQuery;
|
import com.mybatisflex.core.activerecord.query.FieldsQuery;
|
||||||
import com.mybatisflex.core.activerecord.query.QueryModel;
|
import com.mybatisflex.core.activerecord.query.QueryModel;
|
||||||
import com.mybatisflex.core.activerecord.query.RelationsQuery;
|
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 com.mybatisflex.core.util.SqlUtil;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Active Record 模型。
|
* Active Record 模型。
|
||||||
@ -36,7 +36,7 @@ import java.util.Optional;
|
|||||||
@SuppressWarnings({"unused", "unchecked"})
|
@SuppressWarnings({"unused", "unchecked"})
|
||||||
public abstract class Model<T extends Model<T>>
|
public abstract class Model<T extends Model<T>>
|
||||||
extends QueryModel<T>
|
extends QueryModel<T>
|
||||||
implements MapperModel<T>, Serializable {
|
implements MapperModel<T>, MapperQueryChain<T>, Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据实体类构建的条件删除数据。
|
* 根据实体类构建的条件删除数据。
|
||||||
@ -66,175 +66,22 @@ public abstract class Model<T extends Model<T>>
|
|||||||
return SqlUtil.toBool(baseMapper().updateByQuery((T) this, ignoreNulls, queryWrapper()));
|
return SqlUtil.toBool(baseMapper().updateByQuery((T) this, ignoreNulls, queryWrapper()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* 根据实体类构建的条件查询数据数量。
|
public BaseMapper<T> baseMapper() {
|
||||||
*
|
return MapperModel.super.baseMapper();
|
||||||
* @return 数据数量
|
|
||||||
*/
|
|
||||||
public long count() {
|
|
||||||
return baseMapper().selectCountByQuery(queryWrapper());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* 根据实体类构建的条件判断数据是否存在。
|
public QueryWrapper toQueryWrapper() {
|
||||||
*
|
return queryWrapper();
|
||||||
* @return {@code true} 数据存在,{@code false} 数据不存在
|
|
||||||
*/
|
|
||||||
public boolean exists() {
|
|
||||||
return SqlUtil.toBool(count());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* 根据实体类构建的条件获取一条数据。
|
|
||||||
*
|
|
||||||
* @return 数据
|
|
||||||
*/
|
|
||||||
public T one() {
|
|
||||||
return baseMapper().selectOneByQuery(queryWrapper());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取一条数据,返回的数据为 asType 类型。
|
|
||||||
*
|
|
||||||
* @param asType 接收数据类型
|
|
||||||
* @return 数据
|
|
||||||
*/
|
|
||||||
public <R> R oneAs(Class<R> asType) {
|
|
||||||
return baseMapper().selectOneByQueryAs(queryWrapper(), asType);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取一条数据,并封装为 {@link Optional} 返回。
|
|
||||||
*
|
|
||||||
* @return 数据
|
|
||||||
*/
|
|
||||||
public Optional<T> oneOpt() {
|
|
||||||
return Optional.ofNullable(one());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取一条数据,返回的数据为 asType 类型,并封装为 {@link Optional} 返回。
|
|
||||||
*
|
|
||||||
* @param asType 接收数据类型
|
|
||||||
* @return 数据
|
|
||||||
*/
|
|
||||||
public <R> Optional<R> oneAsOpt(Class<R> asType) {
|
|
||||||
return Optional.ofNullable(oneAs(asType));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取第一列,且第一条数据。
|
|
||||||
*
|
|
||||||
* @return 第一列数据
|
|
||||||
*/
|
|
||||||
public Object obj() {
|
|
||||||
return baseMapper().selectObjectByQuery(queryWrapper());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取第一列,且第一条数据并转换为指定类型,比如 {@code Long}, {@code String} 等。
|
|
||||||
*
|
|
||||||
* @param asType 接收数据类型
|
|
||||||
* @return 第一列数据
|
|
||||||
*/
|
|
||||||
public <R> R objAs(Class<R> asType) {
|
|
||||||
return baseMapper().selectObjectByQueryAs(queryWrapper(), asType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取第一列,且第一条数据,并封装为 {@link Optional} 返回。
|
|
||||||
*
|
|
||||||
* @return 第一列数据
|
|
||||||
*/
|
|
||||||
public Optional<Object> objOpt() {
|
|
||||||
return Optional.ofNullable(obj());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取第一列,且第一条数据并转换为指定类型,比如 {@code Long}, {@code String}
|
|
||||||
* 等,封装为 {@link Optional} 返回。
|
|
||||||
*
|
|
||||||
* @param asType 接收数据类型
|
|
||||||
* @return 第一列数据
|
|
||||||
*/
|
|
||||||
public <R> Optional<R> objAsOpt(Class<R> asType) {
|
|
||||||
return Optional.ofNullable(objAs(asType));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取第一列的所有数据。
|
|
||||||
*
|
|
||||||
* @return 第一列数据
|
|
||||||
*/
|
|
||||||
public List<Object> objList() {
|
|
||||||
return baseMapper().selectObjectListByQuery(queryWrapper());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取第一列的所有数据,并转换为指定类型,比如 {@code Long}, {@code String} 等。
|
|
||||||
*
|
|
||||||
* @param asType 接收数据类型
|
|
||||||
* @return 第一列数据
|
|
||||||
*/
|
|
||||||
public <R> List<R> objListAs(Class<R> asType) {
|
|
||||||
return baseMapper().selectObjectListByQueryAs(queryWrapper(), asType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取多条数据。
|
|
||||||
*
|
|
||||||
* @return 数据列表
|
|
||||||
*/
|
|
||||||
public List<T> list() {
|
|
||||||
return baseMapper().selectListByQuery(queryWrapper());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取多条数据,返回的数据为 asType 类型。
|
|
||||||
*
|
|
||||||
* @param asType 接收数据类型
|
|
||||||
* @return 数据列表
|
|
||||||
*/
|
|
||||||
public <R> List<R> listAs(Class<R> asType) {
|
|
||||||
return baseMapper().selectListByQueryAs(queryWrapper(), asType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取分页数据。
|
|
||||||
*
|
|
||||||
* @param page 分页对象
|
|
||||||
* @return 分页数据
|
|
||||||
*/
|
|
||||||
public Page<T> page(Page<T> page) {
|
|
||||||
return baseMapper().paginate(page, queryWrapper());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取分页数据,返回的数据为 asType 类型。
|
|
||||||
*
|
|
||||||
* @param page 分页对象
|
|
||||||
* @param asType 接收数据类型
|
|
||||||
* @return 分页数据
|
|
||||||
*/
|
|
||||||
public <R> Page<R> pageAs(Page<R> page, Class<R> asType) {
|
|
||||||
return baseMapper().paginateAs(page, queryWrapper(), asType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用 {@code Fields Query} 的方式进行关联查询。
|
|
||||||
*
|
|
||||||
* @return {@code Fields Query} 查询
|
|
||||||
*/
|
|
||||||
public FieldsQuery<T> withFields() {
|
public FieldsQuery<T> withFields() {
|
||||||
return new FieldsQuery<>(this);
|
return new FieldsQuery<>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* 使用 {@code Relations Query} 的方式进行关联查询。
|
|
||||||
*
|
|
||||||
* @return {@code Relations Query} 查询
|
|
||||||
*/
|
|
||||||
public RelationsQuery<T> withRelations() {
|
public RelationsQuery<T> withRelations() {
|
||||||
return new RelationsQuery<>(this);
|
return new RelationsQuery<>(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,160 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.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<T extends Model<T>> {
|
|
||||||
|
|
||||||
protected final Model<T> model;
|
|
||||||
|
|
||||||
protected AbstractQuery(Model<T> model) {
|
|
||||||
this.model = model;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return 主键
|
|
||||||
*/
|
|
||||||
protected Object[] pkValues() {
|
|
||||||
return model.pkValues();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return BaseMapper
|
|
||||||
*/
|
|
||||||
protected BaseMapper<T> baseMapper() {
|
|
||||||
return model.baseMapper();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return QueryWrapper
|
|
||||||
*/
|
|
||||||
protected QueryWrapper queryWrapper() {
|
|
||||||
return model.queryWrapper();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类主键获取一条数据。
|
|
||||||
*
|
|
||||||
* @return 数据
|
|
||||||
*/
|
|
||||||
public abstract T oneById();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类主键获取一条数据,并封装为 {@link Optional} 返回。
|
|
||||||
*
|
|
||||||
* @return 数据
|
|
||||||
*/
|
|
||||||
public Optional<T> oneByIdOpt() {
|
|
||||||
return Optional.ofNullable(oneById());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类主键获取一条数据。
|
|
||||||
*
|
|
||||||
* @return 数据
|
|
||||||
*/
|
|
||||||
public abstract <R> R oneByIdAs(Class<R> asType);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类主键获取一条数据,并封装为 {@link Optional} 返回。
|
|
||||||
*
|
|
||||||
* @return 数据
|
|
||||||
*/
|
|
||||||
public <R> Optional<R> oneByIdAsOpt(Class<R> asType) {
|
|
||||||
return Optional.ofNullable(oneByIdAs(asType));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取一条数据。
|
|
||||||
*
|
|
||||||
* @return 数据
|
|
||||||
*/
|
|
||||||
public abstract T one();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取一条数据,返回的数据为 asType 类型。
|
|
||||||
*
|
|
||||||
* @param asType 接收数据类型
|
|
||||||
* @return 数据
|
|
||||||
*/
|
|
||||||
public abstract <R> R oneAs(Class<R> asType);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取一条数据,并封装为 {@link Optional} 返回。
|
|
||||||
*
|
|
||||||
* @return 数据
|
|
||||||
*/
|
|
||||||
public Optional<T> oneOpt() {
|
|
||||||
return Optional.ofNullable(one());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取一条数据,返回的数据为 asType 类型,并封装为 {@link Optional} 返回。
|
|
||||||
*
|
|
||||||
* @param asType 接收数据类型
|
|
||||||
* @return 数据
|
|
||||||
*/
|
|
||||||
public <R> Optional<R> oneOptAs(Class<R> asType) {
|
|
||||||
return Optional.ofNullable(oneAs(asType));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取多条数据。
|
|
||||||
*
|
|
||||||
* @return 数据列表
|
|
||||||
*/
|
|
||||||
public abstract List<T> list();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取多条数据,返回的数据为 asType 类型。
|
|
||||||
*
|
|
||||||
* @param asType 接收数据类型
|
|
||||||
* @return 数据列表
|
|
||||||
*/
|
|
||||||
public abstract <R> List<R> listAs(Class<R> asType);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取分页数据。
|
|
||||||
*
|
|
||||||
* @param page 分页对象
|
|
||||||
* @return 分页数据
|
|
||||||
*/
|
|
||||||
public abstract Page<T> page(Page<T> page);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据实体类构建的条件获取分页数据,返回的数据为 asType 类型。
|
|
||||||
*
|
|
||||||
* @param page 分页对象
|
|
||||||
* @param asType 接收数据类型
|
|
||||||
* @return 分页数据
|
|
||||||
*/
|
|
||||||
public abstract <R> Page<R> pageAs(Page<R> page, Class<R> asType);
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -17,19 +17,14 @@
|
|||||||
package com.mybatisflex.core.activerecord.query;
|
package com.mybatisflex.core.activerecord.query;
|
||||||
|
|
||||||
import com.mybatisflex.core.activerecord.Model;
|
import com.mybatisflex.core.activerecord.Model;
|
||||||
import com.mybatisflex.core.field.FieldQuery;
|
|
||||||
import com.mybatisflex.core.field.FieldQueryManager;
|
import com.mybatisflex.core.field.FieldQueryManager;
|
||||||
import com.mybatisflex.core.field.QueryBuilder;
|
import com.mybatisflex.core.field.QueryBuilder;
|
||||||
import com.mybatisflex.core.mybatis.MappedStatementTypes;
|
import com.mybatisflex.core.mybatis.MappedStatementTypes;
|
||||||
import com.mybatisflex.core.paginate.Page;
|
import com.mybatisflex.core.query.FieldsBuilder;
|
||||||
import com.mybatisflex.core.util.FieldWrapper;
|
|
||||||
import com.mybatisflex.core.util.LambdaGetter;
|
import com.mybatisflex.core.util.LambdaGetter;
|
||||||
import com.mybatisflex.core.util.LambdaUtil;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用 {@code Fields Query} 的方式进行关联查询。
|
* 使用 {@code Fields Query} 的方式进行关联查询。
|
||||||
@ -37,53 +32,34 @@ import java.util.Map;
|
|||||||
* @author 王帅
|
* @author 王帅
|
||||||
* @since 2023-07-30
|
* @since 2023-07-30
|
||||||
*/
|
*/
|
||||||
public class FieldsQuery<T extends Model<T>> extends AbstractQuery<T> {
|
public class FieldsQuery<T extends Model<T>> extends FieldsBuilder<T> {
|
||||||
|
|
||||||
private final Map<String, FieldQuery> fieldQueryMap;
|
|
||||||
|
|
||||||
public FieldsQuery(Model<T> model) {
|
public FieldsQuery(Model<T> model) {
|
||||||
super(model);
|
super(model);
|
||||||
this.fieldQueryMap = new HashMap<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* 设置属性对应的 {@code QueryWrapper} 查询。
|
|
||||||
*
|
|
||||||
* @param field 属性
|
|
||||||
* @param builder {@code QueryWrapper} 构建
|
|
||||||
* @param <F> 属性类型
|
|
||||||
* @return 属性查询构建
|
|
||||||
*/
|
|
||||||
public <F> FieldsQuery<T> fieldMapping(LambdaGetter<F> field, QueryBuilder<F> builder) {
|
public <F> FieldsQuery<T> fieldMapping(LambdaGetter<F> field, QueryBuilder<F> builder) {
|
||||||
return fieldMapping(field, false, builder);
|
super.fieldMapping(field, builder);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置属性对应的 {@code QueryWrapper} 查询。
|
|
||||||
*
|
|
||||||
* @param field 属性
|
|
||||||
* @param prevent 阻止对嵌套类属性的查询
|
|
||||||
* @param builder {@code QueryWrapper} 构建
|
|
||||||
* @param <F> 属性类型
|
|
||||||
* @return 属性查询构建
|
|
||||||
*/
|
|
||||||
public <F> FieldsQuery<T> fieldMapping(LambdaGetter<F> field, boolean prevent, QueryBuilder<F> 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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
|
public <F> FieldsBuilder<T> fieldMapping(LambdaGetter<F> field, boolean prevent, QueryBuilder<F> builder) {
|
||||||
|
super.fieldMapping(field, prevent, builder);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object[] pkValues() {
|
||||||
|
// 懒加载,实际用到的时候才会生成 主键值
|
||||||
|
return ((Model<T>) delegate).pkValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主键查询一条数据。
|
||||||
|
*
|
||||||
|
* @return 一条数据
|
||||||
|
*/
|
||||||
public T oneById() {
|
public T oneById() {
|
||||||
List<T> entities = Collections.singletonList(baseMapper().selectOneById(pkValues()));
|
List<T> entities = Collections.singletonList(baseMapper().selectOneById(pkValues()));
|
||||||
FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap);
|
FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap);
|
||||||
@ -91,9 +67,12 @@ public class FieldsQuery<T extends Model<T>> extends AbstractQuery<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* 根据主键查询一条数据,返回的数据为 asType 类型。
|
||||||
|
*
|
||||||
|
* @param asType 接收数据类型
|
||||||
|
* @param <R> 接收数据类型
|
||||||
|
* @return 一条数据
|
||||||
*/
|
*/
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <R> R oneByIdAs(Class<R> asType) {
|
public <R> R oneByIdAs(Class<R> asType) {
|
||||||
try {
|
try {
|
||||||
@ -106,64 +85,4 @@ public class FieldsQuery<T extends Model<T>> extends AbstractQuery<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public T one() {
|
|
||||||
List<T> entities = Collections.singletonList(baseMapper().selectOneByQuery(queryWrapper()));
|
|
||||||
FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap);
|
|
||||||
return entities.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <R> R oneAs(Class<R> asType) {
|
|
||||||
List<R> entities = Collections.singletonList(baseMapper().selectOneByQueryAs(queryWrapper(), asType));
|
|
||||||
FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap);
|
|
||||||
return entities.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public List<T> list() {
|
|
||||||
List<T> entities = baseMapper().selectListByQuery(queryWrapper());
|
|
||||||
FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap);
|
|
||||||
return entities;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <R> List<R> listAs(Class<R> asType) {
|
|
||||||
List<R> entities = baseMapper().selectListByQueryAs(queryWrapper(), asType);
|
|
||||||
FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap);
|
|
||||||
return entities;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Page<T> page(Page<T> page) {
|
|
||||||
baseMapper().paginate(page, queryWrapper());
|
|
||||||
FieldQueryManager.queryFields(baseMapper(), page.getRecords(), fieldQueryMap);
|
|
||||||
return page;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <R> Page<R> pageAs(Page<R> page, Class<R> asType) {
|
|
||||||
baseMapper().paginateAs(page, queryWrapper(), asType);
|
|
||||||
FieldQueryManager.queryFields(baseMapper(), page.getRecords(), fieldQueryMap);
|
|
||||||
return page;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,10 +17,8 @@
|
|||||||
package com.mybatisflex.core.activerecord.query;
|
package com.mybatisflex.core.activerecord.query;
|
||||||
|
|
||||||
import com.mybatisflex.core.activerecord.Model;
|
import com.mybatisflex.core.activerecord.Model;
|
||||||
import com.mybatisflex.core.paginate.Page;
|
import com.mybatisflex.core.query.RelationsBuilder;
|
||||||
import com.mybatisflex.core.relation.RelationManager;
|
import com.mybatisflex.core.util.LambdaGetter;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用 {@code Relations Query} 的方式进行关联查询。
|
* 使用 {@code Relations Query} 的方式进行关联查询。
|
||||||
@ -28,102 +26,59 @@ import java.util.List;
|
|||||||
* @author 王帅
|
* @author 王帅
|
||||||
* @since 2023-07-30
|
* @since 2023-07-30
|
||||||
*/
|
*/
|
||||||
public class RelationsQuery<T extends Model<T>> extends AbstractQuery<T> {
|
public class RelationsQuery<T extends Model<T>> extends RelationsBuilder<T> {
|
||||||
|
|
||||||
public RelationsQuery(Model<T> model) {
|
public RelationsQuery(Model<T> model) {
|
||||||
super(model);
|
super(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* 忽略查询部分 {@code Relations} 注解标记的属性。
|
|
||||||
*
|
|
||||||
* @param fields 属性
|
|
||||||
* @return {@code Relations} 查询构建
|
|
||||||
*/
|
|
||||||
public RelationsQuery<T> ignoreRelations(String... fields) {
|
public RelationsQuery<T> ignoreRelations(String... fields) {
|
||||||
RelationManager.addIgnoreRelations(fields);
|
super.ignoreRelations(fields);
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置父子关系查询中,默认的递归查询深度。
|
|
||||||
*
|
|
||||||
* @param maxDepth 查询深度
|
|
||||||
* @return {@code Relations} 查询构建
|
|
||||||
*/
|
|
||||||
public RelationsQuery<T> maxDepth(int maxDepth) {
|
|
||||||
RelationManager.setMaxDepth(maxDepth);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加额外的 {@code Relations} 查询条件。
|
|
||||||
*
|
|
||||||
* @param key 键
|
|
||||||
* @param value 值
|
|
||||||
* @return {@code Relations} 查询构建
|
|
||||||
*/
|
|
||||||
public RelationsQuery<T> extraConditionParam(String key, Object value) {
|
|
||||||
RelationManager.addExtraConditionParam(key, value);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
public RelationsQuery<T> ignoreRelations(LambdaGetter<T>... fields) {
|
||||||
|
super.ignoreRelations(fields);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RelationsQuery<T> maxDepth(int maxDepth) {
|
||||||
|
super.maxDepth(maxDepth);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RelationsQuery<T> extraConditionParam(String key, Object value) {
|
||||||
|
super.extraConditionParam(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object[] pkValues() {
|
||||||
|
// 懒加载,实际用到的时候才会生成 主键值
|
||||||
|
return ((Model<T>) delegate).pkValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据主键查询一条数据。
|
||||||
|
*
|
||||||
|
* @return 一条数据
|
||||||
|
*/
|
||||||
public T oneById() {
|
public T oneById() {
|
||||||
return baseMapper().selectOneWithRelationsById(pkValues());
|
return baseMapper().selectOneWithRelationsById(pkValues());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
|
* 根据主键查询一条数据,返回的数据为 asType 类型。
|
||||||
|
*
|
||||||
|
* @param asType 接收数据类型
|
||||||
|
* @param <R> 接收数据类型
|
||||||
|
* @return 一条数据
|
||||||
|
*/
|
||||||
public <R> R oneByIdAs(Class<R> asType) {
|
public <R> R oneByIdAs(Class<R> asType) {
|
||||||
return baseMapper().selectOneWithRelationsByIdAs(pkValues(), asType);
|
return baseMapper().selectOneWithRelationsByIdAs(pkValues(), asType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public T one() {
|
|
||||||
return baseMapper().selectOneWithRelationsByQuery(queryWrapper());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <R> R oneAs(Class<R> asType) {
|
|
||||||
return baseMapper().selectOneWithRelationsByQueryAs(queryWrapper(), asType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public List<T> list() {
|
|
||||||
return baseMapper().selectListWithRelationsByQuery(queryWrapper());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <R> List<R> listAs(Class<R> asType) {
|
|
||||||
return baseMapper().selectListWithRelationsByQueryAs(queryWrapper(), asType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Page<T> page(Page<T> page) {
|
|
||||||
return baseMapper().paginateWithRelations(page, queryWrapper());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <R> Page<R> pageAs(Page<R> page, Class<R> asType) {
|
|
||||||
return baseMapper().paginateWithRelationsAs(page, queryWrapper(), asType);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,9 @@ import org.apache.ibatis.logging.LogFactory;
|
|||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author michael
|
||||||
|
*/
|
||||||
public class DataSourceManager {
|
public class DataSourceManager {
|
||||||
|
|
||||||
private static DataSourceDecipher decipher;
|
private static DataSourceDecipher decipher;
|
||||||
|
|||||||
@ -49,8 +49,13 @@ public class FlexDataSource extends AbstractDataSource {
|
|||||||
private final DataSource defaultDataSource;
|
private final DataSource defaultDataSource;
|
||||||
|
|
||||||
public FlexDataSource(String dataSourceKey, DataSource dataSource) {
|
public FlexDataSource(String dataSourceKey, DataSource dataSource) {
|
||||||
|
this(dataSourceKey, dataSource, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FlexDataSource(String dataSourceKey, DataSource dataSource, boolean needDecryptDataSource) {
|
||||||
|
if (needDecryptDataSource) {
|
||||||
DataSourceManager.decryptDataSource(dataSource);
|
DataSourceManager.decryptDataSource(dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
this.defaultDataSourceKey = dataSourceKey;
|
this.defaultDataSourceKey = dataSourceKey;
|
||||||
this.defaultDataSource = dataSource;
|
this.defaultDataSource = dataSource;
|
||||||
@ -61,11 +66,19 @@ public class FlexDataSource extends AbstractDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addDataSource(String dataSourceKey, DataSource dataSource) {
|
public void addDataSource(String dataSourceKey, DataSource dataSource) {
|
||||||
|
addDataSource(dataSourceKey, dataSource, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void addDataSource(String dataSourceKey, DataSource dataSource, boolean needDecryptDataSource) {
|
||||||
|
if (needDecryptDataSource) {
|
||||||
DataSourceManager.decryptDataSource(dataSource);
|
DataSourceManager.decryptDataSource(dataSource);
|
||||||
|
}
|
||||||
dataSourceMap.put(dataSourceKey, dataSource);
|
dataSourceMap.put(dataSourceKey, dataSource);
|
||||||
dbTypeHashMap.put(dataSourceKey, DbTypeUtil.getDbType(dataSource));
|
dbTypeHashMap.put(dataSourceKey, DbTypeUtil.getDbType(dataSource));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void removeDatasource(String dataSourceKey) {
|
public void removeDatasource(String dataSourceKey) {
|
||||||
dataSourceMap.remove(dataSourceKey);
|
dataSourceMap.remove(dataSourceKey);
|
||||||
dbTypeHashMap.remove(dataSourceKey);
|
dbTypeHashMap.remove(dataSourceKey);
|
||||||
|
|||||||
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* 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.BaseMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象关联查询。
|
||||||
|
*
|
||||||
|
* @author 王帅
|
||||||
|
* @since 2023-08-08
|
||||||
|
*/
|
||||||
|
public abstract class AbstractQueryBuilder<T> implements ChainQuery<T> {
|
||||||
|
|
||||||
|
protected final MapperQueryChain<T> delegate;
|
||||||
|
|
||||||
|
protected AbstractQueryBuilder(MapperQueryChain<T> delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return BaseMapper
|
||||||
|
*/
|
||||||
|
protected BaseMapper<T> baseMapper() {
|
||||||
|
return delegate.baseMapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return QueryWrapper
|
||||||
|
*/
|
||||||
|
protected QueryWrapper queryWrapper() {
|
||||||
|
return delegate.toQueryWrapper();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* 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.paginate.Page;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>链式查询接口。
|
||||||
|
*
|
||||||
|
* <p>该接口定义了通用的链式查询方法:
|
||||||
|
* <ul>
|
||||||
|
* <li><b>one</b>: 查询一条数据。
|
||||||
|
* <li><b>list</b>: 查询多条数据。
|
||||||
|
* <li><b>page</b>: 分页查询数据。
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param <T> 实体类类型
|
||||||
|
* @author 王帅
|
||||||
|
* @since 2023-08-08
|
||||||
|
*/
|
||||||
|
public interface ChainQuery<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取一条数据。
|
||||||
|
*
|
||||||
|
* @return 一条数据
|
||||||
|
*/
|
||||||
|
T one();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取一条数据,返回的数据为 asType 类型。
|
||||||
|
*
|
||||||
|
* @param asType 接收数据类型
|
||||||
|
* @param <R> 接收数据类型
|
||||||
|
* @return 一条数据
|
||||||
|
*/
|
||||||
|
<R> R oneAs(Class<R> asType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取一条数据,并封装为 {@link Optional} 返回。
|
||||||
|
*
|
||||||
|
* @return 一条数据
|
||||||
|
*/
|
||||||
|
default Optional<T> oneOpt() {
|
||||||
|
return Optional.ofNullable(one());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取一条数据,返回的数据为 asType 类型,并封装为 {@link Optional} 返回。
|
||||||
|
*
|
||||||
|
* @param asType 接收数据类型
|
||||||
|
* @param <R> 接收数据类型
|
||||||
|
* @return 一条数据
|
||||||
|
*/
|
||||||
|
default <R> Optional<R> oneAsOpt(Class<R> asType) {
|
||||||
|
return Optional.ofNullable(oneAs(asType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取多条数据。
|
||||||
|
*
|
||||||
|
* @return 数据列表
|
||||||
|
*/
|
||||||
|
List<T> list();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取多条数据,返回的数据为 asType 类型。
|
||||||
|
*
|
||||||
|
* @param asType 接收数据类型
|
||||||
|
* @param <R> 接收数据类型
|
||||||
|
* @return 数据列表
|
||||||
|
*/
|
||||||
|
<R> List<R> listAs(Class<R> asType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分页数据。
|
||||||
|
*
|
||||||
|
* @param page 分页对象
|
||||||
|
* @return 分页数据
|
||||||
|
*/
|
||||||
|
Page<T> page(Page<T> page);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分页数据,返回的数据为 asType 类型。
|
||||||
|
*
|
||||||
|
* @param page 分页对象
|
||||||
|
* @param asType 接收数据类型
|
||||||
|
* @param <R> 接收数据类型
|
||||||
|
* @return 分页数据
|
||||||
|
*/
|
||||||
|
<R> Page<R> pageAs(Page<R> page, Class<R> asType);
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* 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.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<T> extends AbstractQueryBuilder<T> {
|
||||||
|
|
||||||
|
protected final Map<String, FieldQuery> fieldQueryMap;
|
||||||
|
|
||||||
|
public FieldsBuilder(MapperQueryChain<T> delegate) {
|
||||||
|
super(delegate);
|
||||||
|
this.fieldQueryMap = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置属性对应的 {@code QueryWrapper} 查询。
|
||||||
|
*
|
||||||
|
* @param field 属性
|
||||||
|
* @param builder {@code QueryWrapper} 构建
|
||||||
|
* @param <F> 属性类型
|
||||||
|
* @return 属性查询构建
|
||||||
|
*/
|
||||||
|
public <F> FieldsBuilder<T> fieldMapping(LambdaGetter<F> field, QueryBuilder<F> builder) {
|
||||||
|
return fieldMapping(field, false, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置属性对应的 {@code QueryWrapper} 查询。
|
||||||
|
*
|
||||||
|
* @param field 属性
|
||||||
|
* @param prevent 阻止对嵌套类属性的查询
|
||||||
|
* @param builder {@code QueryWrapper} 构建
|
||||||
|
* @param <F> 属性类型
|
||||||
|
* @return 属性查询构建
|
||||||
|
*/
|
||||||
|
public <F> FieldsBuilder<T> fieldMapping(LambdaGetter<F> field, boolean prevent, QueryBuilder<F> 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<T> entities = Collections.singletonList(baseMapper().selectOneByQuery(queryWrapper()));
|
||||||
|
FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap);
|
||||||
|
return entities.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <R> R oneAs(Class<R> asType) {
|
||||||
|
List<R> entities = Collections.singletonList(baseMapper().selectOneByQueryAs(queryWrapper(), asType));
|
||||||
|
FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap);
|
||||||
|
return entities.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<T> list() {
|
||||||
|
List<T> entities = baseMapper().selectListByQuery(queryWrapper());
|
||||||
|
FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap);
|
||||||
|
return entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <R> List<R> listAs(Class<R> asType) {
|
||||||
|
List<R> entities = baseMapper().selectListByQueryAs(queryWrapper(), asType);
|
||||||
|
FieldQueryManager.queryFields(baseMapper(), entities, fieldQueryMap);
|
||||||
|
return entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Page<T> page(Page<T> page) {
|
||||||
|
baseMapper().paginate(page, queryWrapper());
|
||||||
|
FieldQueryManager.queryFields(baseMapper(), page.getRecords(), fieldQueryMap);
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <R> Page<R> pageAs(Page<R> page, Class<R> asType) {
|
||||||
|
baseMapper().paginateAs(page, queryWrapper(), asType);
|
||||||
|
FieldQueryManager.queryFields(baseMapper(), page.getRecords(), fieldQueryMap);
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
* 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.BaseMapper;
|
||||||
|
import com.mybatisflex.core.paginate.Page;
|
||||||
|
import com.mybatisflex.core.util.SqlUtil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>链式 {@link BaseMapper} 查询。
|
||||||
|
*
|
||||||
|
* <p>要求实现类除了包含有 {@link BaseMapper} 接口的引用外,还必须具有 {@link QueryWrapper}
|
||||||
|
* 的查询条件构建功能。在使用时:
|
||||||
|
* <ul>
|
||||||
|
* <li>通过 {@link #baseMapper()} 获取该实现类对应的 {@link BaseMapper} 引用。
|
||||||
|
* <li>通过 {@link #toQueryWrapper()} 将该实现类转换为 {@link QueryWrapper} 对象。
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param <T> 实体类类型
|
||||||
|
* @author 王帅
|
||||||
|
* @since 2023-08-08
|
||||||
|
*/
|
||||||
|
public interface MapperQueryChain<T> extends ChainQuery<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该实现类对应的 {@link BaseMapper} 对象。
|
||||||
|
*
|
||||||
|
* @return {@link BaseMapper}
|
||||||
|
*/
|
||||||
|
BaseMapper<T> 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> R oneAs(Class<R> asType) {
|
||||||
|
return baseMapper().selectOneByQueryAs(toQueryWrapper(), asType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取第一列,且第一条数据。
|
||||||
|
*
|
||||||
|
* @return 第一列数据
|
||||||
|
*/
|
||||||
|
default Object obj() {
|
||||||
|
return baseMapper().selectObjectByQuery(toQueryWrapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取第一列,且第一条数据并转换为指定类型,比如 {@code Long}, {@code String} 等。
|
||||||
|
*
|
||||||
|
* @param asType 接收数据类型
|
||||||
|
* @param <R> 接收数据类型
|
||||||
|
* @return 第一列数据
|
||||||
|
*/
|
||||||
|
default <R> R objAs(Class<R> asType) {
|
||||||
|
return baseMapper().selectObjectByQueryAs(toQueryWrapper(), asType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取第一列,且第一条数据,并封装为 {@link Optional} 返回。
|
||||||
|
*
|
||||||
|
* @return 第一列数据
|
||||||
|
*/
|
||||||
|
default Optional<Object> objOpt() {
|
||||||
|
return Optional.ofNullable(obj());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取第一列,且第一条数据并转换为指定类型,比如 {@code Long}, {@code String}
|
||||||
|
* 等,封装为 {@link Optional} 返回。
|
||||||
|
*
|
||||||
|
* @param asType 接收数据类型
|
||||||
|
* @param <R> 接收数据类型
|
||||||
|
* @return 第一列数据
|
||||||
|
*/
|
||||||
|
default <R> Optional<R> objAsOpt(Class<R> asType) {
|
||||||
|
return Optional.ofNullable(objAs(asType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取第一列的所有数据。
|
||||||
|
*
|
||||||
|
* @return 第一列数据
|
||||||
|
*/
|
||||||
|
default List<Object> objList() {
|
||||||
|
return baseMapper().selectObjectListByQuery(toQueryWrapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取第一列的所有数据,并转换为指定类型,比如 {@code Long}, {@code String} 等。
|
||||||
|
*
|
||||||
|
* @param asType 接收数据类型
|
||||||
|
* @param <R> 接收数据类型
|
||||||
|
* @return 第一列数据
|
||||||
|
*/
|
||||||
|
default <R> List<R> objListAs(Class<R> asType) {
|
||||||
|
return baseMapper().selectObjectListByQueryAs(toQueryWrapper(), asType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
default List<T> list() {
|
||||||
|
return baseMapper().selectListByQuery(toQueryWrapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
default <R> List<R> listAs(Class<R> asType) {
|
||||||
|
return baseMapper().selectListByQueryAs(toQueryWrapper(), asType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
default Page<T> page(Page<T> page) {
|
||||||
|
return baseMapper().paginate(page, toQueryWrapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
default <R> Page<R> pageAs(Page<R> page, Class<R> asType) {
|
||||||
|
return baseMapper().paginateAs(page, toQueryWrapper(), asType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用 {@code Fields Query} 的方式进行关联查询。
|
||||||
|
*
|
||||||
|
* @return {@code Fields Query} 查询
|
||||||
|
*/
|
||||||
|
default FieldsBuilder<T> withFields() {
|
||||||
|
return new FieldsBuilder<>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用 {@code Relations Query} 的方式进行关联查询。
|
||||||
|
*
|
||||||
|
* @return {@code Relations Query} 查询
|
||||||
|
*/
|
||||||
|
default RelationsBuilder<T> withRelations() {
|
||||||
|
return new RelationsBuilder<>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -20,7 +20,6 @@ import com.mybatisflex.core.BaseMapper;
|
|||||||
import com.mybatisflex.core.paginate.Page;
|
import com.mybatisflex.core.paginate.Page;
|
||||||
import com.mybatisflex.core.table.TableInfo;
|
import com.mybatisflex.core.table.TableInfo;
|
||||||
import com.mybatisflex.core.table.TableInfoFactory;
|
import com.mybatisflex.core.table.TableInfoFactory;
|
||||||
import com.mybatisflex.core.util.SqlUtil;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -31,7 +30,7 @@ import java.util.Optional;
|
|||||||
* @author 王帅
|
* @author 王帅
|
||||||
* @since 2023-07-22
|
* @since 2023-07-22
|
||||||
*/
|
*/
|
||||||
public class QueryChain<T> extends QueryWrapperAdapter<QueryChain<T>> {
|
public class QueryChain<T> extends QueryWrapperAdapter<QueryChain<T>> implements MapperQueryChain<T> {
|
||||||
|
|
||||||
private final BaseMapper<T> baseMapper;
|
private final BaseMapper<T> baseMapper;
|
||||||
|
|
||||||
@ -43,98 +42,76 @@ public class QueryChain<T> extends QueryWrapperAdapter<QueryChain<T>> {
|
|||||||
return new QueryChain<>(baseMapper);
|
return new QueryChain<>(baseMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long count() {
|
@Override
|
||||||
return baseMapper.selectCountByQuery(this);
|
public BaseMapper<T> baseMapper() {
|
||||||
|
return baseMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean exists() {
|
@Override
|
||||||
return SqlUtil.toBool(count());
|
public QueryWrapper toQueryWrapper() {
|
||||||
}
|
return this;
|
||||||
|
|
||||||
public T one() {
|
|
||||||
return baseMapper.selectOneByQuery(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <R> R oneAs(Class<R> asType) {
|
|
||||||
return baseMapper.selectOneByQueryAs(this, asType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated 该方法将在 1.6.0 版本移除
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public T oneWithRelations() {
|
public T oneWithRelations() {
|
||||||
return baseMapper.selectOneWithRelationsByQuery(this);
|
return baseMapper.selectOneWithRelationsByQuery(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated 该方法将在 1.6.0 版本移除
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public <R> R oneWithRelationsAs(Class<R> asType) {
|
public <R> R oneWithRelationsAs(Class<R> asType) {
|
||||||
return baseMapper.selectOneWithRelationsByQueryAs(this, asType);
|
return baseMapper.selectOneWithRelationsByQueryAs(this, asType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<T> oneOpt() {
|
/**
|
||||||
return Optional.ofNullable(baseMapper.selectOneByQuery(this));
|
* @deprecated 该方法将在 1.6.0 版本移除
|
||||||
}
|
*/
|
||||||
|
@Deprecated
|
||||||
public <R> Optional<R> oneAsOpt(Class<R> asType) {
|
|
||||||
return Optional.ofNullable(baseMapper.selectOneByQueryAs(this, asType));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<T> oneWithRelationsOpt() {
|
public Optional<T> oneWithRelationsOpt() {
|
||||||
return Optional.ofNullable(baseMapper.selectOneWithRelationsByQuery(this));
|
return Optional.ofNullable(baseMapper.selectOneWithRelationsByQuery(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated 该方法将在 1.6.0 版本移除
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public <R> Optional<R> oneWithRelationsAsOpt(Class<R> asType) {
|
public <R> Optional<R> oneWithRelationsAsOpt(Class<R> asType) {
|
||||||
return Optional.ofNullable(baseMapper.selectOneWithRelationsByQueryAs(this, asType));
|
return Optional.ofNullable(baseMapper.selectOneWithRelationsByQueryAs(this, asType));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object obj() {
|
/**
|
||||||
return baseMapper.selectObjectByQuery(this);
|
* @deprecated 该方法将在 1.6.0 版本移除
|
||||||
}
|
*/
|
||||||
|
@Deprecated
|
||||||
public <R> R objAs(Class<R> asType) {
|
|
||||||
return baseMapper.selectObjectByQueryAs(this, asType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<Object> objOpt() {
|
|
||||||
return Optional.ofNullable(baseMapper.selectObjectByQuery(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
public <R> Optional<R> objAsOpt(Class<R> asType) {
|
|
||||||
return Optional.ofNullable(baseMapper.selectObjectByQueryAs(this, asType));
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Object> objList() {
|
|
||||||
return baseMapper.selectObjectListByQuery(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <R> List<R> objListAs(Class<R> asType) {
|
|
||||||
return baseMapper.selectObjectListByQueryAs(this, asType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<T> list() {
|
|
||||||
return baseMapper.selectListByQuery(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<T> listWithRelations() {
|
public List<T> listWithRelations() {
|
||||||
return baseMapper.selectListWithRelationsByQuery(this);
|
return baseMapper.selectListWithRelationsByQuery(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <R> List<R> listAs(Class<R> asType) {
|
/**
|
||||||
return baseMapper.selectListByQueryAs(this, asType);
|
* @deprecated 该方法将在 1.6.0 版本移除
|
||||||
}
|
*/
|
||||||
|
@Deprecated
|
||||||
public <R> List<R> listWithRelationsAs(Class<R> asType) {
|
public <R> List<R> listWithRelationsAs(Class<R> asType) {
|
||||||
return baseMapper.selectListWithRelationsByQueryAs(this, asType);
|
return baseMapper.selectListWithRelationsByQueryAs(this, asType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Page<T> page(Page<T> page) {
|
/**
|
||||||
return baseMapper.paginate(page, this);
|
* @deprecated 该方法将在 1.6.0 版本移除
|
||||||
}
|
*/
|
||||||
|
@Deprecated
|
||||||
public Page<T> pageWithRelations(Page<T> page) {
|
public Page<T> pageWithRelations(Page<T> page) {
|
||||||
return baseMapper.paginateWithRelations(page, this);
|
return baseMapper.paginateWithRelations(page, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <R> Page<R> pageAs(Page<R> page, Class<R> asType) {
|
/**
|
||||||
return baseMapper.paginateAs(page, this, asType);
|
* @deprecated 该方法将在 1.6.0 版本移除
|
||||||
}
|
*/
|
||||||
|
@Deprecated
|
||||||
public <R> Page<R> pageWithRelationsAs(Page<R> page, Class<R> asType) {
|
public <R> Page<R> pageWithRelationsAs(Page<R> page, Class<R> asType) {
|
||||||
return baseMapper.paginateWithRelationsAs(page, this, asType);
|
return baseMapper.paginateWithRelationsAs(page, this, asType);
|
||||||
}
|
}
|
||||||
@ -145,4 +122,5 @@ public class QueryChain<T> extends QueryWrapperAdapter<QueryChain<T>> {
|
|||||||
CPI.setFromIfNecessary(this, tableInfo.getSchema(), tableInfo.getTableName());
|
CPI.setFromIfNecessary(this, tableInfo.getSchema(), tableInfo.getTableName());
|
||||||
return super.toSQL();
|
return super.toSQL();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* 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.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<T> extends AbstractQueryBuilder<T> {
|
||||||
|
|
||||||
|
public RelationsBuilder(MapperQueryChain<T> delegate) {
|
||||||
|
super(delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 忽略查询部分 {@code Relations} 注解标记的属性。
|
||||||
|
*
|
||||||
|
* @param fields 属性
|
||||||
|
* @return {@code Relations} 查询构建
|
||||||
|
*/
|
||||||
|
public RelationsBuilder<T> ignoreRelations(String... fields) {
|
||||||
|
RelationManager.addIgnoreRelations(fields);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 忽略查询部分 {@code Relations} 注解标记的属性。
|
||||||
|
*
|
||||||
|
* @param fields 属性
|
||||||
|
* @return {@code Relations} 查询构建
|
||||||
|
*/
|
||||||
|
public RelationsBuilder<T> ignoreRelations(LambdaGetter<T>... fields) {
|
||||||
|
RelationManager.addIgnoreRelations(fields);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置父子关系查询中,默认的递归查询深度。
|
||||||
|
*
|
||||||
|
* @param maxDepth 查询深度
|
||||||
|
* @return {@code Relations} 查询构建
|
||||||
|
*/
|
||||||
|
public RelationsBuilder<T> maxDepth(int maxDepth) {
|
||||||
|
RelationManager.setMaxDepth(maxDepth);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加额外的 {@code Relations} 查询条件。
|
||||||
|
*
|
||||||
|
* @param key 键
|
||||||
|
* @param value 值
|
||||||
|
* @return {@code Relations} 查询构建
|
||||||
|
*/
|
||||||
|
public RelationsBuilder<T> extraConditionParam(String key, Object value) {
|
||||||
|
RelationManager.addExtraConditionParam(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public T one() {
|
||||||
|
return baseMapper().selectOneWithRelationsByQuery(queryWrapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <R> R oneAs(Class<R> asType) {
|
||||||
|
return baseMapper().selectOneWithRelationsByQueryAs(queryWrapper(), asType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<T> list() {
|
||||||
|
return baseMapper().selectListWithRelationsByQuery(queryWrapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <R> List<R> listAs(Class<R> asType) {
|
||||||
|
return baseMapper().selectListWithRelationsByQueryAs(queryWrapper(), asType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Page<T> page(Page<T> page) {
|
||||||
|
return baseMapper().paginateWithRelations(page, queryWrapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <R> Page<R> pageAs(Page<R> page, Class<R> asType) {
|
||||||
|
return baseMapper().paginateWithRelationsAs(page, queryWrapper(), asType);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -81,7 +81,10 @@ public class MultiDataSourceAutoConfiguration {
|
|||||||
DataSourceManager.setDecipher(dataSourceDecipher);
|
DataSourceManager.setDecipher(dataSourceDecipher);
|
||||||
|
|
||||||
for (Map.Entry<String, Map<String, String>> entry : dataSourceProperties.entrySet()) {
|
for (Map.Entry<String, Map<String, String>> entry : dataSourceProperties.entrySet()) {
|
||||||
|
|
||||||
DataSource dataSource = new DataSourceBuilder(entry.getValue()).build();
|
DataSource dataSource = new DataSourceBuilder(entry.getValue()).build();
|
||||||
|
DataSourceManager.decryptDataSource(dataSource);
|
||||||
|
|
||||||
if (seataConfig != null && seataConfig.isEnable()) {
|
if (seataConfig != null && seataConfig.isEnable()) {
|
||||||
if (seataConfig.getSeataMode() == SeataMode.XA) {
|
if (seataConfig.getSeataMode() == SeataMode.XA) {
|
||||||
dataSource = new DataSourceProxyXA(dataSource);
|
dataSource = new DataSourceProxyXA(dataSource);
|
||||||
@ -89,10 +92,11 @@ public class MultiDataSourceAutoConfiguration {
|
|||||||
dataSource = new DataSourceProxy(dataSource);
|
dataSource = new DataSourceProxy(dataSource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flexDataSource == null) {
|
if (flexDataSource == null) {
|
||||||
flexDataSource = new FlexDataSource(entry.getKey(), dataSource);
|
flexDataSource = new FlexDataSource(entry.getKey(), dataSource, false);
|
||||||
} else {
|
} else {
|
||||||
flexDataSource.addDataSource(entry.getKey(), dataSource);
|
flexDataSource.addDataSource(entry.getKey(), dataSource, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,20 +3,20 @@ spring:
|
|||||||
# h2:
|
# h2:
|
||||||
# console:
|
# console:
|
||||||
# enabled: true
|
# enabled: true
|
||||||
# datasource:
|
datasource:
|
||||||
## driver-class-name: com.mysql.cj.jdbc.Driver
|
# driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
# url: jdbc:mysql://localhost:3306/flex_test
|
url: jdbc:mysql://localhost:3306/flex_test
|
||||||
# username: root
|
username: root
|
||||||
# password: 131496
|
password: 12345678
|
||||||
# driver-class-name:
|
# driver-class-name:
|
||||||
# datasource:
|
# datasource:
|
||||||
# driver-class-name: org.h2.Driver
|
# driver-class-name: org.h2.Driver
|
||||||
# username: root
|
# username: root
|
||||||
# password: test
|
# password: test
|
||||||
sql:
|
# sql:
|
||||||
init:
|
# init:
|
||||||
schema-locations: classpath:schema.sql
|
# schema-locations: classpath:schema.sql
|
||||||
data-locations: classpath:data.sql
|
# data-locations: classpath:data.sql
|
||||||
mybatis-flex:
|
mybatis-flex:
|
||||||
admin-config:
|
admin-config:
|
||||||
enable: true
|
enable: true
|
||||||
@ -37,12 +37,12 @@ mybatis-flex:
|
|||||||
# username: root
|
# username: root
|
||||||
# password: 12345678
|
# password: 12345678
|
||||||
#mybatis-flex:
|
#mybatis-flex:
|
||||||
datasource:
|
# datasource:
|
||||||
ds3333:
|
# ds3333:
|
||||||
url: jdbc:mysql://127.0.0.1:3306/flex_test
|
# url: jdbc:mysql://127.0.0.1:3306/flex_test
|
||||||
username: root
|
# username: root
|
||||||
password: 131496
|
# password: 131496
|
||||||
ds2:
|
# ds2:
|
||||||
url: jdbc:mysql://127.0.0.1:3306/flex_test1
|
# url: jdbc:mysql://127.0.0.1:3306/flex_test1
|
||||||
username: root
|
# username: root
|
||||||
password: 131496
|
# password: 131496
|
||||||
|
|||||||
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
|
||||||
|
* <p>
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.mybatisflex.test.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<User> 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user