Compare commits
10 Commits
53961b00ac
...
1b61e13205
Author | SHA1 | Date | |
---|---|---|---|
1b61e13205 | |||
a2afbd8753 | |||
75d302a510 | |||
df4349b774 | |||
4f8384ee88 | |||
b9a34446ae | |||
da1977a301 | |||
1cf36280ec | |||
a0da723f5e | |||
073931b000 |
50
README.md
50
README.md
@ -1,4 +1,24 @@
|
|||||||
# fluent-sql
|
# 前言
|
||||||
|
|
||||||
|
不知道大家有没有这样的感觉,就笔者而言,用了这么多年的Mybatis,再怎么说也应该“日久生情”了吧,结果可惜啊可惜,Mybatis这尊大佛还是让我爱不起来。随着开发水平的提高和对写代码这件事情本身如何更为优雅的深入研究后,让早已真正爱上面向对象模式开发的我,硬着头皮偶尔得**强制**切换到面向SQL编程,属实是令人头秃。这不,我这次任性了一回,手撸了一个java代码写SQL的东东,这就是**Fluent SQL。**
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# **这是个啥**
|
||||||
|
|
||||||
|
大家肯定很好奇Fluent SQL是个啥,咱们先从Fluent来理解。
|
||||||
|
|
||||||
|
| fluent | 英[ˈfluːənt] 美[ˈfluːənt] |
|
||||||
|
| ------ | ------------------------------------------------------------ |
|
||||||
|
| adj. | (说话)流利的; (文体) 流畅的; (动作、曲线等) 优美自然的; (河水等) 畅流的; |
|
||||||
|
|
||||||
|
顾名思义,我们想要写出**”优美自然的SQL“**,同时又想要**流畅地**写出SQL,那就快来试试我这个组件吧。组件很轻量级,可以非常快速集成项目中,可以用于替换mybatis的XML。
|
||||||
|
|
||||||
|
作为Java程序员,首选是使用Java代码来处理业务,而非将大多业务都放在XML里,面向SQL编程。都2302年了,我们也该寻求点不同的东西了。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 组件介绍
|
||||||
|
|
||||||
基于Fluent Api实现的SQL构建器,秒杀mybatis plus的存在,易用性的API让你爽到飞起。
|
基于Fluent Api实现的SQL构建器,秒杀mybatis plus的存在,易用性的API让你爽到飞起。
|
||||||
|
|
||||||
@ -59,6 +79,24 @@ public class FluentSqlConfig {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 单表查询
|
||||||
|
单表查询可以省略结果映射,程序自动使用主表实体进行自动映射
|
||||||
|
|
||||||
|
```java
|
||||||
|
class Test {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// 单表查询所有字段
|
||||||
|
List<SaasTenant> tenants = select().from(SaasTenant.class).list();
|
||||||
|
// 单表查询指定字段,条件 年龄小于50
|
||||||
|
List<SaasTenant> points = select(SaasTenant::getId, SaasTenant::getName).from(SaasTenant.class)
|
||||||
|
.matching(where(SaasTenant::getAge).lt(50))
|
||||||
|
.list();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
## 对比直接书写SQL
|
## 对比直接书写SQL
|
||||||
|
|
||||||
本小组件主要解决的是sql的书写问题,旨在用更加优雅的方式实现sql,并且不用再担心数据库方言(SQL Dialect)
|
本小组件主要解决的是sql的书写问题,旨在用更加优雅的方式实现sql,并且不用再担心数据库方言(SQL Dialect)
|
||||||
@ -123,11 +161,11 @@ public class TestSql {
|
|||||||
|
|
||||||
建议使用静态导入,美观代码,如下:
|
建议使用静态导入,美观代码,如下:
|
||||||
|
|
||||||
1. SQL.select => select
|
1. `SQL.select` => `select`
|
||||||
2. SelectComposite.composite => composite
|
2. `SelectComposite.composite` => `composite`
|
||||||
3. SelectComposite.all => all
|
3. `SelectComposite.all` => `all`
|
||||||
4. Order.by => by
|
4. `Order.by` => `by`
|
||||||
5. Query.where => where
|
5. `Query.where` => `where`
|
||||||
|
|
||||||
为了方便演示,我们下面的代码都基于静态导入函数:
|
为了方便演示,我们下面的代码都基于静态导入函数:
|
||||||
|
|
||||||
|
@ -33,6 +33,11 @@
|
|||||||
<groupId>javax.persistence</groupId>
|
<groupId>javax.persistence</groupId>
|
||||||
<artifactId>persistence-api</artifactId>
|
<artifactId>persistence-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.projectreactor</groupId>
|
||||||
|
<artifactId>reactor-core</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
@ -3,10 +3,15 @@ package group.flyfish.fluent.chain;
|
|||||||
import group.flyfish.fluent.chain.common.AfterJoinSqlChain;
|
import group.flyfish.fluent.chain.common.AfterJoinSqlChain;
|
||||||
import group.flyfish.fluent.chain.common.HandleSqlChain;
|
import group.flyfish.fluent.chain.common.HandleSqlChain;
|
||||||
import group.flyfish.fluent.chain.common.PreSqlChain;
|
import group.flyfish.fluent.chain.common.PreSqlChain;
|
||||||
|
import group.flyfish.fluent.chain.execution.BoundEntity;
|
||||||
|
import group.flyfish.fluent.chain.execution.BoundProxy;
|
||||||
|
import group.flyfish.fluent.chain.execution.ReactiveBoundEntity;
|
||||||
import group.flyfish.fluent.chain.select.AfterOrderSqlChain;
|
import group.flyfish.fluent.chain.select.AfterOrderSqlChain;
|
||||||
import group.flyfish.fluent.chain.select.AfterWhereSqlChain;
|
import group.flyfish.fluent.chain.select.AfterWhereSqlChain;
|
||||||
|
import group.flyfish.fluent.chain.select.PieceSqlChain;
|
||||||
import group.flyfish.fluent.chain.update.AfterSetSqlChain;
|
import group.flyfish.fluent.chain.update.AfterSetSqlChain;
|
||||||
import group.flyfish.fluent.debug.FluentSqlDebugger;
|
import group.flyfish.fluent.debug.FluentSqlDebugger;
|
||||||
|
import group.flyfish.fluent.entity.DataPage;
|
||||||
import group.flyfish.fluent.entity.SQLEntity;
|
import group.flyfish.fluent.entity.SQLEntity;
|
||||||
import group.flyfish.fluent.operations.FluentSQLOperations;
|
import group.flyfish.fluent.operations.FluentSQLOperations;
|
||||||
import group.flyfish.fluent.query.JoinCandidate;
|
import group.flyfish.fluent.query.JoinCandidate;
|
||||||
@ -20,13 +25,19 @@ import group.flyfish.fluent.utils.sql.ConcatSegment;
|
|||||||
import group.flyfish.fluent.utils.sql.EntityNameUtils;
|
import group.flyfish.fluent.utils.sql.EntityNameUtils;
|
||||||
import group.flyfish.fluent.utils.sql.SFunction;
|
import group.flyfish.fluent.utils.sql.SFunction;
|
||||||
import group.flyfish.fluent.utils.sql.SqlNameUtils;
|
import group.flyfish.fluent.utils.sql.SqlNameUtils;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static group.flyfish.fluent.utils.cache.CachedWrapper.wrap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询工具类
|
* 查询工具类
|
||||||
*
|
*
|
||||||
@ -36,12 +47,16 @@ final class SQLImpl extends ConcatSegment<SQLImpl> implements SQLOperations, Pre
|
|||||||
|
|
||||||
// 共享的操作
|
// 共享的操作
|
||||||
private static FluentSQLOperations SHARED_OPERATIONS;
|
private static FluentSQLOperations SHARED_OPERATIONS;
|
||||||
|
|
||||||
// 参数map,有序
|
// 参数map,有序
|
||||||
private final List<Object> parameters = new ArrayList<>();
|
private final List<Object> parameters = new ArrayList<>();
|
||||||
|
|
||||||
// 主表class,默认是第一个from的表为主表
|
// 主表class,默认是第一个from的表为主表
|
||||||
private Class<?> primaryClass;
|
private Class<?> primaryClass;
|
||||||
|
|
||||||
|
// sql实体提供者
|
||||||
|
private final Supplier<SQLEntity> entity = wrap(this::toEntity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 绑定实现类
|
* 绑定实现类
|
||||||
*
|
*
|
||||||
@ -141,14 +156,24 @@ final class SQLImpl extends ConcatSegment<SQLImpl> implements SQLOperations, Pre
|
|||||||
return concat("ON").concat(query);
|
return concat("ON").concat(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下一步操作
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public HandleSqlChain then() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 不带连接条件
|
* 不带连接条件
|
||||||
*
|
*
|
||||||
* @return 处理链
|
* @return 处理链
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public HandleSqlChain then() {
|
public <T> BoundProxy<T> next() {
|
||||||
return this;
|
return new DefaultBoundProxy<>(SQLEntity.of(primaryClass, this::sql, this::parsedParameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,6 +182,7 @@ final class SQLImpl extends ConcatSegment<SQLImpl> implements SQLOperations, Pre
|
|||||||
* @param query 条件
|
* @param query 条件
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public AfterWhereSqlChain matching(Query query) {
|
public AfterWhereSqlChain matching(Query query) {
|
||||||
if (withoutParameter(query)) return this;
|
if (withoutParameter(query)) return this;
|
||||||
return concat("WHERE").concat(query);
|
return concat("WHERE").concat(query);
|
||||||
@ -178,45 +204,14 @@ final class SQLImpl extends ConcatSegment<SQLImpl> implements SQLOperations, Pre
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> T one() {
|
|
||||||
return one(SqlNameUtils.cast(primaryClass));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行并获取结果
|
* 获取实体,做下一步的事情
|
||||||
*
|
*
|
||||||
* @param clazz 结果类
|
* @return 结果
|
||||||
* @param <T> 泛型
|
|
||||||
*/
|
|
||||||
public <T> T one(Class<T> clazz) {
|
|
||||||
return SHARED_OPERATIONS.selectOne(entity(), clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> List<T> list() {
|
|
||||||
return list(SqlNameUtils.cast(primaryClass));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行并获取多条结果
|
|
||||||
*
|
|
||||||
* @param clazz 结果类
|
|
||||||
* @return 结果列表
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public <T> List<T> list(Class<T> clazz) {
|
public <T> BoundProxy<T> as(Class<T> type) {
|
||||||
return SHARED_OPERATIONS.select(entity(), clazz);
|
return new DefaultBoundProxy<>(SQLEntity.of(type, wrap(this::sql), wrap(this::parsedParameters)));
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行并获取更新条数
|
|
||||||
*
|
|
||||||
* @return 更新条数
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int execute() {
|
|
||||||
return SHARED_OPERATIONS.execute(entity());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -259,12 +254,80 @@ final class SQLImpl extends ConcatSegment<SQLImpl> implements SQLOperations, Pre
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PieceSqlChain limit(int count) {
|
||||||
|
return concat("LIMIT").concat(String.valueOf(count));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PieceSqlChain offset(int rows) {
|
||||||
|
return concat("OFFSET").concat(String.valueOf(rows));
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
private static class DefaultBoundProxy<T> implements BoundProxy<T> {
|
||||||
|
|
||||||
|
private final SQLEntity<T> entity;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BoundEntity<T> block() {
|
||||||
|
return new DefaultBoundEntity<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReactiveBoundEntity<T> reactive() {
|
||||||
|
return new DefaultReactiveBoundEntity<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将本实体转换为sql实体
|
* 默认的绑定实体
|
||||||
*
|
*
|
||||||
* @return 转换结果
|
* @param <T>
|
||||||
*/
|
*/
|
||||||
private SQLEntity entity() {
|
private static class DefaultBoundEntity<T> implements BoundEntity<T> {
|
||||||
return SQLEntity.of(this::sql, this::parsedParameters);
|
|
||||||
|
@Override
|
||||||
|
public T one() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<T> all() {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataPage<T> page() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int execute() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DefaultReactiveBoundEntity<T> implements ReactiveBoundEntity<T> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<T> one() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Flux<T> all() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<DataPage<T>> page() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Integer> execute() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package group.flyfish.fluent.chain.common;
|
package group.flyfish.fluent.chain.common;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.chain.execution.BoundProxy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 可执行的sql
|
* 可执行的sql
|
||||||
*
|
*
|
||||||
@ -8,9 +10,10 @@ package group.flyfish.fluent.chain.common;
|
|||||||
public interface ExecutableSql {
|
public interface ExecutableSql {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行并获取更新条数
|
* 进入下一步,以主表作为输出结果
|
||||||
*
|
*
|
||||||
* @return 更新条数
|
* @param <T> 泛型
|
||||||
|
* @return 绑定操作
|
||||||
*/
|
*/
|
||||||
int execute();
|
<T> BoundProxy<T> next();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
package group.flyfish.fluent.chain.execution;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.entity.DataPage;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已经绑定的实体
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface BoundEntity<T> {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行一条sql,并且序列化为对象
|
||||||
|
* 注意,如果查询不止一条,该方法仅返回第一条数据
|
||||||
|
* 如果没有结果,将返回null
|
||||||
|
*
|
||||||
|
* @return 查询结果
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
T one();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行一条sql,并且查询出所有行
|
||||||
|
*
|
||||||
|
* @return 返回的列表
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
List<T> all();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询
|
||||||
|
*
|
||||||
|
* @return 返回的分页对象
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
DataPage<T> page();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直接执行sql,根据update count返回更新行数,如果是查询,永远返回0
|
||||||
|
*
|
||||||
|
* @return 更新行数
|
||||||
|
*/
|
||||||
|
int execute();
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package group.flyfish.fluent.chain.execution;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定状态下的代理
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface BoundProxy<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 阻塞的数据库操作
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
BoundEntity<T> block();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步数据库逻辑
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
ReactiveBoundEntity<T> reactive();
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package group.flyfish.fluent.chain.execution;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.entity.DataPage;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已经绑定的异步实体
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface ReactiveBoundEntity<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行一条sql,并且序列化为对象
|
||||||
|
* 注意,如果查询不止一条,该方法仅返回第一条数据
|
||||||
|
* 如果没有结果,将返回null
|
||||||
|
*
|
||||||
|
* @return 查询结果
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
Mono<T> one();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行一条sql,并且查询出所有行
|
||||||
|
*
|
||||||
|
* @return 返回的列表
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
Flux<T> all();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询
|
||||||
|
*
|
||||||
|
* @return 返回的分页对象
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
Mono<DataPage<T>> page();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直接执行sql,根据update count返回更新行数,如果是查询,永远返回0
|
||||||
|
*
|
||||||
|
* @return 更新行数
|
||||||
|
*/
|
||||||
|
Mono<Integer> execute();
|
||||||
|
}
|
@ -1,48 +1,10 @@
|
|||||||
package group.flyfish.fluent.chain.select;
|
package group.flyfish.fluent.chain.select;
|
||||||
|
|
||||||
import group.flyfish.fluent.chain.common.ExecutableSql;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* order做完后支持的操作
|
* order做完后支持的操作
|
||||||
*
|
*
|
||||||
* @author wangyu
|
* @author wangyu
|
||||||
*/
|
*/
|
||||||
public interface AfterOrderSqlChain extends ExecutableSql {
|
public interface AfterOrderSqlChain extends PieceSqlChain {
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行并获取结果
|
|
||||||
*
|
|
||||||
* @param <T> 泛型
|
|
||||||
* @return 单一结果值
|
|
||||||
*/
|
|
||||||
<T> T one();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行并获取结果
|
|
||||||
*
|
|
||||||
* @param clazz 结果类
|
|
||||||
* @param <T> 泛型
|
|
||||||
* @return 单一结果值
|
|
||||||
*/
|
|
||||||
<T> T one(Class<T> clazz);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行并获取多条结果,以主表class为结果
|
|
||||||
*
|
|
||||||
* @param <T> 结果泛型
|
|
||||||
* @return 结果列表
|
|
||||||
*/
|
|
||||||
<T> List<T> list();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行并获取多条结果
|
|
||||||
*
|
|
||||||
* @param clazz 结果类
|
|
||||||
* @param <T> 结果泛型
|
|
||||||
* @return 结果列表
|
|
||||||
*/
|
|
||||||
<T> List<T> list(Class<T> clazz);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package group.flyfish.fluent.chain.select;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.chain.common.ExecutableSql;
|
||||||
|
import group.flyfish.fluent.chain.execution.BoundProxy;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface FetchSqlChain extends ExecutableSql {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为SQL实体
|
||||||
|
*
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
<T> BoundProxy<T> as(Class<T> type);
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package group.flyfish.fluent.chain.select;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支持分片的sql链
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface PieceSqlChain extends FetchSqlChain {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 限制返回条数
|
||||||
|
*
|
||||||
|
* @param count 条数
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
PieceSqlChain limit(int count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跳过多少行
|
||||||
|
*
|
||||||
|
* @param rows 行数
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
PieceSqlChain offset(int rows);
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package group.flyfish.fluent.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据分页
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DataPage<T> {
|
||||||
|
|
||||||
|
private List<T> list;
|
||||||
|
|
||||||
|
private long total;
|
||||||
|
|
||||||
|
private int size = 10;
|
||||||
|
|
||||||
|
private int page = 1;
|
||||||
|
}
|
@ -1,38 +1,51 @@
|
|||||||
package group.flyfish.fluent.entity;
|
package group.flyfish.fluent.entity;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sql运行实体
|
* sql运行实体
|
||||||
*
|
*
|
||||||
|
* @param <T> 结果类型泛型
|
||||||
* @author wangyu
|
* @author wangyu
|
||||||
*/
|
*/
|
||||||
public class SQLEntity {
|
public class SQLEntity<T> {
|
||||||
|
|
||||||
private static final Supplier<Object[]> EMPTY_PARAMETERS = () -> new Object[]{};
|
private static final Supplier<Object[]> EMPTY_PARAMETERS = () -> new Object[]{};
|
||||||
|
|
||||||
// sql提供者
|
// sql提供者
|
||||||
private Supplier<String> sqlProvider;
|
@NonNull
|
||||||
|
private final Supplier<String> sql;
|
||||||
|
|
||||||
// sql参数表提供者
|
// sql参数表提供者
|
||||||
private Supplier<Object[]> parametersProvider;
|
private final Supplier<Object[]> parameters;
|
||||||
|
|
||||||
public static SQLEntity of(Supplier<String> sqlProvider) {
|
// 结果类型
|
||||||
return of(sqlProvider, EMPTY_PARAMETERS);
|
@NonNull
|
||||||
|
@Getter
|
||||||
|
private final Class<T> resultType;
|
||||||
|
|
||||||
|
private SQLEntity(Class<T> resultType, Supplier<String> sql, Supplier<Object[]> parameters) {
|
||||||
|
this.resultType = resultType;
|
||||||
|
this.sql = sql;
|
||||||
|
this.parameters = parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SQLEntity of(Supplier<String> sqlProvider, Supplier<Object[]> parametersProvider) {
|
public static <T> SQLEntity<T> of(Class<T> resultType, Supplier<String> sqlProvider) {
|
||||||
SQLEntity entity = new SQLEntity();
|
return of(resultType, sqlProvider, EMPTY_PARAMETERS);
|
||||||
entity.sqlProvider = sqlProvider;
|
}
|
||||||
entity.parametersProvider = parametersProvider;
|
|
||||||
return entity;
|
public static <T> SQLEntity<T> of(Class<T> resultType, Supplier<String> sql, Supplier<Object[]> parameters) {
|
||||||
|
return new SQLEntity<T>(resultType, sql, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSql() {
|
public String getSql() {
|
||||||
return sqlProvider.get();
|
return sql.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object[] getParameters() {
|
public Object[] getParameters() {
|
||||||
return parametersProvider.get();
|
return parameters.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package group.flyfish.fluent.operations;
|
package group.flyfish.fluent.operations;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.entity.DataPage;
|
||||||
import group.flyfish.fluent.entity.SQLEntity;
|
import group.flyfish.fluent.entity.SQLEntity;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
@ -18,22 +19,29 @@ public interface FluentSQLOperations {
|
|||||||
* 如果没有结果,将返回null
|
* 如果没有结果,将返回null
|
||||||
*
|
*
|
||||||
* @param entity sql实体
|
* @param entity sql实体
|
||||||
* @param clazz 目标类型
|
|
||||||
* @param <T> 目标泛型
|
* @param <T> 目标泛型
|
||||||
* @return 查询结果
|
* @return 查询结果
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
<T> T selectOne(SQLEntity entity, Class<T> clazz);
|
<T> T selectOne(SQLEntity<T> entity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行一条sql,并且查询出所有行
|
* 执行一条sql,并且查询出所有行
|
||||||
*
|
*
|
||||||
* @param entity sql实体
|
* @param entity sql实体
|
||||||
* @param clazz 目标类型
|
|
||||||
* @param <T> 目标泛型
|
* @param <T> 目标泛型
|
||||||
* @return 返回的列表
|
* @return 返回的列表
|
||||||
*/
|
*/
|
||||||
<T> List<T> select(SQLEntity entity, Class<T> clazz);
|
<T> List<T> select(SQLEntity<T> entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询
|
||||||
|
*
|
||||||
|
* @param entity sql实体
|
||||||
|
* @param <T> 目标泛型
|
||||||
|
* @return 返回的分页对象
|
||||||
|
*/
|
||||||
|
<T> DataPage<T> selectPage(SQLEntity<T> entity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 直接执行sql,根据update count返回更新行数,如果是查询,永远返回0
|
* 直接执行sql,根据update count返回更新行数,如果是查询,永远返回0
|
||||||
@ -41,5 +49,5 @@ public interface FluentSQLOperations {
|
|||||||
* @param entity sql实体
|
* @param entity sql实体
|
||||||
* @return 更新行数
|
* @return 更新行数
|
||||||
*/
|
*/
|
||||||
int execute(SQLEntity entity);
|
<T> int execute(SQLEntity<T> entity);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
package group.flyfish.fluent.operations;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.entity.DataPage;
|
||||||
|
import group.flyfish.fluent.entity.SQLEntity;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sql query操作
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface ReactiveFluentSQLOperations {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行一条sql,并且序列化为对象
|
||||||
|
* 注意,如果查询不止一条,该方法仅返回第一条数据
|
||||||
|
* 如果没有结果,将返回null
|
||||||
|
*
|
||||||
|
* @param entity sql实体
|
||||||
|
* @param <T> 目标泛型
|
||||||
|
* @return 查询结果
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
<T> Mono<T> selectOne(SQLEntity<T> entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行一条sql,并且查询出所有行
|
||||||
|
*
|
||||||
|
* @param entity sql实体
|
||||||
|
* @param <T> 目标泛型
|
||||||
|
* @return 返回的列表
|
||||||
|
*/
|
||||||
|
<T> Flux<T> select(SQLEntity<T> entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询
|
||||||
|
*
|
||||||
|
* @param entity sql实体
|
||||||
|
* @param <T> 目标泛型
|
||||||
|
* @return 返回的分页对象
|
||||||
|
*/
|
||||||
|
<T> Mono<DataPage<T>> selectPage(SQLEntity<T> entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直接执行sql,根据update count返回更新行数,如果是查询,永远返回0
|
||||||
|
*
|
||||||
|
* @param entity sql实体
|
||||||
|
* @return 更新行数
|
||||||
|
*/
|
||||||
|
<T> Mono<Integer> execute(SQLEntity<T> entity);
|
||||||
|
}
|
20
fluent-sql-core/src/main/java/group/flyfish/fluent/utils/cache/BoundObject.java
vendored
Normal file
20
fluent-sql-core/src/main/java/group/flyfish/fluent/utils/cache/BoundObject.java
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package group.flyfish.fluent.utils.cache;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定的单值对象
|
||||||
|
*
|
||||||
|
* @param <T> 泛型
|
||||||
|
*/
|
||||||
|
public class BoundObject<T> {
|
||||||
|
|
||||||
|
private T object;
|
||||||
|
|
||||||
|
public T computeIfAbsent(Supplier<T> supplier) {
|
||||||
|
if (null == object) {
|
||||||
|
object = supplier.get();
|
||||||
|
}
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
}
|
23
fluent-sql-core/src/main/java/group/flyfish/fluent/utils/cache/CachedWrapper.java
vendored
Normal file
23
fluent-sql-core/src/main/java/group/flyfish/fluent/utils/cache/CachedWrapper.java
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package group.flyfish.fluent.utils.cache;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存的supplier
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
public interface CachedWrapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 包装简单的supplier
|
||||||
|
*
|
||||||
|
* @param supplier 提供者
|
||||||
|
* @param <T> 泛型
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
static <T> Supplier<T> wrap(Supplier<T> supplier) {
|
||||||
|
BoundObject<T> object = new BoundObject<>();
|
||||||
|
return () -> object.computeIfAbsent(supplier);
|
||||||
|
}
|
||||||
|
}
|
@ -46,8 +46,7 @@ public final class EntityNameUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Map<String, String> getFields(Class<?> clazz) {
|
public static Map<String, String> getFields(Class<?> clazz) {
|
||||||
tryCache(clazz);
|
return tryCache(clazz);
|
||||||
return COLUMN_CACHE.get(clazz);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,7 +77,7 @@ public final class EntityNameUtils {
|
|||||||
SerializedLambda lambda = resolve(func);
|
SerializedLambda lambda = resolve(func);
|
||||||
String property = SqlNameUtils.methodToProperty(lambda.getImplMethodName());
|
String property = SqlNameUtils.methodToProperty(lambda.getImplMethodName());
|
||||||
Class<?> beanClass = resolveEntityClass(lambda);
|
Class<?> beanClass = resolveEntityClass(lambda);
|
||||||
String column = COLUMN_CACHE.get(beanClass).getOrDefault(property, SqlNameUtils.camelToUnderline(property));
|
String column = tryCache(beanClass).getOrDefault(property, SqlNameUtils.camelToUnderline(property));
|
||||||
// 取得别名缓存
|
// 取得别名缓存
|
||||||
AliasComposite.AliasCache cache = AliasComposite.sharedCache();
|
AliasComposite.AliasCache cache = AliasComposite.sharedCache();
|
||||||
// 确定最终名称
|
// 确定最终名称
|
||||||
@ -117,7 +116,9 @@ public final class EntityNameUtils {
|
|||||||
* @return 最终获取的类
|
* @return 最终获取的类
|
||||||
*/
|
*/
|
||||||
private static Class<?> resolveEntityClass(SerializedLambda lambda) {
|
private static Class<?> resolveEntityClass(SerializedLambda lambda) {
|
||||||
return tryCache(lambda.getInstantiatedType());
|
Class<?> type = lambda.getInstantiatedType();
|
||||||
|
tryCache(type);
|
||||||
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -150,8 +151,7 @@ public final class EntityNameUtils {
|
|||||||
*
|
*
|
||||||
* @param entityClass bean的类型
|
* @param entityClass bean的类型
|
||||||
*/
|
*/
|
||||||
private static Class<?> tryCache(Class<?> entityClass) {
|
private static Map<String, String> tryCache(Class<?> entityClass) {
|
||||||
COLUMN_CACHE.computeIfAbsent(entityClass, EntityNameUtils::buildFieldsCache);
|
return COLUMN_CACHE.computeIfAbsent(entityClass, EntityNameUtils::buildFieldsCache);
|
||||||
return entityClass;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package group.flyfish.fluent.utils.sql;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sql方法,用于缓存执行
|
||||||
|
*
|
||||||
|
* @param <R> 返回值泛型
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface SqlMethod<R> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行方法
|
||||||
|
*
|
||||||
|
* @param parameters 参数
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
R execute(Object... parameters);
|
||||||
|
}
|
@ -24,7 +24,17 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>group.flyfish.framework</groupId>
|
<groupId>group.flyfish.framework</groupId>
|
||||||
<artifactId>fluent-sql-spring-jdbc</artifactId>
|
<artifactId>fluent-sql-spring</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-r2dbc</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-jdbc</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@ -1,13 +1,20 @@
|
|||||||
package group.flyfish.fluent.autoconfigure;
|
package group.flyfish.fluent.autoconfigure;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.autoconfigure.FluentSqlAutoConfiguration.JdbcFluentSqlAutoConfigure;
|
||||||
|
import group.flyfish.fluent.autoconfigure.FluentSqlAutoConfiguration.R2dbcFluentSqlAutoConfigure;
|
||||||
import group.flyfish.fluent.operations.FluentSQLOperations;
|
import group.flyfish.fluent.operations.FluentSQLOperations;
|
||||||
import group.flyfish.fluent.operations.JdbcTemplateFluentSQLOperations;
|
import group.flyfish.fluent.operations.JdbcTemplateFluentSQLOperations;
|
||||||
|
import group.flyfish.fluent.operations.R2dbcFluentSQLOperations;
|
||||||
|
import group.flyfish.fluent.operations.ReactiveFluentSQLOperations;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.r2dbc.core.DatabaseClient;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
@ -17,17 +24,38 @@ import javax.sql.DataSource;
|
|||||||
* @author wangyu
|
* @author wangyu
|
||||||
*/
|
*/
|
||||||
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
|
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
|
||||||
|
@Import({R2dbcFluentSqlAutoConfigure.class, JdbcFluentSqlAutoConfigure.class})
|
||||||
public class FluentSqlAutoConfiguration {
|
public class FluentSqlAutoConfiguration {
|
||||||
|
|
||||||
/**
|
@ConditionalOnClass(DatabaseClient.class)
|
||||||
* 动态注入初始化的bean,完成注入配置
|
static class R2dbcFluentSqlAutoConfigure {
|
||||||
*
|
|
||||||
* @param dataSource 从spring datasource注入
|
/**
|
||||||
*/
|
* 动态注入初始化的bean,完成注入配置
|
||||||
@Bean
|
*
|
||||||
@ConditionalOnMissingBean(FluentSQLOperations.class)
|
* @param databaseClient 从spring r2dbc注入
|
||||||
@ConditionalOnBean(DataSource.class)
|
*/
|
||||||
public FluentSQLOperations fluentSQLOperations(DataSource dataSource) {
|
@Bean
|
||||||
return new JdbcTemplateFluentSQLOperations(new JdbcTemplate(dataSource));
|
@ConditionalOnMissingBean(ReactiveFluentSQLOperations.class)
|
||||||
|
@ConditionalOnBean(DatabaseClient.class)
|
||||||
|
public ReactiveFluentSQLOperations fluentSQLOperations(DatabaseClient databaseClient) {
|
||||||
|
return new R2dbcFluentSQLOperations(databaseClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConditionalOnClass(DataSource.class)
|
||||||
|
static class JdbcFluentSqlAutoConfigure {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态注入初始化的bean,完成注入配置
|
||||||
|
*
|
||||||
|
* @param dataSource 从spring datasource注入
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(FluentSQLOperations.class)
|
||||||
|
@ConditionalOnBean(DataSource.class)
|
||||||
|
public FluentSQLOperations fluentSQLOperations(DataSource dataSource) {
|
||||||
|
return new JdbcTemplateFluentSQLOperations(new JdbcTemplate(dataSource));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>fluent-sql-spring-jdbc</artifactId>
|
<artifactId>fluent-sql-spring</artifactId>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>8</maven.compiler.source>
|
<maven.compiler.source>8</maven.compiler.source>
|
||||||
@ -24,6 +24,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-jdbc</artifactId>
|
<artifactId>spring-jdbc</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-r2dbc</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
@ -1,6 +1,7 @@
|
|||||||
package group.flyfish.fluent.operations;
|
package group.flyfish.fluent.operations;
|
||||||
|
|
||||||
import group.flyfish.fluent.chain.SQL;
|
import group.flyfish.fluent.chain.SQL;
|
||||||
|
import group.flyfish.fluent.entity.DataPage;
|
||||||
import group.flyfish.fluent.entity.SQLEntity;
|
import group.flyfish.fluent.entity.SQLEntity;
|
||||||
import group.flyfish.fluent.mapping.SQLMappedRowMapper;
|
import group.flyfish.fluent.mapping.SQLMappedRowMapper;
|
||||||
import org.springframework.dao.EmptyResultDataAccessException;
|
import org.springframework.dao.EmptyResultDataAccessException;
|
||||||
@ -41,7 +42,7 @@ public class JdbcTemplateFluentSQLOperations implements FluentSQLOperations {
|
|||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public <T> T selectOne(SQLEntity entity, Class<T> clazz) {
|
public <T> T selectOne(SQLEntity entity, Class<T> clazz) {
|
||||||
try {
|
try {
|
||||||
String sql = entity.getSql().concat(" limit 1");
|
String sql = entity.getSql();
|
||||||
if (ClassUtils.isPrimitiveOrWrapper(clazz)) {
|
if (ClassUtils.isPrimitiveOrWrapper(clazz)) {
|
||||||
return jdbcOperations.queryForObject(sql, clazz, entity.getParameters());
|
return jdbcOperations.queryForObject(sql, clazz, entity.getParameters());
|
||||||
}
|
}
|
||||||
@ -67,6 +68,18 @@ public class JdbcTemplateFluentSQLOperations implements FluentSQLOperations {
|
|||||||
return jdbcOperations.query(sql, new SQLMappedRowMapper<>(clazz), entity.getParameters());
|
return jdbcOperations.query(sql, new SQLMappedRowMapper<>(clazz), entity.getParameters());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询
|
||||||
|
*
|
||||||
|
* @param entity sql实体
|
||||||
|
* @param clazz 目标类型
|
||||||
|
* @return 返回的分页对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> DataPage<T> selectPage(SQLEntity entity, Class<T> clazz) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 直接执行sql,根据update count返回更新行数,如果是查询,永远返回0
|
* 直接执行sql,根据update count返回更新行数,如果是查询,永远返回0
|
||||||
*
|
*
|
@ -0,0 +1,63 @@
|
|||||||
|
package group.flyfish.fluent.operations;
|
||||||
|
|
||||||
|
import group.flyfish.fluent.entity.DataPage;
|
||||||
|
import group.flyfish.fluent.entity.SQLEntity;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.r2dbc.core.DatabaseClient;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class R2dbcFluentSQLOperations implements ReactiveFluentSQLOperations {
|
||||||
|
|
||||||
|
private final DatabaseClient databaseClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行一条sql,并且序列化为对象
|
||||||
|
* 注意,如果查询不止一条,该方法仅返回第一条数据
|
||||||
|
* 如果没有结果,将返回null
|
||||||
|
*
|
||||||
|
* @param entity sql实体
|
||||||
|
* @param clazz 目标类型
|
||||||
|
* @return 查询结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Mono<T> selectOne(SQLEntity entity, Class<T> clazz) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行一条sql,并且查询出所有行
|
||||||
|
*
|
||||||
|
* @param entity sql实体
|
||||||
|
* @param clazz 目标类型
|
||||||
|
* @return 返回的列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Flux<T> select(SQLEntity entity, Class<T> clazz) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询
|
||||||
|
*
|
||||||
|
* @param entity sql实体
|
||||||
|
* @param clazz 目标类型
|
||||||
|
* @return 返回的分页对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> Mono<DataPage<T>> selectPage(SQLEntity entity, Class<T> clazz) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直接执行sql,根据update count返回更新行数,如果是查询,永远返回0
|
||||||
|
*
|
||||||
|
* @param entity sql实体
|
||||||
|
* @return 更新行数
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Mono<Integer> execute(SQLEntity entity) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@ import group.flyfish.fluent.operations.JdbcTemplateFluentSQLOperations;
|
|||||||
import group.flyfish.framework.cases.FluentSqlTestCase;
|
import group.flyfish.framework.cases.FluentSqlTestCase;
|
||||||
import group.flyfish.framework.cases.JdbcTestCase;
|
import group.flyfish.framework.cases.JdbcTestCase;
|
||||||
import group.flyfish.framework.cases.MybatisTestCase;
|
import group.flyfish.framework.cases.MybatisTestCase;
|
||||||
|
import group.flyfish.framework.cases.SingleTableTestCase;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.jdbc.core.JdbcOperations;
|
import org.springframework.jdbc.core.JdbcOperations;
|
||||||
@ -34,15 +35,20 @@ public class FluentJdbcTest {
|
|||||||
public void testSql() throws SQLException, JsonProcessingException {
|
public void testSql() throws SQLException, JsonProcessingException {
|
||||||
DataSource dataSource = new SimpleDriverDataSource(
|
DataSource dataSource = new SimpleDriverDataSource(
|
||||||
new Driver(),
|
new Driver(),
|
||||||
"jdbc:mysql://127.0.0.1:3306/epi_project?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=Asia/Shanghai",
|
"jdbc:mysql://127.0.0.1:3306/epi_project?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true",
|
||||||
"root",
|
"root",
|
||||||
"Unicom#2018"
|
"Unicom#2018" // "oI3WtMO8h%mSYARp"
|
||||||
);
|
);
|
||||||
// 准备待测试用例
|
// 准备待测试用例
|
||||||
List<TestCase<?>> cases = Arrays.asList(
|
List<TestCase<?>> cases = Arrays.asList(
|
||||||
|
// jdbc测试
|
||||||
new JdbcTestCase(dataSource),
|
new JdbcTestCase(dataSource),
|
||||||
|
// Mybatis测试
|
||||||
new MybatisTestCase(dataSource),
|
new MybatisTestCase(dataSource),
|
||||||
new FluentSqlTestCase(dataSource)
|
// FluentSQL测试
|
||||||
|
new FluentSqlTestCase(dataSource),
|
||||||
|
// 单表测试
|
||||||
|
new SingleTableTestCase()
|
||||||
);
|
);
|
||||||
// 执行测试
|
// 执行测试
|
||||||
cases.forEach(TestCase::test);
|
cases.forEach(TestCase::test);
|
@ -43,20 +43,23 @@ public abstract class AbstractTestCase<T> implements TestCase<T> {
|
|||||||
Name anno = getClass().getAnnotation(Name.class);
|
Name anno = getClass().getAnnotation(Name.class);
|
||||||
assert anno != null;
|
assert anno != null;
|
||||||
String name = anno.value();
|
String name = anno.value();
|
||||||
long current = System.currentTimeMillis();
|
print("=====准备执行任务《{0}》=====", name);
|
||||||
T result = null;
|
|
||||||
try {
|
try {
|
||||||
result = run();
|
long current = System.currentTimeMillis();
|
||||||
|
T result = run();
|
||||||
print("【初次执行】执行任务《{0}》用时:{1}ms", name, System.currentTimeMillis() - current);
|
print("【初次执行】执行任务《{0}》用时:{1}ms", name, System.currentTimeMillis() - current);
|
||||||
current = System.currentTimeMillis();
|
for (int i = 0; i < 10; i ++) {
|
||||||
result = run();
|
current = System.currentTimeMillis();
|
||||||
|
result = run();
|
||||||
|
print("【正常执行】执行任务《{0}》用时:{1}ms", name, System.currentTimeMillis() - current);
|
||||||
|
}
|
||||||
|
printResult(result);
|
||||||
return result;
|
return result;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
print("执行失败!{0}", e.getMessage());
|
print("执行失败!{0}", e.getMessage());
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
} finally {
|
} finally {
|
||||||
print("【正常执行】执行任务《{0}》用时:{1}ms", name, System.currentTimeMillis() - current);
|
print("=====完成执行任务《{0}》=====");
|
||||||
printResult(result);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,8 @@
|
|||||||
package group.flyfish.framework.cases;
|
package group.flyfish.framework.cases;
|
||||||
|
|
||||||
import group.flyfish.fluent.debug.FluentSqlDebugger;
|
import group.flyfish.fluent.chain.select.AfterWhereSqlChain;
|
||||||
import group.flyfish.fluent.operations.JdbcTemplateFluentSQLOperations;
|
import group.flyfish.fluent.operations.JdbcTemplateFluentSQLOperations;
|
||||||
|
import group.flyfish.fluent.utils.cache.CachedWrapper;
|
||||||
import group.flyfish.framework.TestCase;
|
import group.flyfish.framework.TestCase;
|
||||||
import group.flyfish.framework.entity.SaasOrder;
|
import group.flyfish.framework.entity.SaasOrder;
|
||||||
import group.flyfish.framework.entity.SaasPlan;
|
import group.flyfish.framework.entity.SaasPlan;
|
||||||
@ -11,6 +12,7 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
|||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import static group.flyfish.fluent.chain.SQL.select;
|
import static group.flyfish.fluent.chain.SQL.select;
|
||||||
import static group.flyfish.fluent.chain.select.SelectComposite.composite;
|
import static group.flyfish.fluent.chain.select.SelectComposite.composite;
|
||||||
@ -19,6 +21,8 @@ import static group.flyfish.fluent.query.Query.where;
|
|||||||
@TestCase.Name("使用fluent-sql")
|
@TestCase.Name("使用fluent-sql")
|
||||||
public class FluentSqlTestCase extends AbstractTestCase<List<TenantContext>> {
|
public class FluentSqlTestCase extends AbstractTestCase<List<TenantContext>> {
|
||||||
|
|
||||||
|
private AfterWhereSqlChain sql;
|
||||||
|
|
||||||
public FluentSqlTestCase(DataSource dataSource) {
|
public FluentSqlTestCase(DataSource dataSource) {
|
||||||
super(dataSource);
|
super(dataSource);
|
||||||
}
|
}
|
||||||
@ -32,8 +36,20 @@ public class FluentSqlTestCase extends AbstractTestCase<List<TenantContext>> {
|
|||||||
public void initialize() throws Exception {
|
public void initialize() throws Exception {
|
||||||
// 基于构造器自动绑定注册,在实际应用中使用@Bean声明即可,可参考下面的demo
|
// 基于构造器自动绑定注册,在实际应用中使用@Bean声明即可,可参考下面的demo
|
||||||
new JdbcTemplateFluentSQLOperations(new JdbcTemplate(dataSource));
|
new JdbcTemplateFluentSQLOperations(new JdbcTemplate(dataSource));
|
||||||
|
// 缓存构建结果
|
||||||
|
this.sql = select(
|
||||||
|
// 查询租户全量字段
|
||||||
|
composite(SaasTenant::getId, SaasTenant::getName, SaasTenant::getIdentifier, SaasTenant::getDatasource,
|
||||||
|
SaasTenant::getStorage, SaasTenant::getStatus, SaasTenant::getEnable),
|
||||||
|
// 查询套餐
|
||||||
|
composite(SaasOrder::getQuotaConfig, SaasOrder::getOrderTime, SaasOrder::getExpireTime,
|
||||||
|
SaasOrder::getOrderType))
|
||||||
|
.from(SaasTenant.class)
|
||||||
|
.leftJoin(SaasOrder.class).on(where(SaasOrder::getTenantId).eq(SaasTenant::getId))
|
||||||
|
.leftJoin(SaasPlan.class).on(where(SaasPlan::getId).eq(SaasOrder::getPlanId))
|
||||||
|
.matching(where(SaasTenant::getEnable).eq(true));
|
||||||
// 启用调试
|
// 启用调试
|
||||||
FluentSqlDebugger.enable();
|
// FluentSqlDebugger.enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,17 +61,6 @@ public class FluentSqlTestCase extends AbstractTestCase<List<TenantContext>> {
|
|||||||
@Override
|
@Override
|
||||||
public List<TenantContext> run() throws Exception {
|
public List<TenantContext> run() throws Exception {
|
||||||
// 一个平平无奇的查询
|
// 一个平平无奇的查询
|
||||||
return select(
|
return sql.list(TenantContext.class);
|
||||||
// 查询租户全量字段
|
|
||||||
composite(SaasTenant::getId, SaasTenant::getName, SaasTenant::getIdentifier, SaasTenant::getDatasource,
|
|
||||||
SaasTenant::getStorage, SaasTenant::getStatus, SaasTenant::getEnable),
|
|
||||||
// 查询套餐
|
|
||||||
composite(SaasOrder::getQuotaConfig, SaasOrder::getOrderTime, SaasOrder::getExpireTime,
|
|
||||||
SaasOrder::getOrderType))
|
|
||||||
.from(SaasTenant.class)
|
|
||||||
.leftJoin(SaasOrder.class).on(where(SaasOrder::getTenantId).eq(SaasTenant::getId))
|
|
||||||
.leftJoin(SaasPlan.class).on(where(SaasPlan::getId).eq(SaasOrder::getPlanId))
|
|
||||||
.matching(where(SaasTenant::getEnable).eq(true))
|
|
||||||
.list(TenantContext.class);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package group.flyfish.framework.cases;
|
package group.flyfish.framework.cases;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||||
|
import group.flyfish.fluent.utils.data.ObjectMappers;
|
||||||
import group.flyfish.framework.TestCase;
|
import group.flyfish.framework.TestCase;
|
||||||
import group.flyfish.framework.entity.SaasQuota;
|
import group.flyfish.framework.entity.SaasQuota;
|
||||||
import group.flyfish.framework.entity.SaasTenant;
|
import group.flyfish.framework.entity.SaasTenant;
|
||||||
@ -40,6 +41,7 @@ public class MybatisTestCase extends AbstractTestCase<List<TenantContext>> {
|
|||||||
Configuration configuration = new Configuration(environment);
|
Configuration configuration = new Configuration(environment);
|
||||||
configuration.addMapper(TenantContextMapper.class);
|
configuration.addMapper(TenantContextMapper.class);
|
||||||
TypeHandlerRegistry registry = configuration.getTypeHandlerRegistry();
|
TypeHandlerRegistry registry = configuration.getTypeHandlerRegistry();
|
||||||
|
JacksonTypeHandler.setObjectMapper(ObjectMappers.shared());
|
||||||
registry.register(SaasTenant.DataSourceConfig.class, JacksonTypeHandler.class);
|
registry.register(SaasTenant.DataSourceConfig.class, JacksonTypeHandler.class);
|
||||||
registry.register(SaasQuota.class, JacksonTypeHandler.class);
|
registry.register(SaasQuota.class, JacksonTypeHandler.class);
|
||||||
registry.register(SaasTenant.StorageConfig.class, JacksonTypeHandler.class);
|
registry.register(SaasTenant.StorageConfig.class, JacksonTypeHandler.class);
|
@ -0,0 +1,43 @@
|
|||||||
|
package group.flyfish.framework.cases;
|
||||||
|
|
||||||
|
import group.flyfish.framework.TestCase;
|
||||||
|
import group.flyfish.framework.entity.SaasTenant;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static group.flyfish.fluent.chain.SQL.select;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单表测试用例
|
||||||
|
*
|
||||||
|
* @author wangyu
|
||||||
|
*/
|
||||||
|
@TestCase.Name("单表查询测试")
|
||||||
|
public class SingleTableTestCase extends AbstractTestCase<List<SaasTenant>> {
|
||||||
|
|
||||||
|
public SingleTableTestCase() {
|
||||||
|
super(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化
|
||||||
|
*
|
||||||
|
* @throws Exception 异常
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void initialize() throws Exception {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试运行逻辑
|
||||||
|
*
|
||||||
|
* @return 运行结果
|
||||||
|
* @throws Exception 异常
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<SaasTenant> run() throws Exception {
|
||||||
|
// 单表查询
|
||||||
|
return select().from(SaasTenant.class).list();
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@
|
|||||||
<result column="storage" property="storage" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />
|
<result column="storage" property="storage" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />
|
||||||
<result column="status" property="status" />
|
<result column="status" property="status" />
|
||||||
<result column="enable" property="enable" />
|
<result column="enable" property="enable" />
|
||||||
<result column="quotaConfig" property="quota" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />
|
<result column="quota" property="quota" jdbcType="LONGVARCHAR" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler" />
|
||||||
<result column="orderTime" property="orderTime" />
|
<result column="orderTime" property="orderTime" />
|
||||||
<result column="expireTime" property="expireTime" />
|
<result column="expireTime" property="expireTime" />
|
||||||
<result column="orderType" property="orderType" />
|
<result column="orderType" property="orderType" />
|
||||||
@ -24,7 +24,7 @@
|
|||||||
t1.`storage` as `storage`,
|
t1.`storage` as `storage`,
|
||||||
t1.`status` as `status`,
|
t1.`status` as `status`,
|
||||||
t1.`enable` as `enable`,
|
t1.`enable` as `enable`,
|
||||||
t2.`quota_config` as `quotaConfig`,
|
t2.`quota_config` as `quota`,
|
||||||
t2.`order_time` as `orderTime`,
|
t2.`order_time` as `orderTime`,
|
||||||
t2.`expire_time` as `expireTime`,
|
t2.`expire_time` as `expireTime`,
|
||||||
t2.`order_type` as `orderType`
|
t2.`order_type` as `orderType`
|
14
pom.xml
14
pom.xml
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>fluent-sql-core</module>
|
<module>fluent-sql-core</module>
|
||||||
<module>fluent-sql-spring-jdbc</module>
|
<module>fluent-sql-spring</module>
|
||||||
<module>fluent-sql-annotations</module>
|
<module>fluent-sql-annotations</module>
|
||||||
<module>fluent-sql-spring-boot-starter</module>
|
<module>fluent-sql-spring-boot-starter</module>
|
||||||
</modules>
|
</modules>
|
||||||
@ -88,7 +88,7 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>group.flyfish.framework</groupId>
|
<groupId>group.flyfish.framework</groupId>
|
||||||
<artifactId>fluent-sql-spring-jdbc</artifactId>
|
<artifactId>fluent-sql-spring</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -121,6 +121,11 @@
|
|||||||
<artifactId>spring-context</artifactId>
|
<artifactId>spring-context</artifactId>
|
||||||
<version>${spring.version}</version>
|
<version>${spring.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-r2dbc</artifactId>
|
||||||
|
<version>${spring.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mybatis</groupId>
|
<groupId>org.mybatis</groupId>
|
||||||
<artifactId>mybatis</artifactId>
|
<artifactId>mybatis</artifactId>
|
||||||
@ -153,6 +158,11 @@
|
|||||||
<version>8.0.29</version>
|
<version>8.0.29</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.projectreactor</groupId>
|
||||||
|
<artifactId>reactor-core</artifactId>
|
||||||
|
<version>3.6.7</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user