newInstance,
+ Object... args);
+
+ /**
+ * 创建原生SQL查询器
+ *
+ * 预编译参数仅支持?,如果要使用模版,请使用{@link org.hswebframework.ezorm.rdb.executor.SqlRequests#template(String, Object)}
+ * 构造sql以及参数
+ *
{@code
+ *
+ * Flux records = helper.select("select * from table where type = ?",type)
+ * //注入动态查询条件
+ * .where(param)
+ * //执行查询
+ * .fetch();
+ * }
+ *
+ * join逻辑:
+ *
+ *
{@code
+ *
+ * helper.select("select t1.id,t2.* from table t1"+
+ * " left join table2 t2 on t1.id = t2.id") ...
+ *
+ * 将返回结构:
+ * [
+ * {"id":"t1.id的值",
+ * "t2":{
+ * "c1":"t2的字段",
+ * ...
+ * }}
+ * ]
+ *
+ *
+ * }
+ *
+ *
+ * ⚠️注意:避免动态拼接SQL语句,应该使用预编译参数或者动态注入动态条件来进行条件处理
+ *
+ * @param sql SQL查询语句
+ * @param args 预编译参数
+ * @return 查询构造器
+ */
+ NativeQuerySpec select(String sql, Object... args);
+
+
/**
* 创建一个查询构造器
*
@@ -58,6 +126,41 @@ public interface QueryHelper {
Consumer> mapperSpec);
+ interface NativeQuerySpec extends ExecuteSpec {
+
+ /**
+ * 以DSL方式构造查询条件
+ * {@code
+ * helper
+ * .select("select * from table t")
+ * .where(dsl->dsl.is("type","device"))
+ * }
+ *
+ * @param dsl DSL
+ * @return this
+ */
+ default ExecuteSpec where(Consumer> dsl) {
+ Query, QueryParamEntity> query = QueryParamEntity.newQuery();
+ dsl.accept(query);
+ return where(query.getParam());
+ }
+
+ /**
+ * 指定动态查询条件,通常用于前端动态传入查询条件
+ * {@code
+ * helper
+ * .select("select * from table t")
+ * .where(param)
+ * .fetch()
+ * }
+ *
+ * @param param DSL
+ * @return this
+ */
+ ExecuteSpec where(QueryParamEntity param);
+
+ }
+
interface SelectSpec {
/**
diff --git a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveCrudService.java b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveCrudService.java
index 0ab0afa4c..cec6e0ce8 100644
--- a/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveCrudService.java
+++ b/hsweb-commons/hsweb-commons-crud/src/main/java/org/hswebframework/web/crud/service/ReactiveCrudService.java
@@ -27,6 +27,8 @@ import java.util.function.Function;
* @see GenericReactiveCrudService
* @see GenericReactiveTreeSupportCrudService
* @see EnableCacheReactiveCrudService
+ * @see org.hswebframework.web.crud.query.QueryHelper
+ * @since 4.0
*/
public interface ReactiveCrudService {
diff --git a/hsweb-commons/hsweb-commons-crud/src/test/java/org/hswebframework/web/crud/query/DefaultQueryHelperTest.java b/hsweb-commons/hsweb-commons-crud/src/test/java/org/hswebframework/web/crud/query/DefaultQueryHelperTest.java
index c10bbe1ef..2958b782c 100644
--- a/hsweb-commons/hsweb-commons-crud/src/test/java/org/hswebframework/web/crud/query/DefaultQueryHelperTest.java
+++ b/hsweb-commons/hsweb-commons-crud/src/test/java/org/hswebframework/web/crud/query/DefaultQueryHelperTest.java
@@ -29,6 +29,39 @@ class DefaultQueryHelperTest {
private DatabaseOperator database;
+ @Test
+ public void testNative() {
+ database.dml()
+ .insert("s_test_event")
+ .value("id", "helper_testNative")
+ .value("name", "Ename2")
+ .execute()
+ .sync();
+
+ database.dml()
+ .insert("s_test")
+ .value("id", "helper_testNative")
+ .value("name", "main2")
+ .value("age", 20)
+ .execute()
+ .sync();
+
+ DefaultQueryHelper helper = new DefaultQueryHelper(database);
+
+ helper.select("select e.*,t.id as \"id\" from s_test t " +
+ "left join s_test_event e on e.id = t.id" +
+ " where t.age = ? order by t.age desc", 20)
+ .where(dsl -> dsl
+ .is("e.id", "helper_testNative")
+ .is("t.age", 20))
+ .fetch()
+ .doOnNext(v -> System.out.println(JSON.toJSONString(v, SerializerFeature.PrettyFormat)))
+ .as(StepVerifier::create)
+ .expectNextCount(1)
+ .verifyComplete();
+
+ }
+
@Test
public void test() {
diff --git a/hsweb-commons/hsweb-commons-crud/src/test/java/org/hswebframework/web/crud/query/QueryAnalyzerImplTest.java b/hsweb-commons/hsweb-commons-crud/src/test/java/org/hswebframework/web/crud/query/QueryAnalyzerImplTest.java
new file mode 100644
index 000000000..51beaadbb
--- /dev/null
+++ b/hsweb-commons/hsweb-commons-crud/src/test/java/org/hswebframework/web/crud/query/QueryAnalyzerImplTest.java
@@ -0,0 +1,73 @@
+package org.hswebframework.web.crud.query;
+
+import org.hswebframework.ezorm.rdb.executor.SqlRequest;
+import org.hswebframework.ezorm.rdb.executor.wrapper.ResultWrappers;
+import org.hswebframework.ezorm.rdb.operator.DatabaseOperator;
+import org.hswebframework.web.api.crud.entity.QueryParamEntity;
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import reactor.test.StepVerifier;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@SpringBootTest
+@RunWith(SpringJUnit4ClassRunner.class)
+class QueryAnalyzerImplTest {
+ @Autowired
+ private DatabaseOperator database;
+
+
+ @Test
+ void testInject() {
+ QueryAnalyzerImpl analyzer = new QueryAnalyzerImpl(database,
+ "select count(distinct time) t2, \"name\" n from \"s_test\" t");
+ SqlRequest request = analyzer.inject(
+ QueryParamEntity
+ .newQuery()
+ .and("name", "123")
+ .getParam());
+
+ System.out.println(request);
+ }
+
+ @Test
+ void test() {
+ QueryAnalyzerImpl analyzer = new QueryAnalyzerImpl(database,
+ "select name n from s_test t");
+
+ assertNotNull(analyzer.select().table.alias, "t");
+ assertNotNull(analyzer.select().table.metadata.getName(), "s_test");
+
+ assertNotNull(analyzer.select().columns.get("n"));
+
+
+ }
+
+ @Test
+ void testSub() {
+ QueryAnalyzerImpl analyzer = new QueryAnalyzerImpl(database,
+ "select * from ( select distinct(name) as n from s_test ) t");
+
+ assertEquals(analyzer.select().table.alias, "t");
+
+ assertNotNull(analyzer.select().getColumns().get("n"));
+
+ SqlRequest request = analyzer
+ .inject(QueryParamEntity
+ .newQuery()
+ .where("n", "123")
+ .getParam());
+
+ System.out.println(request);
+
+ database.sql()
+ .reactive()
+ .select(request, ResultWrappers.map())
+ .as(StepVerifier::create)
+ .expectComplete()
+ .verify();
+ }
+}
\ No newline at end of file