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 视频教程 - 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)
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
<dependency>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-spring-boot-starter</artifactId>
|
||||
<version>1.7.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@ -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<T extends Model<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 数据数量
|
||||
*/
|
||||
public long count() {
|
||||
return baseMapper().selectCountByQuery(queryWrapper());
|
||||
@Override
|
||||
public BaseMapper<T> 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> 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} 查询
|
||||
*/
|
||||
@Override
|
||||
public FieldsQuery<T> withFields() {
|
||||
return new FieldsQuery<>(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 {@code Relations Query} 的方式进行关联查询。
|
||||
*
|
||||
* @return {@code Relations Query} 查询
|
||||
*/
|
||||
@Override
|
||||
public RelationsQuery<T> withRelations() {
|
||||
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;
|
||||
|
||||
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<T extends Model<T>> extends AbstractQuery<T> {
|
||||
|
||||
private final Map<String, FieldQuery> fieldQueryMap;
|
||||
public class FieldsQuery<T extends Model<T>> extends FieldsBuilder<T> {
|
||||
|
||||
public FieldsQuery(Model<T> model) {
|
||||
super(model);
|
||||
this.fieldQueryMap = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置属性对应的 {@code QueryWrapper} 查询。
|
||||
*
|
||||
* @param field 属性
|
||||
* @param builder {@code QueryWrapper} 构建
|
||||
* @param <F> 属性类型
|
||||
* @return 属性查询构建
|
||||
*/
|
||||
@Override
|
||||
public <F> FieldsQuery<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> 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);
|
||||
super.fieldMapping(field, builder);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@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() {
|
||||
List<T> entities = Collections.singletonList(baseMapper().selectOneById(pkValues()));
|
||||
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")
|
||||
public <R> R oneByIdAs(Class<R> asType) {
|
||||
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;
|
||||
|
||||
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<T extends Model<T>> extends AbstractQuery<T> {
|
||||
public class RelationsQuery<T extends Model<T>> extends RelationsBuilder<T> {
|
||||
|
||||
public RelationsQuery(Model<T> model) {
|
||||
super(model);
|
||||
}
|
||||
|
||||
/**
|
||||
* 忽略查询部分 {@code Relations} 注解标记的属性。
|
||||
*
|
||||
* @param fields 属性
|
||||
* @return {@code Relations} 查询构建
|
||||
*/
|
||||
@Override
|
||||
public RelationsQuery<T> ignoreRelations(String... fields) {
|
||||
RelationManager.addIgnoreRelations(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);
|
||||
super.ignoreRelations(fields);
|
||||
return this;
|
||||
}
|
||||
|
||||
@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() {
|
||||
return baseMapper().selectOneWithRelationsById(pkValues());
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* 根据主键查询一条数据,返回的数据为 asType 类型。
|
||||
*
|
||||
* @param asType 接收数据类型
|
||||
* @param <R> 接收数据类型
|
||||
* @return 一条数据
|
||||
*/
|
||||
public <R> R oneByIdAs(Class<R> 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 java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* @author michael
|
||||
*/
|
||||
public class DataSourceManager {
|
||||
|
||||
private static DataSourceDecipher decipher;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,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<T> extends QueryWrapperAdapter<QueryChain<T>> {
|
||||
public class QueryChain<T> extends QueryWrapperAdapter<QueryChain<T>> implements MapperQueryChain<T> {
|
||||
|
||||
private final BaseMapper<T> baseMapper;
|
||||
|
||||
@ -43,98 +42,76 @@ public class QueryChain<T> extends QueryWrapperAdapter<QueryChain<T>> {
|
||||
return new QueryChain<>(baseMapper);
|
||||
}
|
||||
|
||||
public long count() {
|
||||
return baseMapper.selectCountByQuery(this);
|
||||
@Override
|
||||
public BaseMapper<T> baseMapper() {
|
||||
return baseMapper;
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
return SqlUtil.toBool(count());
|
||||
}
|
||||
|
||||
public T one() {
|
||||
return baseMapper.selectOneByQuery(this);
|
||||
}
|
||||
|
||||
public <R> R oneAs(Class<R> 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> R oneWithRelationsAs(Class<R> asType) {
|
||||
return baseMapper.selectOneWithRelationsByQueryAs(this, asType);
|
||||
}
|
||||
|
||||
public Optional<T> oneOpt() {
|
||||
return Optional.ofNullable(baseMapper.selectOneByQuery(this));
|
||||
}
|
||||
|
||||
public <R> Optional<R> oneAsOpt(Class<R> asType) {
|
||||
return Optional.ofNullable(baseMapper.selectOneByQueryAs(this, asType));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 该方法将在 1.6.0 版本移除
|
||||
*/
|
||||
@Deprecated
|
||||
public Optional<T> oneWithRelationsOpt() {
|
||||
return Optional.ofNullable(baseMapper.selectOneWithRelationsByQuery(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 该方法将在 1.6.0 版本移除
|
||||
*/
|
||||
@Deprecated
|
||||
public <R> Optional<R> oneWithRelationsAsOpt(Class<R> asType) {
|
||||
return Optional.ofNullable(baseMapper.selectOneWithRelationsByQueryAs(this, asType));
|
||||
}
|
||||
|
||||
public Object obj() {
|
||||
return baseMapper.selectObjectByQuery(this);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 该方法将在 1.6.0 版本移除
|
||||
*/
|
||||
@Deprecated
|
||||
public List<T> listWithRelations() {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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());
|
||||
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,18 +81,22 @@ public class MultiDataSourceAutoConfiguration {
|
||||
DataSourceManager.setDecipher(dataSourceDecipher);
|
||||
|
||||
for (Map.Entry<String, Map<String, String>> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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