diff --git a/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/README.md b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/README.md
new file mode 100644
index 00000000..a11a3834
--- /dev/null
+++ b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/README.md
@@ -0,0 +1,154 @@
+# APIJSONDemo
+
+## 支持多数据源-rediSQL
+
+redis table 表名规范: REDIS_TABLE_*
+开发人员可自行控制
+JedisBuildData
+public static final String REDIS_TABLE_KEY = "REDIS_TABLE_"; // rediSql table prefix
+
+Access、Request配置 访问操作权限
+
+rediSQL安装使用
+https://github.com/RedBeardLab/rediSQL
+
+官方docker安装
+docker pull dalongrong/redisql
+docker run -itd --name redisql -p 6399:6379 dalongrong/redisql
+
+rediSQL注意事项
+rediSQL免费版有后遥控制,每个小时会发送 redist info 统计信息
+
+
+可以配host ,弄一个nginx 本地代理 解决,或者 项目自己重新打包
+
+rediSQL java:
+https://github.com/RedBeardLab/JRediSQL
+https://www.youtube.com/watch?v=YRusC-AIq_g
+
+本示例 用 redis cluster rediSQL, 具体redis集群模式, 开发人员自行控制
+将 libredis_sql.so 导入redis.config
+--loadmodule /etc/redis/libredis_sql.so
+rediSQL 创建数据库表命令
+```
+REDISQL.EXEC DB "CREATE TABLE REDIS_TABLE_A(id TEXT, A INT, B TEXT, C TEXT, userId TEXT);"
+
+REDISQL.EXEC DB "INSERT INTO REDIS_TABLE_A(id,A,B,C,userId) VALUES('1', 3, '1c', 'bar','1');"
+
+
+REDISQL.EXEC DB "SELECT * FROM REDIS_TABLE_A;"
+
+REDISQL.EXEC DB "drop table REDIS_TABLE_A;"
+```
+
+测试用例
+```
+{
+ "@datasource": "redisCluster",
+ "REDIS_TABLE_A":{
+ "A": 1,
+ "B": "B",
+ "C": "C"
+ },
+ "tag": "REDIS_TABLE_A",
+ "@explain": true,
+ "format": true
+}
+
+{
+ "REDIS_TABLE_A[]": [
+ {
+ "A": 5,
+ "B": "5B",
+ "C": "5C"
+ },
+ {
+ "A": 6,
+ "B": "6B",
+ "C": "6C"
+ },
+ {
+ "A": 7,
+ "B": "7B",
+ "C": "7C"
+ }
+ ],
+ "tag": "REDIS_TABLE_A[]",
+ "@datasource": "redisCluster",
+ "@explain": true,
+ "format": true
+}
+
+{
+ "@datasource": "redisCluster",
+ "REDIS_TABLE_A": {
+ "id": "f2621698-99fa-4698-9fb0-8c7b585da403",
+ "A": 1,
+ "B": "1B"
+ },
+ "tag": "REDIS_TABLE_A",
+ "@explain": true,
+ "format": true
+}
+
+Request 表配置
+{"REDIS_TABLE_A[]": [{"MUST": "A,B", "REFUSE": "id"}], "INSERT": {"@role": "OWNER,ADMIN","@combine": "A | B"}}
+
+{
+ "@datasource": "redisCluster",
+ "REDIS_TABLE_A": {
+ "A": 1,
+ "B": "1B",
+ "C": "1-1-1C"
+ },
+ "tag": "REDIS_TABLE_A",
+ "@explain": true,
+ "format": true
+}
+
+{
+ "@datasource": "redisCluster",
+ "REDIS_TABLE_A:a": {
+ "@column":"a,b,c",
+ "b$": "7B%"
+ },
+ "@explain": true,
+ "format": true
+}
+
+{
+ "@datasource": "redisCluster",
+ "REDIS_TABLE_A:a[]": {
+ "REDIS_TABLE_A": {
+ "@column":"a,b,c"
+ //"b$": "1%"
+ },
+ "page":0,
+ "count":3,
+ "query": 2
+ },
+ "total@": "/REDIS_TABLE_A:a[]/total",
+ "@explain": true,
+ "format": true
+}
+
+{
+ "@datasource": "redisCluster",
+ "REDIS_TABLE_A": {
+ "id": "f2621698-99fa-4698-9fb0-8c7b585da403"
+ },
+ "tag": "REDIS_TABLE_A",
+ "@explain": true,
+ "format": true
+}
+
+{
+ "@datasource": "redisCluster",
+ "REDIS_TABLE_A:a": {
+ "id{}": ["1","eb3dd7c9-bab6-410c-b70a-cbbc3bd12896", "c83b3cfa-034e-4a9e-b2cf-83520db1ce05"]
+ },
+ "tag": "REDIS_TABLE_A[]",
+ "@explain": true,
+ "format": true
+}
+```
diff --git a/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/pom.xml b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/pom.xml
new file mode 100644
index 00000000..06af836d
--- /dev/null
+++ b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/pom.xml
@@ -0,0 +1,196 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.5.13
+
+
+ apijson.demo
+ apijsondemo-multidatasource-rediSQL
+ 6.0.0
+
+ apijsondemo-multidatasource-rediSQL
+ Demo project for testing APIJSON server based on SpringBoot
+
+
+ UTF-8
+ UTF-8
+ 3.12.0
+ 1.1.16
+ 3.5.1
+ 2.3.3
+ 4.4
+ 1.10
+ 30.1.1-jre
+ 1.2.72
+ 4.1.1
+ 1.18.4
+ 3.12.0
+ 2.5
+ 1.10
+ 4.4
+ 1.10
+ 6.0.0
+ 8.0.31
+ 5.3.18
+ 2.6.6
+ 3.5.2
+ 3.7.1
+ 1.8
+
+
+
+
+
+ com.github.Tencent
+ APIJSON
+ ${apijson.version}
+
+
+ com.github.APIJSON
+ apijson-framework
+ ${apijson.version}
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework
+ spring-context-support
+ ${spring-context-support.version}
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ ${spring-boot-configuration-processor.version}
+ true
+
+
+ com.alibaba
+ druid-spring-boot-starter
+ ${druid.version}
+
+
+ com.baomidou
+ dynamic-datasource-spring-boot-starter
+ ${dynamic-datasource-spring-boot-starter.version}
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+ ${mybatisplus.version}
+
+
+ com.baomidou
+ mybatis-plus-generator
+
+
+
+
+ com.baomidou
+ mybatis-plus-support
+ ${mybatis-plus-support.version}
+
+
+ org.apache.commons
+ commons-collections4
+ ${commons-collections4.version}
+
+
+ mysql
+ mysql-connector-java
+ ${mysql.version}
+
+
+ com.google.guava
+ guava
+ ${guava.version}
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+ commons-io
+ commons-io
+ ${commons.io.version}
+
+
+ commons-codec
+ commons-codec
+ ${commons.codec.version}
+
+
+ commons-configuration
+ commons-configuration
+ ${commons.configuration.version}
+
+
+ redis.clients
+ jedis
+ ${jedis.version}
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+ apijson.demo.DemoApplication
+
+
+
+
+ repackage
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 1.8
+ 1.8
+
+
+
+
+
+
+
+
+ jitpack.io
+ https://jitpack.io
+
+ true
+
+
+
+
+ spring-snapshots
+ https://repo.spring.io/snapshot
+
+ true
+
+
+
+ spring-milestones
+ https://repo.spring.io/milestone
+
+
+
+
\ No newline at end of file
diff --git a/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DataBaseConfig.java b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DataBaseConfig.java
new file mode 100644
index 00000000..6e261537
--- /dev/null
+++ b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DataBaseConfig.java
@@ -0,0 +1,22 @@
+package apijson.demo;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class DataBaseConfig {
+ private String primary;
+
+ @Value("${spring.datasource.dynamic.primary}")
+ public void setPrimary(String primary) {
+ this.primary = primary;
+ }
+
+ public String getPrimary() {
+ return primary;
+ }
+
+ public static DataBaseConfig getInstence() {
+ return SpringContextUtils.getBean(DataBaseConfig.class);
+ }
+}
diff --git a/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DataBaseUtil.java b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DataBaseUtil.java
new file mode 100644
index 00000000..a4c6bbf3
--- /dev/null
+++ b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DataBaseUtil.java
@@ -0,0 +1,54 @@
+package apijson.demo;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+public class DataBaseUtil {
+
+ /**
+ * 根据url获取库名
+ * @param url
+ * @return
+ */
+ public static String getLibname(String url) {
+ Pattern p = Pattern.compile("jdbc:(?\\w+):.*((//)|@)(?.+):(?\\d+)(/|(;DatabaseName=)|:)(?\\w+)\\??.*");
+ Matcher m = p.matcher(url);
+ if(m.find()) {
+ return m.group("dbName");
+ }
+ return null;
+ }
+
+ /***
+ * primary: master
+ * strict: false
+ * @param datasource: 匹配不成功, 自动匹配默认数据库
+ * @return
+ */
+ public static javax.sql.DataSource getDataSource(String datasource) {
+ try {
+ return DynamicDataSource.getDetail(datasource).getDataSource(); // 数据源
+ } catch (Exception e) {
+ throw new IllegalArgumentException("动态数据源配置错误 " + datasource);
+ }
+ }
+
+ public static String getDruidUrl(String datasource) {
+ return DynamicDataSource.getDetail(datasource).getUrl(); // 数据库连接url
+ }
+
+ public static String getDruidSchema(String datasource) {
+ return getLibname(DynamicDataSource.getDetail(datasource).getUrl()); // 数据库名;
+ }
+
+ public static String getDruidDBAccount(String datasource) {
+ return DynamicDataSource.getDetail(datasource).getDbAccount(); // 数据库用户名
+ }
+
+ public static String getDruidDBPassword(String datasource) {
+ return DynamicDataSource.getDetail(datasource).getDbPassword(); // 数据库密码
+ }
+}
diff --git a/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoApplication.java b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoApplication.java
new file mode 100644
index 00000000..ef58ecff
--- /dev/null
+++ b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoApplication.java
@@ -0,0 +1,103 @@
+/*Copyright ©2016 TommyLemon(https://github.com/TommyLemon/APIJSON)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.*/
+
+package apijson.demo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.server.WebServerFactoryCustomizer;
+import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import apijson.Log;
+import apijson.framework.APIJSONApplication;
+import apijson.framework.APIJSONCreator;
+import apijson.framework.APIJSONParser;
+import apijson.orm.AbstractFunctionParser;
+import apijson.orm.AbstractVerifier;
+import apijson.orm.FunctionParser;
+import apijson.orm.Parser;
+import apijson.orm.SQLConfig;
+import apijson.orm.SQLExecutor;
+
+/**
+ * Demo SpringBoot Application 主应用程序启动类 右键这个类 > Run As > Java Application 具体见
+ * SpringBoot 文档
+ * https://www.springcloud.cc/spring-boot.html#using-boot-locating-the-main-class
+ *
+ * @author Lemon
+ */
+@Configuration
+@SpringBootApplication
+@EnableConfigurationProperties
+public class DemoApplication implements WebServerFactoryCustomizer {
+ public static final String TAG = "DemoApplication";
+
+ public static void main(String[] args) throws Exception {
+ SpringApplication.run(DemoApplication.class, args);
+ Log.DEBUG = true;
+ APIJSONParser.IS_PRINT_BIG_LOG = false;
+ AbstractFunctionParser.ENABLE_SCRIPT_FUNCTION = true;
+ AbstractVerifier.IS_UPDATE_MUST_HAVE_ID_CONDITION = false;
+ APIJSONApplication.init(false); // 4.4.0 以上需要这句来保证以上 static 代码块中给 DEFAULT_APIJSON_CREATOR 赋值会生效
+ }
+
+ // SpringBoot 2.x 自定义端口方式
+ @Override
+ public void customize(ConfigurableServletWebServerFactory server) {
+ server.setPort(8080);
+ }
+
+ // 支持 APIAuto 中 JavaScript 代码跨域请求
+ @Bean
+ public WebMvcConfigurer corsConfigurer() {
+ return new WebMvcConfigurer() {
+ @Override
+ public void addCorsMappings(CorsRegistry registry) {
+ registry.addMapping("/**").allowedOriginPatterns("*").allowedMethods("*").allowCredentials(true)
+ .maxAge(3600);
+ }
+ };
+ }
+
+ static {
+ // 使用本项目的自定义处理类
+ APIJSONApplication.DEFAULT_APIJSON_CREATOR = new APIJSONCreator() {
+ @Override
+ public Parser createParser() {
+ return new DemoParser();
+ }
+
+ @Override
+ public SQLConfig createSQLConfig() {
+ return new DemoSQLConfig();
+ }
+
+ @Override
+ public FunctionParser createFunctionParser() {
+ return new DemoFunctionParser();
+ }
+
+ @Override
+ public SQLExecutor createSQLExecutor() {
+ return new DemoSQLExecutor();
+ }
+ };
+ }
+
+}
diff --git a/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoController.java b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoController.java
new file mode 100644
index 00000000..c91c58f3
--- /dev/null
+++ b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoController.java
@@ -0,0 +1,99 @@
+/*Copyright ©2016 TommyLemon(https://github.com/TommyLemon/APIJSON)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.*/
+
+package apijson.demo;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.net.URLDecoder;
+import java.util.Map;
+
+import javax.servlet.http.HttpSession;
+
+import apijson.RequestMethod;
+import apijson.StringUtil;
+import apijson.framework.APIJSONController;
+import apijson.orm.Parser;
+
+
+/**请求路由入口控制器,包括通用增删改查接口等,转交给 APIJSON 的 Parser 来处理
+ * 具体见 SpringBoot 文档
+ * https://www.springcloud.cc/spring-boot.html#boot-features-spring-mvc
+ * 以及 APIJSON 通用文档 3.设计规范 3.1 操作方法
+ * https://github.com/Tencent/APIJSON/blob/master/Document.md#3.1
+ *
建议全通过HTTP POST来请求:
+ *
1.减少代码 - 客户端无需写HTTP GET,PUT等各种方式的请求代码
+ *
2.提高性能 - 无需URL encode和decode
+ *
3.调试方便 - 建议使用 APIAuto(http://apijson.cn/api) 或 Postman
+ * @author Lemon
+ */
+@RestController
+@RequestMapping("")
+public class DemoController extends APIJSONController {
+
+ @Override
+ public Parser newParser(HttpSession session, RequestMethod method) {
+ return super.newParser(session, method).setNeedVerify(false); // TODO 这里关闭校验,方便新手快速测试,实际线上项目建议开启
+ }
+
+ /**增删改查统一接口,这个一个接口可替代 7 个万能通用接口,牺牲一些路由解析性能来提升一点开发效率
+ * @param method
+ * @param request
+ * @param session
+ * @return
+ */
+ @PostMapping(value = "{method}") // 如果和其它的接口 URL 冲突,可以加前缀,例如改为 crud/{method} 或 Controller 注解 @RequestMapping("crud")
+ @Override
+ public String crud(@PathVariable String method, @RequestBody String request, HttpSession session) {
+ return super.crud(method, request, session);
+ }
+
+ /**增删改查统一接口,这个一个接口可替代 7 个万能通用接口,牺牲一些路由解析性能来提升一点开发效率
+ * @param method
+ * @param tag
+ * @param params
+ * @param request
+ * @param session
+ * @return
+ */
+ @PostMapping("{method}/{tag}") // 如果和其它的接口 URL 冲突,可以加前缀,例如改为 crud/{method}/{tag} 或 Controller 注解 @RequestMapping("crud")
+ @Override
+ public String crudByTag(@PathVariable String method, @PathVariable String tag, @RequestParam Map params, @RequestBody String request, HttpSession session) {
+ return super.crudByTag(method, tag, params, request, session);
+ }
+
+ /**获取
+ * 只为兼容HTTP GET请求,推荐用HTTP POST,可删除
+ * @param request 只用String,避免encode后未decode
+ * @param session
+ * @return
+ * @see {@link RequestMethod#GET}
+ */
+ @GetMapping("get/{request}")
+ public String openGet(@PathVariable String request, HttpSession session) {
+ try {
+ request = URLDecoder.decode(request, StringUtil.UTF_8);
+ } catch (Exception e) {
+ // Parser 会报错
+ }
+ return get(request, session);
+ }
+
+}
\ No newline at end of file
diff --git a/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoFunctionParser.java b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoFunctionParser.java
new file mode 100644
index 00000000..92e729c7
--- /dev/null
+++ b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoFunctionParser.java
@@ -0,0 +1,93 @@
+package apijson.demo;
+
+import javax.servlet.http.HttpSession;
+
+import com.alibaba.fastjson.JSONObject;
+
+import apijson.NotNull;
+import apijson.RequestMethod;
+import apijson.StringUtil;
+import apijson.framework.APIJSONFunctionParser;
+import apijson.framework.APIJSONVerifier;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class DemoFunctionParser extends APIJSONFunctionParser {
+ public DemoFunctionParser() {
+ this(null, null, 0, null, null);
+ }
+
+ // 展示在远程函数内部可以用 this 拿到的东西
+ public DemoFunctionParser(RequestMethod method, String tag, int version, JSONObject request, HttpSession session) {
+ super(method, tag, version, request, session);
+ }
+
+ /***
+ * 获取当前用户id
+ *
+ * @param current
+ * @return
+ */
+ public String getCurrentUserId(@NotNull JSONObject current) {
+ if (this.getSession() == null) {
+ return "test"; // 启动时的自动测试
+ }
+ return APIJSONVerifier.getVisitorId(getSession());
+ }
+
+ /**
+ * 一个最简单的远程函数示例,返回一个前面拼接了 Hello 的字符串
+ *
+ * @param current
+ * @param name
+ * @return
+ * @throws Exception
+ */
+ public String sayHello(@NotNull JSONObject current, @NotNull String name) throws Exception {
+ // 注意这里参数 name 是 key,不是 value
+ Object obj = current.get(name);
+
+ if (this.getSession() == null) {
+ return "test"; // 启动时的自动测试
+ }
+
+ if (obj == null) {
+ throw new IllegalArgumentException();
+ }
+ if (!(obj instanceof String)) {
+ throw new IllegalArgumentException();
+ }
+
+ // 之后可以用 this.getSession 拿到当前的 HttpSession
+ return "Hello, " + obj.toString();
+ }
+
+ /***
+ * 密码加密
+ *
+ * @param current
+ * @param id 添加id生成
+ * @param password 密码字段名
+ * @return
+ * @throws Exception
+ */
+ public void pwdEncrypt(@NotNull JSONObject current, @NotNull String id, @NotNull String password)
+ throws Exception {
+ String c_password = current.getString(password);
+ current.put(password, c_password + "_" + System.currentTimeMillis());
+ }
+
+ public void childFunTest(@NotNull JSONObject current, @NotNull String addr) throws Exception {
+ String c_addr = current.getString(addr);
+ current.put(addr, c_addr + "_" + System.currentTimeMillis());
+ }
+
+
+ public void removeKeys(@NotNull JSONObject current, String keys) {
+ String[] ks = StringUtil.split(keys, ";"); // 用分号 ; 分割
+ // 根据 ks remove 掉 current 里的字段
+ for (int i = 0; i < ks.length; i++) {
+ current.remove(ks[i]);
+ }
+ }
+}
diff --git a/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoObjectParser.java b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoObjectParser.java
new file mode 100644
index 00000000..627de64c
--- /dev/null
+++ b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoObjectParser.java
@@ -0,0 +1,26 @@
+package apijson.demo;
+
+import java.util.List;
+
+import javax.servlet.http.HttpSession;
+
+import com.alibaba.fastjson.JSONObject;
+
+import apijson.NotNull;
+import apijson.RequestMethod;
+import apijson.framework.APIJSONObjectParser;
+import apijson.orm.Join;
+import apijson.orm.SQLConfig;
+
+public class DemoObjectParser extends APIJSONObjectParser {
+
+ public DemoObjectParser(HttpSession session, @NotNull JSONObject request, String parentPath, SQLConfig arrayConfig
+ , boolean isSubquery, boolean isTable, boolean isArrayMainTable) throws Exception {
+ super(session, request, parentPath, arrayConfig, isSubquery, isTable, isArrayMainTable);
+ }
+
+ @Override
+ public SQLConfig newSQLConfig(RequestMethod method, String table, String alias, JSONObject request, List joinList, boolean isProcedure) throws Exception {
+ return DemoSQLConfig.newSQLConfig(method, table, alias, request, joinList, isProcedure);
+ }
+}
diff --git a/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoParser.java b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoParser.java
new file mode 100644
index 00000000..e12333e6
--- /dev/null
+++ b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoParser.java
@@ -0,0 +1,35 @@
+package apijson.demo;
+
+import com.alibaba.fastjson.JSONObject;
+
+import apijson.RequestMethod;
+import apijson.framework.APIJSONObjectParser;
+import apijson.framework.APIJSONParser;
+import apijson.orm.SQLConfig;
+
+public class DemoParser extends APIJSONParser {
+ public DemoParser() {
+ super();
+ }
+
+ public DemoParser(RequestMethod method) {
+ super(method);
+ }
+
+ public DemoParser(RequestMethod method, boolean needVerify) {
+ super(method, needVerify);
+ }
+
+ // 可重写来设置最大查询数量
+ // @Override
+ // public int getMaxQueryCount() {
+ // return 50;
+ // }
+
+ @Override
+ public APIJSONObjectParser createObjectParser(JSONObject request, String parentPath, SQLConfig arrayConfig, boolean isSubquery, boolean isTable, boolean isArrayMainTable) throws Exception {
+ return new DemoObjectParser(getSession(), request, parentPath, arrayConfig, isSubquery, isTable, isArrayMainTable).setMethod(getMethod()).setParser(this);
+ }
+
+
+}
\ No newline at end of file
diff --git a/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoSQLConfig.java b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoSQLConfig.java
new file mode 100644
index 00000000..6337d426
--- /dev/null
+++ b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoSQLConfig.java
@@ -0,0 +1,133 @@
+/*Copyright ©2016 TommyLemon(https://github.com/TommyLemon/APIJSON)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.*/
+
+package apijson.demo;
+
+import java.util.UUID;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+import apijson.RequestMethod;
+import apijson.framework.APIJSONSQLConfig;
+import apijson.orm.AbstractSQLConfig;
+
+
+/**SQL 配置
+ * TiDB 用法和 MySQL 一致
+ * 具体见详细的说明文档 C.开发说明 C-1-1.修改数据库链接
+ * https://github.com/Tencent/APIJSON/blob/master/%E8%AF%A6%E7%BB%86%E7%9A%84%E8%AF%B4%E6%98%8E%E6%96%87%E6%A1%A3.md#c-1-1%E4%BF%AE%E6%94%B9%E6%95%B0%E6%8D%AE%E5%BA%93%E9%93%BE%E6%8E%A5
+ * @author Lemon
+ */
+public class DemoSQLConfig extends APIJSONSQLConfig {
+
+ public DemoSQLConfig() {
+ super();
+ }
+
+ public DemoSQLConfig(RequestMethod method, String table) {
+ super(method, table);
+ }
+
+ static {
+// DEFAULT_DATABASE = DATABASE_ELASTICSEARCH; // TODO 默认数据库类型,改成你自己的
+// DEFAULT_SCHEMA = "sys"; // TODO 默认数据库名/模式,改成你自己的,默认情况是 MySQL: sys, PostgreSQL: public, SQL Server: dbo, Oracle:
+
+ // 表名和数据库不一致的,需要配置映射关系。只使用 APIJSONORM 时才需要;
+ // 如果用了 apijson-framework 且调用了 APIJSONApplication.init 则不需要
+ // (间接调用 DemoVerifier.init 方法读取数据库 Access 表来替代手动输入配置)。
+ // 但如果 Access 这张表的对外表名与数据库实际表名不一致,仍然需要这里注册。例如
+ // TABLE_KEY_MAP.put(Access.class.getSimpleName(), "access");
+
+ SIMPLE_CALLBACK = new SimpleCallback() {
+
+ @Override
+ public AbstractSQLConfig getSQLConfig(RequestMethod method, String database, String schema,
+ String datasource, String table) {
+ return new DemoSQLConfig(method, table);
+ }
+
+ // 取消注释来实现数据库自增 id
+ @Override
+ public String newId(RequestMethod method, String database, String schema, String datasource, String table) {
+ if(table.equals("Access") || table.equals("Request") || table.equals("Function")){
+ return null;
+ }
+ return UUID.randomUUID().toString(); // return null 则不生成 id,一般用于数据库自增 id
+ }
+ };
+ }
+
+ @JSONField(serialize = false) // 不在日志打印 账号/密码 等敏感信息,用了 UnitAuto 则一定要加
+ @Override
+ public String getDBVersion() {
+ return DynamicDataSource.getDetail(this.getDatasource()).getDbVersion();
+ }
+
+ @JSONField(serialize = false) // 不在日志打印 账号/密码 等敏感信息,用了 UnitAuto 则一定要加
+ @Override
+ public String getDatabase() {
+ if (super.getDatabase() != null) {
+ return super.getDatabase();
+ }
+ try {
+ return DynamicDataSource.getDetail(this.getDatasource()).getDatabase();
+ } catch (Exception e) {
+ throw new IllegalArgumentException("动态数据源配置错误 " + this.getDatasource());
+ }
+ }
+
+ @JSONField(serialize = false) // 不在日志打印 账号/密码 等敏感信息,用了 UnitAuto 则一定要加
+ @Override
+ public String getSchema() {
+ if (super.getSchema() != null) {
+ return super.getSchema();
+ }
+ try {
+ return DynamicDataSource.getDetail(this.getDatasource()).getSchema();
+ } catch (Exception e) {
+ throw new IllegalArgumentException("动态数据源配置错误 " + this.getDatasource());
+ }
+ }
+
+ @JSONField(serialize = false) // 不在日志打印 账号/密码 等敏感信息,用了 UnitAuto 则一定要加
+ @Override
+ public String getDBUri() {
+ try {
+ return DynamicDataSource.getDetail(this.getDatasource()).getUrl(); // 数据库连接url
+ } catch (Exception e) {
+ throw new IllegalArgumentException("动态数据源配置错误 " + this.getDatasource());
+ }
+ }
+
+ @JSONField(serialize = false) // 不在日志打印 账号/密码 等敏感信息,用了 UnitAuto 则一定要加
+ @Override
+ public String getDBAccount() {
+ try {
+ return DynamicDataSource.getDetail(this.getDatasource()).getDbAccount();
+ } catch (Exception e) {
+ throw new IllegalArgumentException("动态数据源配置错误 " + this.getDatasource());
+ }
+ }
+
+ @JSONField(serialize = false) // 不在日志打印 账号/密码 等敏感信息,用了 UnitAuto 则一定要加
+ @Override
+ public String getDBPassword() {
+ try {
+ return DynamicDataSource.getDetail(this.getDatasource()).getDbPassword();
+ } catch (Exception e) {
+ throw new IllegalArgumentException("动态数据源配置错误 " + this.getDatasource());
+ }
+ }
+
+}
diff --git a/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoSQLExecutor.java b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoSQLExecutor.java
new file mode 100644
index 00000000..66b17e00
--- /dev/null
+++ b/APIJSON-Java-Server/APIJSONDemo-MultiDataSource-RediSQL/src/main/java/apijson/demo/DemoSQLExecutor.java
@@ -0,0 +1,206 @@
+/*Copyright ©2016 TommyLemon(https://github.com/TommyLemon/APIJSON)
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.*/
+
+package apijson.demo;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.sql.DataSource;
+import com.redbeardlab.redisql.client.ParseRediSQLReply;
+import apijson.Log;
+import apijson.NotNull;
+import apijson.StringUtil;
+import apijson.demo.resultSet.DataBuildResultSet;
+import apijson.framework.APIJSONSQLExecutor;
+import apijson.orm.SQLConfig;
+import lombok.extern.log4j.Log4j2;
+
+/**
+ * SQL 执行器,支持连接池及多数据源 具体见 https://github.com/Tencent/APIJSON/issues/151
+ *
+ * @author Lemon
+ */
+@Log4j2
+public class DemoSQLExecutor extends APIJSONSQLExecutor {
+ public static final String TAG = "DemoSQLExecutor";
+
+ // 适配连接池,如果这里能拿到连接池的有效 Connection,则 SQLConfig 不需要配置 dbVersion, dbUri, dbAccount,
+ // dbPassword
+ @Override
+ public Connection getConnection(SQLConfig config) throws Exception {
+ String datasource = config.getDatasource();
+ Log.d(TAG, "getConnection config.getDatasource() = " + datasource);
+
+ String key = datasource + "-" + config.getDatabase();
+ Connection conn = connectionMap.get(key);
+ if (conn == null || conn.isClosed()) {
+ DataSource dataSource = DataBaseUtil.getDataSource(datasource);
+ connectionMap.put(key, dataSource == null ? null : dataSource.getConnection());
+ }
+ return super.getConnection(config);
+ }
+
+ @Override
+ public ResultSet executeQuery(@NotNull SQLConfig config, String sql) throws Exception {
+ if (config.isRedis()) {
+ return redisExecuteQuery(config, sql);
+ }
+ return super.executeQuery(config, sql);
+ }
+
+ @SuppressWarnings("unchecked")
+ public ResultSet redisExecuteQuery(@NotNull SQLConfig config, String sql) throws Exception {
+ List> rsData = new ArrayList<>();
+ List headers = new ArrayList<>();
+ // redis不支持explain 查询
+ if (config.isExplain()) {
+ // 没有查询到数据
+ return new DataBuildResultSet(null, headers, rsData);
+ }
+
+ if (config.getColumn() != null) {
+ headers = config.getColumn();
+ }
+
+ if (config.getTable().startsWith(JedisBuildData.REDIS_TABLE_KEY)) {
+ if (config.getColumn() != null) {
+ headers = config.getColumn();
+ } else {
+ // redisql不支持返回字段名, select * 请把字段填充进去
+ throw new IllegalArgumentException("redis table 查询必须指定 column !");
+ }
+ sql = StringUtil.isEmpty(sql) ? config.getSQL(false) : sql;
+ switch (config.getMethod()) {
+ case GET:
+ case HEAD:
+ List