Explorar el Código

feat:添加声明型jdbc支持

zuy hace 1 año
padre
commit
1e2b9cc6e4
Se han modificado 55 ficheros con 9191 adiciones y 9 borrados
  1. 6 0
      car-wash-admin/pom.xml
  2. 298 0
      car-wash-admin/src/main/java/com/kym/admin/utils/CommJdbcCodeGenerator.java
  3. 139 0
      car-wash-admin/src/main/java/com/kym/admin/utils/DBHelper.java
  4. 133 0
      car-wash-admin/src/main/resources/application-dev1.yml
  5. 207 0
      car-wash-admin/src/main/resources/tmpl/Controller.java.vm
  6. 51 0
      car-wash-admin/src/main/resources/tmpl/Service.java.vm
  7. 166 0
      car-wash-admin/src/main/resources/tmpl/ServiceImpl.java.vm
  8. 13 0
      car-wash-admin/src/main/resources/tmpl/dao.vm
  9. 153 0
      car-wash-admin/src/main/resources/tmpl/dialog.vm
  10. 171 0
      car-wash-admin/src/main/resources/tmpl/drawer.vm
  11. 262 0
      car-wash-admin/src/main/resources/tmpl/listvue.vm
  12. 134 0
      car-wash-admin/src/main/resources/tmpl/pojo.vm
  13. 114 0
      car-wash-common/src/main/java/com/kym/common/ContextHelper.java
  14. 32 0
      car-wash-common/src/main/java/com/kym/common/IUser.java
  15. 0 8
      car-wash-common/src/main/java/com/kym/common/filter/RequestLogFilter.java
  16. 8 0
      car-wash-entity/pom.xml
  17. 54 0
      car-wash-jdbc/pom.xml
  18. 473 0
      car-wash-jdbc/src/main/java/com/kym/DbUtil.java
  19. 175 0
      car-wash-jdbc/src/main/java/com/kym/JacksonUtil.java
  20. 9 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/BasicData.java
  21. 25 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/BasicEntity.java
  22. 32 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/BasicQuery.java
  23. 37 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/Bean.java
  24. 9 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/DbException.java
  25. 15 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/ExtQuery.java
  26. 7 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/IBuilder.java
  27. 30 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/IHandler.java
  28. 542 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/MBuilder.java
  29. 879 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/OBuilder.java
  30. 15 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/ResultHandler.java
  31. 16 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/SQLEntity.java
  32. 11 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/CNT.java
  33. 52 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/DBF.java
  34. 33 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/Entity.java
  35. 28 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/FK.java
  36. 9 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/JoinType.java
  37. 39 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/Many.java
  38. 28 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/OP.java
  39. 53 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/One.java
  40. 22 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/QE.java
  41. 62 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/QF.java
  42. 8 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/QueryType.java
  43. 16 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/express/DdlSQLExpress.java
  44. 82 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/express/DeleteSQLExpress.java
  45. 133 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/express/InsertSQLExpress.java
  46. 265 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/express/SQLExpress.java
  47. 58 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/express/SelectSQLExpress.java
  48. 15 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/express/UpdateSQLExpress.java
  49. 10 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/lambda/ColumnFunc.java
  50. 48 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/lambda/DefaultLambdaParser.java
  51. 1948 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/template/JdbcExecute.java
  52. 1733 0
      car-wash-jdbc/src/main/java/com/kym/jdbc/template/JdbcHelper.java
  53. 7 0
      car-wash-service/pom.xml
  54. 307 0
      car-wash-service/src/main/java/com/kym/dao/DbHandler.java
  55. 19 1
      pom.xml

+ 6 - 0
car-wash-admin/pom.xml

@@ -49,6 +49,12 @@
             <version>1.6.2</version>
         </dependency>
 
+        <dependency>
+            <groupId>org.apache.velocity</groupId>
+            <artifactId>velocity</artifactId>
+            <version>1.7</version>
+        </dependency>
+
     </dependencies>
 
     <properties>

+ 298 - 0
car-wash-admin/src/main/java/com/kym/admin/utils/CommJdbcCodeGenerator.java

@@ -0,0 +1,298 @@
+package com.kym.admin.utils;
+
+import cn.hutool.core.io.IoUtil;
+import com.google.common.collect.ImmutableMap;
+import com.kym.common.utils.CommUtil;
+import com.kym.JacksonUtil;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.StringWriter;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 代码生成器业务类
+ *
+ * @author yaop
+ * @date 2024-05-03T18:00:29.140164200
+ */
+@Slf4j
+public class CommJdbcCodeGenerator {
+
+
+    public static final int CONTROLLER = 1;
+    public static final int SERVICE = 2;
+    public static final int LISTVUE = 3;
+    public static final int EDITVUE = 4;
+    public static final int INFOVUE = 5;
+    public static final int POJO = 6;
+    public static final int PERMISSION = 7;
+    public static final int MAPPER = 8;
+
+    private static DBHelper db;
+
+
+    private void closeDb() {
+        if (null != db) {
+            db.closeDb();
+        }
+    }
+
+    private void openDb() throws Exception {
+        db = new DBHelper();
+        db.open("jdbc:mysql://121.40.98.15:3307/car-wash-admin?serverTimezone=Asia/Shanghai", "root", "KuaiyuMan/*-");
+    }
+
+    public static void main(String[] args) {
+        CodeGeneratorParam param = new CodeGeneratorParam();
+        param.setTableName("t_admin_user");
+        param.setBasePackage("com.kym.admin");
+
+
+
+
+        List<Map<String, String>> pathList = new ArrayList<>();
+        pathList.add(ImmutableMap.of("vm", "Controller.java.vm", "output", "/home/zuy/02.code/wash/car-wash-admin/src/main/java/com/kym/admin/controller"));
+//        pathList.add(ImmutableMap.of("vm", "Service.java.vm", "output", "/home/zuy/02.code/wash/car-wash-service/src/main/java/com/kym/service/admin/"));
+//        pathList.add(ImmutableMap.of("vm", "ServiceImpl.java.vm", "output", "/home/zuy/02.code/wash/car-wash-service/src/main/java/com/kym/service/admin/impl"));
+//        pathList.add(ImmutableMap.of("vm", "dao.vm", "output", "src/main/java/com/kym/admin/controller/"));
+//        pathList.add(ImmutableMap.of("vm", "pojo.vm", "output", "src/main/java/com/kym/admin/controller/"));
+//        pathList.add(ImmutableMap.of("vm", "listvue.vm", "output", "src/main/java/com/kym/admin/controller/"));
+//        pathList.add(ImmutableMap.of("vm", "dialog.vm", "output", "src/main/java/com/kym/admin/controller/"));
+//        pathList.add(ImmutableMap.of("vm", "drawer.vm", "output", "src/main/java/com/kym/admin/controller/"));
+        param.setPathList(pathList);
+        new CommJdbcCodeGenerator().generate(param);
+    }
+
+    /**
+     * 数据表列表
+     *
+     * @param databaseId
+     * @return
+     */
+    public Object tableList(int databaseId) {
+        try {
+            openDb();
+            String sql = "SELECT table_name tableName, ENGINE, table_comment tableComment, create_time createTime FROM information_schema.tables " + "WHERE  table_schema=?";
+            List<Map<String, Object>> maps = db.executeQueryList(sql, "car-wash-admin");
+            return ImmutableMap.of("list", maps);
+        } catch (Exception e) {
+            log.error("tableList error", e);
+        } finally {
+            closeDb();
+        }
+        return Collections.emptyList();
+    }
+
+
+    private String getPojoName(String name) {
+        StringBuilder result = new StringBuilder();
+        if (name.startsWith("t_")) {
+            name = name.substring(2);
+        }
+        char[] chars = name.toCharArray();
+        int comulaIdx = -1;
+        for (int i = 0; i < chars.length; i++) {
+            if (i == 0) {
+                result.append(Character.toUpperCase(chars[i]));
+            } else if (chars[i] == "_".toCharArray()[0]) {
+                comulaIdx = i;
+            } else if (i - 1 == comulaIdx) {
+                result.append(Character.toUpperCase(chars[i]));
+            } else {
+                result.append(chars[i]);
+            }
+
+        }
+        return result.toString();
+    }
+
+    public static String getFirstLowCase(String name) {
+        StringBuilder sbr = new StringBuilder();
+        char[] chars = name.toCharArray();
+        for (int i = 0; i < chars.length; i++) {
+            if (i == 0) {
+                sbr.append(Character.toLowerCase(chars[i]));
+            } else {
+                sbr.append(chars[i]);
+            }
+        }
+        return sbr.toString();
+    }
+
+    private String getPojoFieldName(String name) {
+        StringBuilder result = new StringBuilder();
+        char[] chars = name.toCharArray();
+        int comulaIdx = -10;
+        for (int i = 0; i < chars.length; i++) {
+            if (chars[i] == "_".toCharArray()[0]) {
+                comulaIdx = i;
+            } else if (i - 1 == comulaIdx) {
+                result.append(Character.toUpperCase(chars[i]));
+            } else {
+                result.append(chars[i]);
+            }
+        }
+        return result.toString();
+    }
+
+
+    public void generate(CodeGeneratorParam param) {
+
+        try {
+            Map<String, Object> vars = vars(param);
+            VelocityEngine engine = new VelocityEngine();
+            Velocity.init();
+            VelocityContext context = new VelocityContext();
+            vars.forEach(context::put);
+            for (Map<String, String> entry : param.getPathList()) {
+                String vm = entry.get("vm");
+                String output = entry.get("output");
+                Object o = vars.get("pojoName");
+                StringWriter writer = new StringWriter();
+                String content = IoUtil.read(getClass().getClassLoader().getResourceAsStream("tmpl/" + vm), "utf-8");
+                engine.evaluate(context, writer, "code", content);
+                String string = writer.toString();
+                writer.flush();
+                IoUtil.close(writer);
+                FileWriter writer1 = new FileWriter(new File(output, o.toString() + vm.split(".vm")[0]));
+                writer1.write(string);
+                IoUtil.close(writer1);
+
+            }
+        } catch (Exception e) {
+           e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * 模板变量清单
+     *
+     * @param param
+     * @return
+     */
+    public Map<String, Object> vars(CodeGeneratorParam param) {
+        Map<String, Object> context = new HashMap<>(64);
+        DevVariableVo varVo = new DevVariableVo();
+        String sql = "SELECT table_name  as tablename, ENGINE, table_comment as tablecomment, create_time createTime FROM information_schema.tables " + "WHERE  table_name= ?  ";
+        try {
+            openDb();
+            Map<String, Object> dataMap = db.executeQuery(sql, param.getTableName());
+            varVo.setTableName(CommUtil.null2String(dataMap.get("tablename")));
+            varVo.setTableComment(CommUtil.null2String(dataMap.get("tablecomment")));
+
+            log.info("generate list:{}", JacksonUtil.toJSONString(varVo));
+
+        //table meta
+        StringBuilder sbr = new StringBuilder("insert into t_permission (name,pid,perm,level) values ");
+        String[] premissionLabel = {"list", "list", "modify", "remove", "add", "status"};
+        StringBuilder rb = new StringBuilder("insert into t_permission (name,pid,perm,level) values (\'").append(varVo.getTableComment()).append("\',0,\'").append(getFirstLowCase(getPojoName(varVo.getTableName()))).append(".list\',0)");
+        System.out.println("===============================\n");
+        System.out.println(rb.toString() );
+//        System.out.println(rb + " RETURNING id;");
+                System.out.println("SELECT @parentId := LAST_INSERT_ID();");
+
+
+        // permission SQL
+        for (int i = 0; i < premissionLabel.length; i++) {
+            String pn = varVo.getTableComment();
+            if (i == 1) {
+                pn = "查看";
+            } else if (i == 2) {
+                pn = "编辑";
+            } else if (i == 3) {
+                pn = "删除";
+            } else if (i == 4) {
+                pn = "新增";
+            }
+            if (i == 0) {
+            } else {
+                sbr.append("(\'").append(pn).append("\',@parentId,\'").append(getFirstLowCase(getPojoName(varVo.getTableName()))).append(".").append(premissionLabel[i]).append("\',1),");
+            }
+        }
+
+        System.out.println(sbr.substring(0, sbr.length() - 1));
+        System.out.println("\n===============================");
+
+            List<Map<String, Object>> tableVarList = new ArrayList<>();
+            String tableSql = "select column_name,data_type,character_maximum_length,column_comment from information_schema.columns" + " where  table_name = '" + varVo.getTableName() + "'";
+            log.warn(tableSql);
+            List<Map<String, Object>> tableVarDataList = db.executeQueryList(tableSql);
+            tableVarDataList.forEach(tableRet -> {
+                Map<String, Object> map = new HashMap<>();
+                map.put("columnName", getPojoFieldName(CommUtil.null2String(tableRet.get("column_name"))));
+                map.put("column", CommUtil.null2String(tableRet.get("column_name")));
+                map.put("columnComment", tableRet.get("column_comment"));
+                String dataType = CommUtil.null2String(tableRet.get("data_type"));
+                if ("int4".equals(dataType)) {
+                    dataType = "int";
+                }
+                if ("int8".equals(dataType)) {
+                    dataType = "bigint";
+                }
+                map.put("dataType", dataType);
+                map.put("viewType", CommUtil.null2String(tableRet.get("data_type")));
+                map.put("dataLength", CommUtil.null2String(tableRet.get("character_maximum_length")));
+                tableVarList.add(map);
+            });
+            varVo.setColumnSchemaList(tableVarList);
+
+            context.put("fields", varVo.getColumnSchemaList());
+            context.put("fieldSize", varVo.getColumnSchemaList().size());
+            context.put("pojoComment", varVo.getTableComment());
+            context.put("tableName", varVo.getTableName());
+            String pojoName = getPojoName(varVo.getTableName());
+            context.put("pojoName", pojoName);
+            context.put("lowPojoName", pojoName.toLowerCase());
+            context.put("firstLowPojoName", pojoName.substring(0, 1).toLowerCase() + pojoName.substring(1));
+            context.put("basePackage", param.getBasePackage());
+            context.put("dollar", "$");
+            context.put("datetime", LocalDateTime.now().toString());
+
+            return context;
+        } catch (Exception e) {
+            log.error("vars error", e);
+        } finally {
+            closeDb();
+        }
+
+        return context;
+    }
+
+    @Data
+    public static class DevVariableVo {
+        private String tableName;
+        private String tableComment;
+        private List<Map<String, Object>> columnSchemaList;
+    }
+
+
+    @Data
+    public static class CodeGeneratorParam {
+        private long databaseId;
+        private String basePackage;
+        private String tableName;
+        private List<Long> plainTemplateIdList;
+        private boolean permSQL;
+        private List<Map<String, String>> pathList;
+
+    }
+
+
+}
+
+
+
+

+ 139 - 0
car-wash-admin/src/main/java/com/kym/admin/utils/DBHelper.java

@@ -0,0 +1,139 @@
+package com.kym.admin.utils;
+
+import com.kym.DbUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 数据库链接工具
+ */
+public class DBHelper implements AutoCloseable {
+
+    private Logger logger = LoggerFactory.getLogger(DBHelper.class);
+
+    public final String DIALECT_MYSQL = "mysql";
+    public final String DIALECT_PGSQL = "postgresql";
+
+
+    private Connection conn = null;
+    private PreparedStatement pst = null;
+
+    public void open(String url, String username, String password) throws Exception {
+        Class.forName("com.mysql.cj.jdbc.Driver");
+        this.conn = DriverManager.getConnection(url, username, password);
+    }
+
+    public List<Map<String, Object>> executeQueryList(String sql, Object... params) {
+        List<Map<String, Object>> result = new ArrayList<>();
+        ResultSet resultSet = null;
+        try {
+            logger.info("executeQueryList sql:\n{},params:\n{}", sql, params);
+            pst = this.conn.prepareStatement(sql);
+            DbUtil.setParameter(pst, params);
+            resultSet = pst.executeQuery();
+            ResultSetMetaData metaData = resultSet.getMetaData();
+            int columnCount = metaData.getColumnCount();
+            while (resultSet.next()) {
+                Map<String, Object> data = new HashMap<>();
+                for (int i = 1; i <= columnCount; i++) {
+                    String columnName = metaData.getColumnName(i);
+                    data.put(columnName, resultSet.getString(i));
+                }
+                result.add(data);
+            }
+        } catch (Exception e) {
+            logger.error("DBHelper executeQueryList error", e);
+        } finally {
+            if (null != resultSet) {
+                try {
+                    resultSet.close();
+                } catch (SQLException e) {
+                    logger.error("DBHelper executeQueryList close error", e);
+                }
+            }
+        }
+
+        return result;
+    }
+
+    public Map<String, Object> executeQuery(String sql, Object... params) throws Exception {
+        Map<String, Object> result = new HashMap<>();
+        ResultSet resultSet = null;
+        logger.info("executeQuery sql:\n{}", sql);
+        try {
+            pst = this.conn.prepareStatement(sql);//准备执行语句
+            DbUtil.setParameter(pst, params);
+            resultSet = pst.executeQuery();
+            ResultSetMetaData metaData = resultSet.getMetaData();
+            int columnCount = metaData.getColumnCount();
+            while (resultSet.next()) {
+                for (int i = 1; i <= columnCount; i++) {
+                    String columnName = metaData.getColumnLabel(i);
+                    result.put(columnName, resultSet.getString(i));
+                }
+            }
+        } catch (Exception e) {
+            logger.error("DBHelper executeQuery error", e);
+        } finally {
+            if (null != resultSet) {
+                try {
+                    resultSet.close();
+                } catch (SQLException e) {
+                    logger.error("DBHelper executeQuery close error", e);
+                }
+            }
+        }
+        return result;
+    }
+
+
+    public void executeUpdate(String sql, Object... params) throws Exception {
+        try {
+            logger.info("executeUpdate sql:\n{}", sql);
+            pst = this.conn.prepareStatement(sql);//准备执行语句
+            DbUtil.setParameter(pst, params);
+            int i = pst.executeUpdate();
+            logger.info("DBHelper executeUpdate rows:{}", i);
+        } catch (Exception e) {
+            logger.error("DBHelper executeUpdate error", e);
+        }
+    }
+
+    //关闭
+    public void closeDb() {
+        try {
+            if (null != pst) {
+                if (!pst.isClosed()) {
+                    pst.close();
+                    logger.info("close preparedStatement...");
+                }
+            }
+            if (null != conn) {
+                if (!conn.isClosed()) {
+                    conn.close();
+                    logger.info("close connection..");
+                }
+            }
+        } catch (SQLException e) {
+            logger.error(e.getMessage(), e);
+        }
+    }
+
+
+    @Override
+    public void close() throws Exception {
+        closeDb();
+    }
+}

+ 133 - 0
car-wash-admin/src/main/resources/application-dev1.yml

@@ -0,0 +1,133 @@
+# EN+充电配置
+en-plus:
+  # 运营商ID
+  operatorId: MA5HJNDG1
+  # 运营商密钥
+  operatorSecret: 5009db3dc1e94ea8
+  # 消息密钥
+  dataSecret: 8c15f5bf050948ba
+  # 消息密钥初始化向量
+  dataSecretIv: 915bea94fa13461d
+  # 签名密钥
+  sigSecret: 46050b0bb5b7415c
+  # 最小充电余额(分)
+  chargeMinAmount: 200
+  # 接口地址
+  apiDomain: https://dev.en-plus.cn/Charge/evcs/v1//MA5HJNDG1/
+  # sass配置
+  sass: https://dev.en-plus.cn/Charge/op/login?username=快与慢&password=Admin123
+  # sass结算订单
+  sassClose: https://dev.en-plus.cn/Charge/op/analyze/order/close?orderCode=
+
+# 微信支付
+wechat:
+  payment:
+    appid: wx369fcff95d387bde
+    # 微信商户号
+    mchid: 1635831469
+    mchsn: 6A45EEB068369430B2FFD45EA29F641A8E18165F
+    v3Key: iTRovdvaTUQq0b9Jr91D7Tx66JnIes5U
+    notifyUrl: https://dev.kuaiyuman.cn/api/payment/notify
+    refundNotifyUrl: https://dev.kuaiyuman.cn/api/payment/refundNotify
+    certPath: cert/apiclient_cert.pem
+    keyPath: cert/apiclient_key.pem
+
+  miniapp:
+    appid: wx369fcff95d387bde
+    secret: e36560b99afd5f744754cd09e8f6cc2a
+    # 以下需要先开通消息推送
+    token: #微信小程序消息服务器配置的token
+    aesKey: #微信小程序消息服务器配置的EncodingAESKey
+    msgDataFormat: JSON
+
+  #运营看板小程序
+  kanban:
+    appid: wx35b22037ccbc7b3f
+    secret: b59ff1b7d160b5c0efe33570a2f3fdf2
+    token:
+
+  fapiao:
+    baseInformation: https://api.mch.weixin.qq.com/v3/new-tax-control-fapiao/merchant/base-information
+    taxCodes: https://api.mch.weixin.qq.com/v3/new-tax-control-fapiao/merchant/tax-codes
+    fapiaoApplications: https://api.mch.weixin.qq.com/v3/new-tax-control-fapiao/fapiao-applications
+    notifyUrl: https://dev.kuaiyuman.cn/api/invoice/notify
+    devConfig: https://api.mch.weixin.qq.com/v3/new-tax-control-fapiao/merchant/development-config
+    fapiaoFiles: https://api.mch.weixin.qq.com/v3/new-tax-control-fapiao/fapiao-applications/%s/fapiao-files
+    queryFapiao: https://api.mch.weixin.qq.com/v3/new-tax-control-fapiao/fapiao-applications/%s
+    titleUrl: https://api.mch.weixin.qq.com/v3/new-tax-control-fapiao/user-title/title-url
+    userTitle: https://api.mch.weixin.qq.com/v3/new-tax-control-fapiao/user-title?scene=WITHOUT_WECHATPAY&fapiao_apply_id=%s
+    cardTemplate: https://api.mch.weixin.qq.com/v3/new-tax-control-fapiao/card-template
+
+ # 公众号配置
+  mp:
+    appid: wx93b0ef1be901bd19
+    secret: eea715b3058717f44e17c3e4e5cf1d2f
+    token: kym
+    aeskey: U1ZC5gRrY4DDLZeyKxwpvU5Q7lQvvnQdOV0aX0UPpn6
+
+spring:
+  datasource:
+    url: jdbc:mysql://121.40.98.15:3307/car-wash-admin?serverTimezone=Asia/Shanghai
+    username: root
+    password: KuaiyuMan/*-
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    druid: #以下是全局默认值,可以全局更改
+      #监控统计拦截的filters
+      filters: stat,slf4j
+      #配置初始化大小/最小/最大
+      initial-size: 2
+      min-idle: 2
+      max-active: 20
+      #获取连接等待超时时间
+      max-wait: 60000
+      #间隔多久进行一次检测,检测需要关闭的空闲连接
+      time-between-eviction-runs-millis: 60000
+      #一个连接在池中最小生存的时间
+      min-evictable-idle-time-millis: 300000
+      validation-query: SELECT 'x'
+      test-while-idle: true
+      test-on-borrow: false
+      test-on-return: false
+      #打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
+      pool-prepared-statements: false
+      max-pool-prepared-statement-per-connection-size: 20
+  data:
+    redis:
+      port: 6380
+      host: 121.40.98.15
+      password: KtXA^Zx!TZmLEy(@JjB@2(TVG0kdy5)&
+      database: 11
+      lettuce:
+        pool:
+          min-idle: 1
+        cluster:
+          refresh:
+            adaptive: true
+            period: 20
+  cache:
+    type: redis
+    redis:
+      # 缓存过期时间:7天
+      time-to-live: 604800
+  rabbitmq:
+    host: 121.40.98.15
+    port: 5674
+    username: kym
+    password: kym!@123
+    virtual-host: /
+    publisher-returns: true
+    publisher-confirms: true
+    listener:
+      simple:
+        acknowledge-mode: manual
+        retry:
+          enabled: true
+          max-attempts: 3
+          initial-interval: 3000ms
+          max-interval: 6000ms
+          multiplier: 2
+        consumer-batch-enabled: true #开启批量消费
+        batch-size: 100 #每次批量消费大小
+
+kym:
+  notify-email: skyline@kuaiyuman.cn

+ 207 - 0
car-wash-admin/src/main/resources/tmpl/Controller.java.vm

@@ -0,0 +1,207 @@
+package ${basePackage}.controller;
+
+import ${basePackage}.controller.IController;
+import com.kym.util.log.annotation.CLP;
+import com.kym.util.page.Res;
+import com.kym.util.permission.PermissionHelper;
+import com.kym.util.permission.annotation.Permission;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.validation.Valid;
+import jakarta.annotation.Resource;
+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.RestController;
+
+
+/**
+* ${pojoComment}接口
+*
+* @date ${datetime}
+*/
+@Tag(name = "${pojoComment}")
+@RestController
+@RequestMapping("${firstLowPojoName}")
+public class ${pojoName}Controller extends IController {
+
+@Resource
+private ${pojoName}Service ${firstLowPojoName}Service;
+
+#if(${hasCategory})
+/**
+* ${pojoComment}类型新增接口
+*
+* @param category
+* @return Res
+* @author   ${datetime}
+*/
+@Operation(summary="${pojoComment}类型新增")
+@Permission(values ="${firstLowPojoName}Category.add")
+@PostMapping("addCategory")
+@CLP(desc = "${pojoComment}类型新增接口", title = "${pojoComment}-新增类型")
+public Res addCategory(@Valid @RequestBody ${pojoName}Category.${pojoName}CategoryInfo category) {
+return resp(()->${firstLowPojoName}Service.addCategory(category));
+}
+
+
+/**
+* ${pojoComment}类型编辑接口
+*
+*  @param category
+* @param errors
+*  @return Res
+*  @author   ${datetime}
+*/
+@Operation(summary="${pojoComment}类型编辑")
+@Permission(values ="${firstLowPojoName}Category.modify")
+@PostMapping("modifyCategory")
+@CLP(desc = "${pojoComment}类型更新接口", title = "${pojoComment}-编辑类型")
+public Res modifyCategory(@Valid @RequestBody ${pojoName}Category.${pojoName}CategoryInfo category,  BindingResult errors) {
+return resp((t)->${firstLowPojoName}Service.modifyCategory(category));
+}
+
+
+/**
+* 查询${pojoComment}类型列表接口
+*
+* @param query
+* @return Res
+* @author   ${datetime}
+*/
+@Operation(summary="${pojoComment}类型列表")
+@Permission(values ="${firstLowPojoName}Category.list",p=P.CMS)
+@PostMapping("listCategory")
+@CLP(desc = "查询${pojoComment}类型列表接口")
+public Res listCategory(@RequestBody ${pojoName}Category.${pojoName}CategoryBasicQuery query) {
+return resp(()->${firstLowPojoName}Service.listCategory(query));
+}
+
+/**
+* 查询${pojoComment}类型详情接口
+*
+* @param id
+* @return Res
+* @author   ${datetime}
+*/
+@Operation(summary="${pojoComment}类型详情")
+@Permission(values ="${firstLowPojoName}Category.list",p=P.CMS)
+@GetMapping("detailCategory/{id}")
+@CLP(desc = "查询${pojoComment}类型详情接口")
+public Res detailCategory(@PathVariable long id) {
+return resp(()->${firstLowPojoName}Service.detailCategory(id));
+}
+
+/**
+* ${pojoComment}类型删除接口
+*
+* @return Res
+* @author   ${datetime}
+*/
+@Operation(summary="${pojoComment}类型删除")
+@Permission(values ="${firstLowPojoName}Category.remove")
+@GetMapping("removeCategory/{id}")
+@CLP(desc = "删除${pojoComment}类型",title="${pojoComment}-删除")
+public Res removeCategory(@PathVariable long id) {
+return resp((t)->${firstLowPojoName}Service.removeCategory(id));
+}
+
+
+#end
+
+/**
+* ${pojoComment}新增接口
+*
+* @param ${firstLowPojoName} 新增${pojoComment}
+* @return Res
+* @author   ${datetime}
+*/
+@Operation(summary="${pojoComment}新增")
+@Permission(values ="${firstLowPojoName}.add")
+@PostMapping("add")
+@CLP(desc = "${pojoComment}新增接口", title = "${pojoComment}-新增")
+public Res add(@Valid @RequestBody ${pojoName}Info ${firstLowPojoName}) {
+//other params checked
+return resp(()->${firstLowPojoName}Service.add(${firstLowPojoName}));
+}
+
+/**
+* ${pojoComment}编辑
+*@return Res
+*  @author   ${datetime}
+*/
+@Operation(summary="${pojoComment}更新")
+@Permission(values ="${firstLowPojoName}.modify")
+@PostMapping("modify")
+@CLP(desc = "${pojoComment}更新接口", title = "${pojoComment}-编辑")
+public Res modify(@Valid @RequestBody  ${pojoName}Info ${firstLowPojoName}) {
+return resp((t)->${firstLowPojoName}Service.modify(${firstLowPojoName}));
+}
+
+
+/**
+* ${pojoComment}查询接口
+*
+* @param query 条件构造对象
+* @return Res
+* @author   ${datetime}
+*/
+@Operation(summary="${pojoComment}列表")
+@Permission(values ="${firstLowPojoName}.list",p=PermissionHelper.PLATFORM_CMS)
+@PostMapping("list")
+@CLP(desc = "${pojoComment}列表查询接口")
+public Res list(@RequestBody ${pojoName}.${pojoName}BasicQuery query) {
+return resp(()->${firstLowPojoName}Service.list(query));
+}
+
+
+/**
+* ${pojoComment}查询详情接口
+*
+* @return Res
+* @author   ${datetime}
+*/
+@Operation(summary="${pojoComment}详情")
+@Permission(values ="${firstLowPojoName}.list",p=PermissionHelper.PLATFORM_CMS)
+@GetMapping("detail/{id}")
+@CLP(desc = "${pojoComment}详情查询接口")
+public Res detail(@PathVariable long id) {
+return resp(()->${firstLowPojoName}Service.detail(id));
+}
+
+
+
+/**
+* ${pojoComment}变更状态接口
+*
+* @return Res
+* @author   ${datetime}
+*/
+@Operation(summary="${pojoComment}变更状态")
+@Permission(values ="${firstLowPojoName}.modify")
+@GetMapping("status/{id}/{status}")
+@CLP(desc = "${pojoComment}变更状态",title="${pojoComment}-状态")
+public Res status(@PathVariable long id,@PathVariable int status) {
+return resp((t)->${firstLowPojoName}Service.status(id,status));
+}
+
+
+
+/**
+* ${pojoComment}删除接口
+*
+* @return Res
+* @author   ${datetime}
+*/
+@Operation(summary="${pojoComment}删除")
+@Permission(values ="${firstLowPojoName}.remove")
+@GetMapping("remove/{id}")
+@CLP(desc = "${pojoComment}删除",title="${pojoComment}-删除")
+public Res remove(@PathVariable long id) {
+return resp((t)->${firstLowPojoName}Service.remove(id));
+}
+
+
+}

+ 51 - 0
car-wash-admin/src/main/resources/tmpl/Service.java.vm

@@ -0,0 +1,51 @@
+package ${basePackage}.service;
+
+import com.kym.common.enumeration.MsgEnum;
+import com.kym.common.exception.BizException;
+import com.kym.common.page.Res;
+import com.kym.jdbc.Bean;
+
+import ${basePackage}.entity.po.${pojoName};
+import ${basePackage}.entity.po.${pojoName}.${pojoName}Info;
+import ${basePackage}.entity.po.${pojoName}.${pojoName}BasicQuery;
+
+
+/**
+ * ${pojoComment}业务类
+ * @date ${datetime}
+ */
+public interface  ${pojoName}Service {
+
+    /**
+     * ${pojoComment}分页查询
+     */
+    public Bean<${pojoName}Info> list(${pojoName}BasicQuery query);
+
+    /**
+     * 查询${pojoComment}详情
+     */
+    public ${pojoName}Info detail(long id);
+
+
+    /**
+     * ${pojoComment}状态变更
+     */
+    public void status(long id, int status);
+
+
+    /**
+     * 新增${pojoComment}
+     */
+    public long add(${pojoName}Info ${firstLowPojoName});
+
+    /**
+     * 物理删除${pojoComment}
+     */
+    public Res remove(long id);
+
+    /**
+     * 编辑${pojoComment}
+     */
+    public void modify(${pojoName}Info ${firstLowPojoName});
+
+}

+ 166 - 0
car-wash-admin/src/main/resources/tmpl/ServiceImpl.java.vm

@@ -0,0 +1,166 @@
+package ${basePackage}.service;
+
+import com.kym.common.enumeration.MsgEnum;
+import com.kym.common.exception.BizException;
+import com.kym.common.page.Res;
+import com.kym.jdbc.Bean;
+
+import ${basePackage}.entity.po.${pojoName};
+import ${basePackage}.entity.po.${pojoName}.${pojoName}Info;
+import ${basePackage}.entity.po.${pojoName}.${pojoName}BasicQuery;
+import org.springframework.transaction.annotation.Transactional;
+
+
+/**
+* ${pojoComment}业务类
+* @date ${datetime}
+*/
+@Service("${firstLowPojoName}Service")
+public class ${pojoName}Service extends IService implements ${pojoName}Service{
+
+@Resource
+private  ${pojoName}DAO ${firstLowPojoName}DAO;
+/**
+* ${pojoComment}分页查询
+*/
+public Bean<${pojoName}Info>list(${pojoName}BasicQuery query) {
+    setupQuery(query);
+    return ${firstLowPojoName}DAO.selectPageBean(query);
+    }
+
+    /**
+    * 查询${pojoComment}详情
+    */
+    public ${pojoName}Info detail(long id) {
+    return ${firstLowPojoName}DAO.selectOneExist(${pojoName}Info.class,id);
+    }
+
+
+    /**
+    * ${pojoComment}状态变更
+    */
+    @Transactional(rollbackFor = Exception.class)
+    public void status(long id,int status) {
+    ${pojoName} old = ${firstLowPojoName}DAO.selectOneExist(${pojoName}.class,id);
+    ${pojoName} bean = new  ${pojoName}();
+    bean.setId(id);
+    bean.setStatus(status);
+    ${firstLowPojoName}DAO.updateSelective(bean,"status");
+    super.afterModify(old,bean);
+    }
+
+
+    /**
+    * 新增${pojoComment}
+    */
+    @Transactional(rollbackFor = Exception.class)
+    public long add(${pojoName}Info ${firstLowPojoName}) {
+    ${firstLowPojoName}DAO.checkUniqueValue(${pojoName}.class,oBuilder().eq(${pojoName}::getName, ${firstLowPojoName}.getName()),${pojoName}::getName,${firstLowPojoName}.getId());
+    long ret = ${firstLowPojoName}DAO.insert(${firstLowPojoName});
+    CommUtil.asserts(ret>0, MsgEnum.DB_ERROR);
+
+    super.afterAdd(ret);
+    return ret;
+    }
+
+    /**
+    * 物理删除${pojoComment}
+    */
+    @Transactional(rollbackFor = Exception.class)
+    public Res remove(long id) {
+    ${firstLowPojoName}DAO.delete(${pojoName}.class,id);
+    super.afterRemove(id);
+    return Res.success();
+    }
+
+    /**
+    * 编辑${pojoComment}
+    */
+    @Transactional(rollbackFor = Exception.class)
+    public void modify(${pojoName}Info ${firstLowPojoName}) {
+    ${pojoName}Info oldBean = ${firstLowPojoName}DAO.selectOneExist(${pojoName}Info.class, ${firstLowPojoName}.getId());
+    int ret = ${firstLowPojoName}DAO.update(${firstLowPojoName});
+    CommUtil.asserts(ret>0, MsgEnum.DB_ERROR);
+    super.afterModify(oldBean,${firstLowPojoName});
+    }
+
+
+    #if(${hasCategory})
+        /**
+        * 新增${pojoComment}分类
+        */
+        @Transactional(rollbackFor = Exception.class)
+        public int addCategory(${pojoName}Category category) {
+        ${firstLowPojoName}DAO.checkUniqueValue(${pojoName}Category.class,oBuilder().eq(${pojoName}Category::getName, category.getName()),${pojoName}Category::getName,category.getId());
+        int ret = ${firstLowPojoName}DAO.insert(category);
+        CommUtil.asserts(ret>0, MsgEnum.DB_ERROR);
+        super.afterAdd(ret);
+        return ret;
+        }
+
+
+        /**
+        * ${pojoComment}分类分页查询
+        */
+        public Res listCategory(${pojoName}Category.${pojoName}CategoryBasicQuery query) {
+        return Res.succ(${firstLowPojoName}DAO.selectPageList(query));
+        }
+
+
+        /**
+        * ${pojoComment}分类详情
+        */
+        public ${pojoName}Category detailCategory(long id) {
+        return ${firstLowPojoName}DAO.selectOneExist(${pojoName}Category.class,id)
+        }
+
+
+
+
+        /**
+        * ${pojoComment}分类编辑
+        */
+        @Transactional(rollbackFor = Exception.class)
+        public void modifyCategory(${pojoName}Category category) {
+        ${pojoName} old = ${firstLowPojoName}DAO.selectOneExist(${pojoName}.class,id);
+        int ret =${firstLowPojoName}DAO.update(category);
+        CommUtil.asserts(ret>0, MsgEnum.DB_ERROR);
+        super.afterModify(old,category);
+        }
+
+        /**
+        * ${pojoComment}分类发布/撤回
+        */
+        @Transactional(rollbackFor = Exception.class)
+        public void releaseCategory(long id,int releaseStatus) {
+        ${pojoName} old = ${firstLowPojoName}DAO.selectOneExist(${pojoName}.class,id);
+        ${pojoName} bean = new  ${pojoName}();
+        bean.setId(id);
+        //bean.releaseStatus = releaseStatus;
+        ${firstLowPojoName}DAO.updateSelective(bean,"releaseStatus");
+        super.afterModify(old,bean);
+        }
+
+
+
+
+        /**
+        * ${pojoComment}分类物理删除
+        */
+        @Transactional(rollbackFor = Exception.class)
+        public void removeCategory(long categoryId) {
+        //子分类
+        int subCategoryCount = ${firstLowPojoName}DAO.selectCount(${pojoName}Category.class, oBuilder().eq(${pojoName}Category::getPid, categoryId));
+        if (subCategoryCount > 0) {
+        throw new BizException(MsgEnum.SYSTEM_ERROR.code, "有子分类存在,请先删除子分类再操作");
+        }
+        int typeCount = ${firstLowPojoName}DAO.selectCount(${pojoName}.class,oBuilder().eq(${pojoName}.class::getCategoryId,categoryId));
+        if(typeCount>0){
+        throw new BizException(MsgEnum.SYSTEM_ERROR.code,"此分类下有${pojoComment},请解除分类关系或删除后再删除");
+        }
+        ${firstLowPojoName}DAO.delete(${pojoName}Category.class,categoryId);
+
+        super.afterRemove(categoryId);
+        }
+    #end
+    }

+ 13 - 0
car-wash-admin/src/main/resources/tmpl/dao.vm

@@ -0,0 +1,13 @@
+package ${basePackage}.entity.po;
+
+import com.png.dao.DbHandler;
+import org.springframework.stereotype.Component;
+
+/**
+*${pojoComment}
+*@date ${datetime}
+*/
+@Component
+public class ${pojoName}DAO extends DbHandler {
+
+}

+ 153 - 0
car-wash-admin/src/main/resources/tmpl/dialog.vm

@@ -0,0 +1,153 @@
+<style scoped lang="scss">
+
+</style>
+<template>
+    <div class="system-dialog-container">
+        <el-dialog
+                :title="state.dialog.title"
+                v-model="state.dialog.visible"
+                width="820px"
+                draggable
+                destroy-on-close
+                :close-on-click-modal="false"
+                align-center>
+            <el-form
+                    inline
+                    :model="state.ruleForm"
+                    :rules="rules"
+                    label-position="top"
+                    ref="formRef"
+                    size="default"
+                    label-width="100px"
+                    class="mt5">
+                #foreach($ele in $fields)
+                    #if($ele.columnName!="id"&&$ele.columnName!="createTime"&&$ele.columnName!="updateTime" )
+                        <el-form-item label="$ele.columnComment" prop="$ele.columnName">
+                            <el-input
+                                    v-model="state.ruleForm.$ele.columnName"
+                                    placeholder="$ele.columnComment"
+                                    clearable
+                                    class="wd350">
+                            </el-input>
+                        </el-form-item>
+                    #end
+                #end
+            </el-form>
+
+            <template #footer>
+
+                <el-row>
+                    <el-col :span="12">
+                        <p class="text-align-left">
+                            <el-checkbox v-model="state.continueAdd" v-if="!state.ruleForm.id">继续创建</el-checkbox>
+                            <template v-else>
+                                <el-button @click="handleActive" type="success" v-if="state.ruleForm.status !=1" size="default">激 活</el-button>
+                                <el-button @click="handleActive" type="danger" v-else size="default">禁 用</el-button>
+                            </template>
+                        </p>
+                    </el-col>
+                    <el-col :span="12">
+                        <p class="dialog-footer">
+                            <el-button @click="onCancel" size="default">取 消</el-button>
+                            <el-button :loading="state.btnLoading" type="primary" @click="onSubmit" size="default">{{ state.dialog.submitTxt }}</el-button>
+                        </p>
+                    </el-col>
+                </el-row>
+
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup lang="ts" name="${pojoName}Dialog">
+    import {defineAsyncComponent, reactive, onMounted, ref} from 'vue';
+    import {Msg} from "/@/utils/message";
+    import {$body, $get} from "/@/utils/request";
+    import u from '/@/utils/u'
+
+    // 引入异步组件
+    // const ExtDetailForm = defineAsyncComponent(() => import('/@/components/form/ExtDetailForm.vue'));
+
+    // 定义子组件向父组件传值/事件
+    const emit = defineEmits(['refresh']);
+    const formRef = ref();
+    //定义初始变量,重置使用
+    const initState = () => ({
+        ruleForm: {
+            id: 0
+        },
+        btnLoading: false,
+        dialog: {
+            visible: false,
+            type: '',
+            title: '',
+            submitTxt: '',
+        },
+        rules: {},
+        continueAdd: false
+    })
+
+    // 定义变量内容
+    const state = reactive(initState());
+
+
+    // 打开弹窗
+    const open = (action: string = 'add', row: any) => {
+        state.dialog.title = u.dialog.actions[action].title + "『${pojoComment}』"
+        state.dialog.submitTxt = u.dialog.actions[action].btn + "『${pojoComment}』"
+        state.dialog.visible = true;
+        if (action !== 'add') {
+            loadData(row.id);
+        } else {
+            state.ruleForm = Object.assign(state.ruleForm, row);
+        }
+    };
+    // 关闭弹窗
+    const onClose = () => {
+        state.dialog.visible = false;
+        Object.assign(state, initState())
+    };
+    // 取消
+    const onCancel = () => {
+        onClose();
+    };
+    // 提交
+    const onSubmit = () => {
+        formRef.value.validate((v: boolean) => {
+            if (v) {
+                state.btnLoading = true;
+                const url = state.ruleForm.id > 0 ? "${firstLowPojoName}/modify" : "${firstLowPojoName}/add"
+                    $body(url, state.ruleForm).then(() => {
+                    state.btnLoading = false;
+                    Msg.message('操作成功');
+                    console.log('submit!')
+                    if (!state.continueAdd) {
+                        onClose();
+                        emit('refresh');
+                    }
+                })
+            } else {
+                state.btnLoading = false;
+                Msg.message('请先完整填写表单', 'error');
+            }
+        })
+    };
+
+    const handleFormChange = (formData: any) => {
+        console.log(formData)
+    }
+
+    // 初始化详情页
+    const loadData = (id: number) => {
+            $get(`${firstLowPojoName}/detail/${id}`).then((res: any) => {
+            state.ruleForm = res;
+        })
+    }
+
+    // 暴露变量
+    defineExpose({
+        open
+    });
+
+
+</script>

+ 171 - 0
car-wash-admin/src/main/resources/tmpl/drawer.vm

@@ -0,0 +1,171 @@
+<style scoped lang="scss">
+
+</style>
+<template>
+    <div class="system-dialog-container">
+        <el-drawer
+                :title="state.dialog.title"
+                v-model="state.dialog.visible"
+                width="820px"
+                append-to-body
+                destroy-on-close
+                :close-on-click-modal="false"
+        >
+            <el-form
+                    inline
+                    :model="state.ruleForm"
+                    :rules="rules"
+                    label-position="top"
+                    ref="formRef"
+                    size="default"
+                    label-width="100px"
+                    class="mt5">
+                #foreach($ele in $fields)
+                    #if($ele.columnName!="id"&&$ele.columnName!="createTime"&&$ele.columnName!="updateTime" )
+                        <el-form-item label="$ele.columnComment" prop="$ele.columnName">
+                            <el-input
+                                    v-model="state.ruleForm.$ele.columnName"
+                                    placeholder="$ele.columnComment"
+                                    clearable
+                                    class="wd350">
+                            </el-input>
+                        </el-form-item>
+                    #end
+                #end
+            </el-form>
+
+            <template #footer>
+				<span class="dialog-footer">
+					<el-button @click="onCancel" size="default">取 消</el-button>
+					<el-button :loading="state.btnLoading" type="primary" @click="onSubmit" size="default">{{ state.dialog.submitTxt }}</el-button>
+				</span>
+            </template>
+        </el-drawer>
+    </div>
+</template>
+
+<script setup lang="ts" name="${pojoName}Dialog">
+    import {defineAsyncComponent, reactive, onMounted, ref} from 'vue';
+    import {Msg} from "/@/utils/message";
+    import {$body, $get} from "/@/utils/request";
+    import u from '/@/utils/u'
+
+    // 引入组件
+    // const ExtDetailForm = defineAsyncComponent(() => import('/@/components/form/ExtDetailForm.vue'));
+
+    // 定义子组件向父组件传值/事件
+    const emit = defineEmits(['refresh']);
+    const formRef = ref();
+    //定义初始变量,重置使用
+    const initState = ()=>({
+        ruleForm: {
+            id:0
+        },
+        btnLoading: false,
+        dialog: {
+            visible: false,
+            type: '',
+            title: '',
+            submitTxt: '',
+        },
+        columns: [
+            #foreach($ele in $fields)
+                #if($ele.dataType=="int" || $ele.dataType=="tinyint" )
+                    #if($ele.columnName =="status" ||$ele.columnName =="releaseStatus" )
+                        {
+                            label: '$ele.columnComment',
+                            prop: '$ele.columnName',
+                            sortable: 'custom',
+                            align: 'center',
+                            type: 'dict',
+                            conf: {dict: '${pojoName}.status'}
+                        },
+                    #end
+                    #if($ele.columnName!="createBy"&&$ele.columnName!="updateBy")
+                        {label: '$ele.columnComment', prop: '$ele.columnName', type: 'user',},
+                    #end
+                #end
+                #if($ele.dataType=="json")
+                    {label: '$ele.columnComment', prop: '$ele.columnName', type: 'user'},
+                #end
+                #if($ele.dataType=="char" || $ele.dataType=="varchar" )
+                    #if($ele.columnName =="title"||$ele.columnName=="name")
+                        {
+                            label: '$ele.columnComment', prop: '$ele.columnName', type: "text",
+                        },
+                    #end
+                    #if($ele.columnName !="title"&&$ele.columnName!="name")
+                        {label: '$ele.columnComment', prop: '$ele.columnName', type: 'text',},
+                    #end
+                #end
+                #if($ele.dataType=="date" || $ele.dataType=="datetime" || $ele.dataType=="timestamp" )
+                    {
+                        label: '$ele.columnComment', prop: '$ele.columnName', type: 'datetime',
+                    },
+                #end
+            #end
+        ],
+        rules: {},
+    })
+
+    // 定义变量内容
+    const state = reactive(initState());
+
+
+    // 打开弹窗
+    const open = (action: string='add', row: any) => {
+        state.dialog.title = u.dialog.actions[action].title +"『${pojoComment}』"
+        state.dialog.submitTxt = u.dialog.actions[action].btn +"『${pojoComment}』"
+        state.dialog.visible = true;
+        if (action !=='add') {
+            loadData(row.id);
+        }
+    };
+    // 关闭弹窗
+    const onClose = () => {
+        state.dialog.visible = false;
+        Object.assign(state,initState())
+    };
+    // 取消
+    const onCancel = () => {
+        onClose();
+    };
+    // 提交
+    const onSubmit = () => {
+        formRef.value.validate((valid:boolean) => {
+            if(valid){
+                state.btnLoading = true;
+                const url = state.ruleForm.id > 0 ? "${firstLowPojoName}/modify" : "${firstLowPojoName}/add"
+                    $body(url, state.ruleForm).then(() => {
+                    state.btnLoading = false;
+                    Msg.message('操作成功');
+                    console.log('submit!')
+                    onClose();
+                    emit('refresh');
+                })
+            }
+
+        }).catch(() => {
+            state.btnLoading = false;
+            Msg.message('表单校验失败', 'error');
+        })
+    };
+
+    const handleFormChange = (formData: any) => {
+        console.log(formData)
+    }
+
+    // 初始化表格数据
+    const loadData = (id: number) => {
+            $get(`${firstLowPojoName}/detail/${id}`).then((res: any) => {
+            state.ruleForm = res;
+        })
+    }
+
+    // 暴露变量
+    defineExpose({
+        open
+    });
+
+
+</script>

+ 262 - 0
car-wash-admin/src/main/resources/tmpl/listvue.vm

@@ -0,0 +1,262 @@
+<style scoped lang="scss">
+    .system-container {
+
+        :deep(.el-card__body) {
+            display: flex;
+            flex-direction: column;
+            justify-content: space-between;
+            flex: 1;
+            overflow: auto;
+
+            .el-table {
+                flex: 1;
+            }
+
+        }
+    }
+
+    .page-content {
+        margin-bottom: 20px;
+    }
+
+    .page-pager {
+        background-color: #fff;
+        height: 24px;
+    }
+</style>
+<template>
+    <div class="system-container layout-padding">
+        <el-card shadow="hover" class="layout-padding-auto">
+            <ext-query-form
+                    class="page-search"
+                    ref="queryRef"
+                    v-model="state.formQuery"
+                    :columns="state.columns"
+                    :import-config="state.importConfig"
+                    :export-config="state.exportConfig"
+                    @on-change="loadData(true)"
+                    @imported="loadData(true)">
+                <!--  <template #extraQuery></template>
+                  <template #extraLeft></template>
+                  <template #extraRight></template>-->
+
+                <template #extraLeft>
+                    <ext-button name="创建"
+                                auth="'${firstLowPojoName}.add'" icon="ele-FolderAdd"
+                                size="default" type="success"
+                                class="ml10" @click="onRowClick('add',null)"/>
+                </template>
+            </ext-query-form>
+
+            <ext-table
+                    class="page-content"
+                    :height="state.tableData.height"
+                    :data-list="state.tableData.data"
+                    :columns="state.columns"
+                    :border="true"
+                    :loading="state.tableData.loading">
+            </ext-table>
+
+            ##            <el-affix position="bottom" :offset="48">
+            <ext-page class="page-pager" v-model="state.pageQuery" @change="loadData(false)"/>
+            ##            </el-affix>
+        </el-card>
+    </div>
+    <${pojoName}Dialog ref="${firstLowPojoName}DialogRef" @refresh="loadData(true)"/>
+</template>
+
+<script setup lang="ts" name="${pojoName}List">
+    import {defineAsyncComponent, reactive, onMounted, onBeforeMount, ref, getCurrentInstance,nextTick,onBeforeUnmount} from 'vue';
+    import {$body,$get} from "/@/utils/request";
+    import u from '/@/utils/u'
+    import {Msg} from "/@/utils/message";
+    import {Session} from "/@/utils/storage";
+
+    const {proxy}: any = getCurrentInstance();
+
+    import ExtPage from '/@/components/form/ExtPage.vue'
+    import ExtQueryForm from "/@/components/form/ExtAdminQueryForm.vue";
+    import ExtTable from "/@/components/form/ExtAdminTable.vue";
+
+    import mittBus from '/@/utils/mitt';
+
+    import {ElButton} from 'element-plus'
+
+
+    const ${pojoName}Dialog = defineAsyncComponent(() => import("/@/views/page/${pojoName}Dialog.vue"));
+
+    //定义引用
+    const queryRef = ref();
+    const ${firstLowPojoName}DialogRef = ref();
+
+    //定义变量
+    const state = reactive({
+        formQuery: {},
+        pageQuery: {
+            pageIndex: 1,
+            pageSize: 10,
+            total: 0
+        },
+        tableData: {
+            height:500,
+            data: [] as Array < any >,
+            loading: false
+        },
+        importConfig: {},
+        exportConfig: {},
+        columns: [
+            {type: 'selection', width: 60, align: 'center', fixed: 'left'},
+            #foreach($ele in $fields)
+                #if($ele.dataType=="int" || $ele.dataType=="tinyint" )
+                    #if($ele.columnName =="status" ||$ele.columnName =="releaseStatus" )
+                        {label: '$ele.columnComment', prop: '$ele.columnName', sortable: 'custom', align: 'center', query: true, type: 'dict', cfg: {dict: '${pojoName}.status'}},
+                    #else
+                        {label: '$ele.columnComment', prop: '$ele.columnName', query: true, type: '', resizable: true},
+                    #end
+                #end
+                #if($ele.dataType=="json")
+                    {label: '$ele.columnComment', prop: '$ele.columnName', query: true, type: '', resizable: true},
+                #end
+                #if($ele.dataType=="char" || $ele.dataType=="varchar" )
+                    #if($ele.columnName =="title"||$ele.columnName=="name")
+                        {
+                            label: '$ele.columnComment', prop: '$ele.columnName', query: true, type: "text", resizable: true,
+                            render: (h: any, row: any) => {
+                                return h('div', null, [
+                                    h('div', {
+                                        style:{
+                                            cursor:'pointer',
+                                            color:'var(--el-color-primary-light-1)'
+                                        },
+                                        onClick: () => {
+                                            onRowClick('view', row)
+                                        }
+                                    }, row.name||row.title)])
+                            }
+                        },
+                    #end
+                    #if($ele.columnName !="title"&&$ele.columnName!="name")
+                        {label: '$ele.columnComment', prop: '$ele.columnName', query: true, type: 'text', resizable: true},
+                    #end
+                #end
+                #if($ele.dataType=="date" || $ele.dataType=="datetime" || $ele.dataType=="timestamp" )
+                    {label: '$ele.columnComment', prop: '$ele.columnName', query: true, sortable: 'custom', type: 'datetime', resizable: true, cfg: {format: (val: any) => u.fmt.fmtDate(val)}},
+                #end
+            #end
+            {                label: '操作', prop: 'action', type: 'render', width: 180, align: 'center', fixed: 'right',
+                render: (h: any, row: any) => {
+                    return (
+                            h('div', null, [
+                                proxy.$auth('${firstLowPojoName}.modify') ?
+                                        h(ElButton, {
+                                            type: 'warning',
+                                            text: true,
+                                            size: 'small',
+                                            onClick: () => {
+                                                onRowClick('edit', row)
+                                            }
+                                        }, () => '编辑') : '',
+                                proxy.$auth('${firstLowPojoName}.remove') ?
+                                        h(ElButton, {
+                                            type: 'danger',
+                                            text: true,
+                                            size: 'small',
+                                            onClick: () => {
+
+                                                onRowDel(row)
+                                            }
+                                        }, () => '删除') : '',
+                            ])
+                    )
+                }
+            }
+        ],
+    })
+
+        ## // 定义子组件向父组件传值/事件
+        ## const emit = defineEmits(['${pojoName}.edit']);
+
+    // 监听双向绑定 modelValue 的变化
+    // watch(
+    //         () => state.pageIndex,
+    //         () => {
+    //
+    //         }
+    // );
+
+    //生命周期钩子
+    onBeforeMount(() => {
+        let token = Session.get("token")
+        let encodeToken = encodeURIComponent(token)
+        let exportUrl = `poi/export?type=${firstLowPojoName}&X-Token=${encodeToken}`
+        //导入导出参数配置
+        state.importConfig = {
+            auths: ['${firstLowPojoName}.add'],
+            url: `poi/import?type=${firstLowPojoName}&X-Token=${encodeToken}`,
+            template: `${exportUrl}&isTemplate=true`
+        }
+
+        state.exportConfig = {url: exportUrl,}
+
+    })
+
+    onMounted(() => {
+        loadData();
+
+        nextTick(()=>{
+            let bodyHeight = document.body.clientHeight;
+            let queryHeight = queryRef.value.$el.clientHeight;
+            state.tableData.height = bodyHeight - queryHeight - 220
+        })
+
+        //  mittBus.on("${firstLowPojoName}.refresh",()=>{
+        //     loadData();
+        // })
+    });
+
+    onBeforeUnmount(()=>{
+        //   mittBus.off("${firstLowPojoName}.refresh")
+    })
+
+
+    //region 方法区
+    // 初始化表格数据
+    const loadData = (refresh: boolean = false) => {
+        if (refresh) {
+            state.pageQuery.pageIndex = 1;
+        }
+        state.tableData.loading = true;
+            $body(`/${firstLowPojoName}/list`, {...state.formQuery, ...state.pageQuery}).then((res: any) => {
+            let {list, count} = res;
+            state.tableData.data = list;
+            state.pageQuery.total = count;
+            state.tableData.loading = false;
+        }).catch(e => {
+            console.error(e)
+            state.tableData.loading = false;
+        })
+    };
+
+    // 打开弹窗
+    const onRowClick = (type: string, row: any) => {
+            ${firstLowPojoName}DialogRef.value.open(type, row);
+    };
+    // 删除
+    const onRowDel = (row: any) => {
+        Msg.confirm(`此操作将永久删除:『${row.name}』,是否继续?`).then(() => {
+                $get(`/${firstLowPojoName}/delete/${row.id}`).then(() => {
+                Msg.message("删除成功", 'success')
+            }).catch(() => {
+                Msg.message("删除失败", 'error')
+            })
+        });
+    };
+
+    //endregion
+
+
+    // 暴露变量
+    // defineExpose({
+    //     loadData,
+    // });
+</script>

+ 134 - 0
car-wash-admin/src/main/resources/tmpl/pojo.vm

@@ -0,0 +1,134 @@
+package ${basePackage}.entity.po;
+
+import com.png.jdbc.BasicQuery;
+import com.png.jdbc.annotations.*;
+import com.png.jdbc.BasicEntity;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+*${pojoComment}
+*@date ${datetime}
+*/
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Entity(clz = ${pojoName}.class,comment ="${pojoComment}")
+public class ${pojoName} implements Serializable{
+#foreach($ele in $fields)
+    #if($ele.dataType=="int")
+    @DBF(comment = "${ele.columnComment}")
+    private int $ele.columnName;
+    #end
+    #if($ele.dataType=="tinyint"||$ele.dataType=="bool"||$ele.dataType=='int2')
+    @DBF(comment = "${ele.columnComment}")
+    private boolean $ele.columnName ;
+    #end
+    #if($ele.dataType=="bigint")
+    @DBF(comment = "${ele.columnComment}")
+    private long $ele.columnName ;
+    #end
+    #if($ele.dataType=="text")
+    @DBF(comment = "${ele.columnComment}")
+        #if($ele.columnName.indexOf("IdList")!=-1)
+        private List<Long> $ele.columnName ;
+        #else
+            private List<SimpleVo> $ele.columnName ;
+        #end
+    #end
+    #if($ele.dataType=="varchar")
+        @DBF(comment = "${ele.columnComment}",max=$ele.dataLength)
+        private String $ele.columnName ;
+    #end
+    #if($ele.dataType=="text")
+        @DBF(comment = "${ele.columnComment}")
+        private String $ele.columnName ;
+    #end
+    #if($ele.dataType=="char")
+        @DBF(comment = "${ele.columnComment}",max=$ele.dataLength)
+        private String $ele.columnName ;
+    #end
+    #if($ele.dataType=="date")
+        @DBF(comment = "${ele.columnComment}")
+        private Date $ele.columnName;
+    #end
+    #if($ele.dataType=="datetime")
+        @DBF(comment = "${ele.columnComment}")
+        private Date $ele.columnName;
+    #end
+    #if($ele.dataType=="timestamp")
+        @DBF(comment = "${ele.columnComment}")
+        private Date $ele.columnName;
+    #end
+#end
+
+    @Data
+    @EqualsAndHashCode(callSuper = true)
+    public static class ${pojoName}Info extends ${pojoName}{
+    // @One(mkf = "createBy", tf = "name", comment = "创建人")
+    // private String createName;
+
+    // @One(mkf = "updateBy", tf = "name", comment = "更新人")
+    // private String updateName;
+    }
+
+    @Data
+    @EqualsAndHashCode(callSuper = true)
+    @QE(clz=${pojoName}Info.class)
+    public static class ${pojoName}BasicQuery extends BasicQuery{
+#foreach($ele in $fields)
+    #if($ele.dataType=="int")
+        private Integer ${ele.columnName} ;
+    #end
+    #if($ele.dataType=="tinyint")
+        private Boolean $ele.columnName ;
+    #end
+    #if($ele.dataType=="bigint")
+        private Long $ele.columnName ;
+    #end
+    #if($ele.dataType=="varchar")
+        private String $ele.columnName ;
+    #end
+    #if($ele.dataType=="text")
+        private String $ele.columnName ;
+    #end
+    #if($ele.dataType=="char")
+        private String $ele.columnName ;
+    #end
+#end
+
+    //region date query
+#foreach($ele in $fields)
+    #if($ele.dataType=="date")
+        @QF(op=OP.GTE,tf="${ele.columnName}")
+        private Date ${ele.columnName}Start  ;
+        @QF(op=OP.LTE,tf="${ele.columnName}")
+        private Date ${ele.columnName}End    ;
+    #end
+    #if($ele.dataType=="datetime")
+        @QF(op=OP.GTE,tf="${ele.columnName}")
+        private Date ${ele.columnName}Start;
+        @QF(op=OP.LTE,tf="${ele.columnName}")
+        private Date ${ele.columnName}End    ;
+    #end
+    #if($ele.dataType=="timestamp")
+        @QF(op=OP.GTE,tf="${ele.columnName}")
+        private Date ${ele.columnName}Start  ;
+        @QF(op=OP.LTE,tf="${ele.columnName}")
+        private Date ${ele.columnName}End    ;
+    #end
+#end
+    //endregion
+
+
+    //region sort query
+#foreach($ele in $fields)
+    #if($ele.dataType=="int"||$ele.dataType=="bigint"||$ele.dataType=="tinyint"||$ele.dataType=="datetime"||$ele.dataType=="timestamp")
+        private Integer ${ele.columnName}Sort    ;
+    #end
+#end
+    //endregion
+    }
+    }

+ 114 - 0
car-wash-common/src/main/java/com/kym/common/ContextHelper.java

@@ -0,0 +1,114 @@
+package com.kym.common;
+
+
+import com.kym.common.utils.CommUtil;
+
+
+/**
+ * 线程变量管理类
+ *
+ * @author yaopeng
+ */
+public class ContextHelper {
+
+    private static ThreadLocal<String> TID = new ThreadLocal<>();
+    private static ThreadLocal<String> TOKEN = new ThreadLocal<>();
+    private static ThreadLocal<IUser> USER = new ThreadLocal<>();
+    private static ThreadLocal<String> IP = new ThreadLocal<>();
+    private static ThreadLocal<Integer> CLIENT = new ThreadLocal<>();
+    private static ThreadLocal<Long> ASSOID = new ThreadLocal<>();
+    private static ThreadLocal<String> REMARK = new ThreadLocal<>();
+    private static ThreadLocal<Long> ORGID = new ThreadLocal<>();
+
+
+    public static String getTid() {
+        return TID.get();
+    }
+
+    public static void setTid(String tid) {
+        TID.set(tid);
+    }
+
+    public static long getOid() {
+        return CommUtil.null2Long(ORGID.get());
+    }
+
+    public static void setOid(Long orgId) {
+        ORGID.set(orgId);
+    }
+
+    public static void setToken(String token) {
+        TOKEN.set(token);
+    }
+
+    public static IUser getUser() {
+        return USER.get();
+    }
+
+    public static void setUser(IUser user) {
+        USER.set(user);
+    }
+
+    public static void clear() {
+        TID.remove();
+        USER.remove();
+        IP.remove();
+        CLIENT.remove();
+        TOKEN.remove();
+        ASSOID.remove();
+        REMARK.remove();
+        ORGID.remove();
+    }
+
+    public static String getIp() {
+        return IP.get();
+    }
+
+    public static void setIp(String ip) {
+        IP.set(ip);
+    }
+
+    public static String getToken() {
+        return TOKEN.get();
+    }
+
+
+    public static void setClient(int client) {
+        CLIENT.set(client);
+    }
+
+    /**
+     * PLATFORM_ALL = 0;<br>
+     * PLATFORM_CMS = 1;<br>
+     * PLATFORM_PORTAL = 3;<br>
+     * PLATFORM_WX_PB = 4;<br>
+     * PLATFORM_APP = 5;<br>
+     * PLATFORM_WX_MINI = 2;<br>
+     * PLATFORM_BD_MINI = 6;<br>
+     * PLATFORM_ZFB_MINI = 7;<br>
+     * PLATFORM_TT_MINI = 8;<br>
+     * PLATFORM_QQ_MINI = 9;
+     * <br>分终端进行权限验证
+     */
+    public static int getClient() {
+        return null == CLIENT.get() ? -1 : CLIENT.get();
+    }
+
+
+    public static void setAssoId(long id) {
+        ASSOID.set(id);
+    }
+
+    public static Long getAssoId() {
+        return null == ASSOID.get() ? 0L : ASSOID.get();
+    }
+
+    public static void setRemark(String remark) {
+        REMARK.set(remark);
+    }
+
+    public static String getRemark() {
+        return REMARK.get();
+    }
+
+}

+ 32 - 0
car-wash-common/src/main/java/com/kym/common/IUser.java

@@ -0,0 +1,32 @@
+package com.kym.common;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 公共运营人员实体类
+ * @author yaop at 2019/6/2 14:20
+ */
+public class IUser implements Serializable {
+    public long id;
+    public long companyId;
+    public int boss;
+    public String name;
+    public String nickName;
+    public String email;
+    public String mobile;
+    public String avatar;
+    public String ip;
+    public String lastIp;
+    public Date lastLoginAt;
+    public int status;
+    public long createBy;
+    public long updateBy;
+    public Date createAt;
+    public Date updateAt;
+    public List<Long> roleIdList  = new ArrayList<>();
+    public List<Long> deptIdList = new ArrayList<>();
+    public List<Long> postIdList = new ArrayList<>();
+}

+ 0 - 8
car-wash-common/src/main/java/com/kym/common/filter/RequestLogFilter.java

@@ -17,14 +17,6 @@ import org.slf4j.MDC;
 import java.io.IOException;
 
 
-
-
-
-
-
-
-
-
 /**
  * 权限过滤器
  *

+ 8 - 0
car-wash-entity/pom.xml

@@ -18,6 +18,14 @@
     </properties>
 
     <dependencies>
+
+        <dependency>
+            <groupId>com.kym</groupId>
+            <artifactId>car-wash-jdbc</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+
+
         <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>

+ 54 - 0
car-wash-jdbc/pom.xml

@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <parent>
+        <groupId>com.kym</groupId>
+        <artifactId>car-wash</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>car-wash-jdbc</artifactId>
+    <modelVersion>4.0.0</modelVersion>
+    <version>0.0.1-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <properties>
+        <maven.compiler.source>17</maven.compiler.source>
+        <maven.compiler.target>17</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.validation</groupId>
+            <artifactId>jakarta.validation-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>mysql-connector-j</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-jdbc</artifactId>
+        </dependency>
+
+
+        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
+
+    </dependencies>
+</project>

+ 473 - 0
car-wash-jdbc/src/main/java/com/kym/DbUtil.java

@@ -0,0 +1,473 @@
+package com.kym;
+
+import com.kym.jdbc.annotations.DBF;
+import com.kym.jdbc.annotations.Entity;
+import com.kym.jdbc.annotations.Many;
+import com.kym.jdbc.annotations.One;
+import com.kym.jdbc.annotations.QE;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 描述:基础工具类 refelct、empty check
+ * create at 2020/5/7 23:01
+ *
+ * @author yaop
+ */
+public class DbUtil {
+    private static final Logger logger = LoggerFactory.getLogger(DbUtil.class);
+
+    private DbUtil() {
+    }
+
+
+    public static void asserts(boolean express, String errorMsg) {
+        if (!express) {
+            throw new RuntimeException(errorMsg);
+        }
+    }
+
+    public static int null2Int(Object val) {
+        if (null == val) {
+            return 0;
+        }
+        try {
+            return Integer.parseInt(val.toString());
+        } catch (Exception e) {
+            // do nothings
+        }
+        return 0;
+    }
+
+    public static boolean isGenericType(Type type) {
+        Class<?> t =  (Class) type;
+        return  t.isAssignableFrom(String.class)
+                ||t.isAssignableFrom(byte.class)
+                ||t.isAssignableFrom(Byte.class)
+                ||t.isAssignableFrom(boolean.class)
+                ||t.isAssignableFrom(Boolean.class)
+                ||t.isAssignableFrom(int.class)
+                ||t.isAssignableFrom(Integer.class)
+                ||t.isAssignableFrom(short.class)
+                ||t.isAssignableFrom(Short.class)
+                ||t.isAssignableFrom(long.class)
+                ||t.isAssignableFrom(Long.class)
+                ||t.isAssignableFrom(float.class)
+                ||t.isAssignableFrom(Float.class)
+                ||t.isAssignableFrom(double.class)
+                ||t.isAssignableFrom(Double.class)
+                ||t.isAssignableFrom(char.class)
+                ||t.isAssignableFrom(Character.class);
+//        return type == String.class || type == Integer.TYPE || type == Long.TYPE || type == Short.TYPE || type == Byte.TYPE
+//                || type == Boolean.TYPE || type == Character.TYPE || type == Float.TYPE || type == Double.TYPE;
+    }
+
+    public static boolean isGenericType(Object obj) {
+        if (obj instanceof Integer) {
+            return true;
+        } else if (obj instanceof Short) {
+            return true;
+        } else if (obj instanceof Float) {
+            return true;
+        } else if (obj instanceof Double) {
+            return true;
+        } else if (obj instanceof Byte) {
+            return true;
+        } else if (obj instanceof Boolean) {
+            return true;
+        } else if (obj instanceof Long) {
+            return true;
+        } else if (obj instanceof String) {
+            return true;
+        } else {
+            return obj instanceof Character;
+        }
+    }
+
+    /**
+     * 数据库持久化的字段
+     */
+    public static boolean isUsageField(Field f) {
+        if (f.isAnnotationPresent(DBF.class)) {
+            return true;
+        }
+        if (f.isAnnotationPresent(One.class) && !f.getAnnotation(One.class).noQuery()) {
+            return true;
+        }
+        return f.isAnnotationPresent(Many.class) && !f.getAnnotation(Many.class).noQuery();
+    }
+
+    public static boolean isStaticFinalField(Field f) {
+        int modifiers = f.getModifiers();
+        return Modifier.isFinal(modifiers) || Modifier.isStatic(modifiers);
+    }
+
+    public static String firstToLowerCase(String str) {
+        return str.substring(0, 1).toLowerCase() + str.substring(1);
+    }
+
+    public static String getColumnName(String field) {
+        StringBuilder sbr = new StringBuilder();
+        char[] chars = field.toCharArray();
+        for (char ch : chars) {
+            if (Character.isUpperCase(ch)) {
+                sbr.append("_").append(Character.toLowerCase(ch));
+            } else {
+                sbr.append(ch);
+            }
+        }
+        return sbr.toString();
+    }
+
+    public static Object getFieldValue(Object o, Field field) {
+
+        field.setAccessible(true);
+        Object val = null;
+        try {
+            val = field.get(o);
+        } catch (IllegalAccessException e) {
+            logger.error("SQLHelper ERR# Object getFieldValue,fileName:{}", field.getName());
+        }
+        field.setAccessible(false);
+        return val;
+    }
+
+    public static Object getFieldValue(Object bean, String fieldName) {
+        Object defValu = "";
+        try {
+            fieldName = getCamelName(fieldName);
+            Field field = null;
+            try {
+                field = bean.getClass().getDeclaredField(fieldName);
+            } catch (Exception e) {
+                try {
+                    field = bean.getClass().getField(fieldName);
+                } catch (Exception e1) {
+                    try {
+                        field = bean.getClass().getSuperclass().getDeclaredField(fieldName);
+                    } catch (Exception e2) {
+                        field = bean.getClass().getSuperclass().getField(fieldName);
+                    }
+                }
+            }
+
+            field.setAccessible(true);
+            defValu = field.get(bean);
+            if (null == defValu) {
+                Class<?> type = field.getType();
+                if (type == String.class) {
+                    defValu = "";
+                } else if (type == int.class || type == Integer.class) {
+                    defValu = 0;
+                } else if (type == double.class || type == Double.class) {
+                    defValu = 0d;
+                } else if (type == long.class || type == Long.class) {
+                    defValu = 0L;
+                } else if (type == float.class || type == Float.class) {
+                    defValu = 0F;
+                } else if (type == short.class || type == Short.class) {
+                    defValu = 0;
+                } else if (type == byte.class || type == Byte.class) {
+                    defValu = 0;
+                } else if (type == boolean.class || type == Boolean.class) {
+                    defValu = 0;
+                } else if (type == char.class || type == Character.class) {
+                    defValu = '0';
+                } else if (field.getType().isArray()) {
+                    defValu = new Object[]{};
+                }
+            }
+            field.setAccessible(false);
+        } catch (Exception e) {
+            logger.warn("SQLHandler ERR# getFieldValue ,field:{}, bean:{} ,msg:{}", fieldName, bean.getClass().getSimpleName(), e.getMessage());
+        }
+        return defValu;
+    }
+
+    public static String getCamelName(String fieldName) {
+        StringBuilder sbr = new StringBuilder();
+        char[] chars = fieldName.toCharArray();
+        boolean preUpper = false;
+        for (char ch : chars) {
+            if ('_' == ch) {
+                preUpper = true;
+            } else {
+                if (preUpper) {
+                    sbr.append(Character.toUpperCase(ch));
+                    preUpper = false;
+                } else {
+                    sbr.append(ch);
+                }
+            }
+        }
+        return sbr.toString();
+    }
+
+    public static void setFieldValue(Object o, Field field, Object val) {
+        try {
+            field.setAccessible(true);
+            field.set(o, val);
+            field.setAccessible(false);
+        } catch (IllegalAccessException e) {
+            logger.error("SQLHelper ERR# Object setFieldValue ,fileName:{}", field.getName());
+        }
+    }
+
+
+    /**
+     * 判断集合是否为空
+     */
+    public static boolean isEmptyOrNull(Collection<?> collection) {
+        return null == collection || collection.isEmpty();
+    }
+
+
+    /**
+     * 判断数组是否为空
+     */
+    public static boolean isEmptyOrNull(Object[] array) {
+        return null == array || array.length == 0 || null == array[0];
+    }
+
+    /**
+     * 判断字符串是否为空
+     */
+    public static boolean isEmptyOrNull(String value) {
+
+        return null == value || value.length() == 0;
+    }
+
+    public static boolean isEmptyOrNull(Object value) {
+        if (null == value) {
+            return true;
+        }
+        if (value.getClass().isArray()) {
+            return Array.getLength(value) == 0;
+        } else if (value instanceof Collection) {
+            return ((Collection<?>) value).isEmpty();
+        } else if (value instanceof Map) {
+            return ((Map<?, ?>) value).isEmpty();
+        } else {
+            return isEmptyOrNull(value.toString());
+        }
+    }
+
+    public final static String regex = "(create\\s+(database|table|index)|truncate|drop\\s+(table|index|database)|alter\\s+(table|database)|update[\\s\\S]*set|insert[\\s\\S]*into|delete\\s+from|net user|xp_cmdshell|#)+\\s+";
+/*
+    public final static String regex = "'|and|exec|execute|insert|select|delete|update|count|drop|\\*|%|chr|mid|master|truncate|" +
+            "char|declare|sitename|net user|xp_cmdshell|;|or|-|\\+|,|like'|and|exec|execute|insert|create|drop|" +
+            "table|from|grant|use|group_concat|column_name|" +
+            "information_schema.columns|table_schema|union|where|select|delete|update|order|by|count|\\*|" +
+            "chr|mid|master|truncate|char|declare|or|;|-|--|\\+|,|like|//|/|%|#";
+*/
+
+    /**
+     * 把SQL关键字追加$字符串
+     */
+    public static String injectDefend(String param) {
+        if (param == null) {
+            return null;
+        }
+        // (?i)不区分大小写替换
+        return param.replaceAll("(?i)" + regex, "$0.");
+    }
+
+    /**
+     * 返回经过防注入处理的字符串
+     */
+    public static String fitlerSql(String sql) {
+        return injectDefend(sql);
+    }
+
+    /**
+     * 设置参数
+     *
+     * @param ps
+     * @param parameters
+     */
+    public static void setParameter(PreparedStatement ps, Object... parameters) throws SQLException {
+        if (parameters == null || parameters.length == 0) {
+            return;
+        }
+        int i = 1;
+        for (Object o : parameters) {
+            if (o == null) {
+                ps.setString(i++, null);
+                continue;
+            }
+            if (o instanceof String) {
+                ps.setString(i++, ((String) o));
+                continue;
+            }
+            if (o instanceof Date date) {
+                ps.setTimestamp(i++, new Timestamp(date.getTime()));
+                continue;
+            }
+            // Integer
+            if (o instanceof Integer) {
+                ps.setInt(i++, ((Integer) o));
+                continue;
+            }
+            if (o instanceof Double) {
+                ps.setDouble(i++, ((Double) o));
+                continue;
+            }
+            if (o instanceof Float) {
+                ps.setFloat(i++, ((Float) o));
+                continue;
+            }
+            if (o instanceof BigDecimal) {
+                ps.setBigDecimal(i++, ((BigDecimal) o));
+                continue;
+            }
+            if (o instanceof Long) {
+                ps.setLong(i++, ((Long) o));
+                continue;
+            }
+            if (o instanceof Short) {
+                ps.setShort(i++, ((Short) o));
+                continue;
+            }
+            if (o instanceof Byte) {
+                ps.setByte(i++, ((Byte) o));
+                continue;
+            }
+            if (o instanceof byte[]) {
+                ps.setBytes(i++, ((byte[]) o));
+                continue;
+            }
+            if (o instanceof Boolean) {
+                ps.setBoolean(i++, ((Boolean) o));
+                continue;
+            }
+
+            if (o.getClass().isArray()) {
+                ps.setString(i++, JacksonUtil.toJSONString(o));
+                continue;
+            }
+
+            if (List.class.isAssignableFrom(o.getClass())) {
+                ps.setString(i++, JacksonUtil.toJSONString(o));
+                continue;
+            }
+
+            if (Set.class.isAssignableFrom(o.getClass())) {
+                ps.setString(i++, JacksonUtil.toJSONString(o));
+                continue;
+            }
+
+            if (Map.class.isAssignableFrom(o.getClass())) {
+                ps.setString(i++, JacksonUtil.toJSONString(o));
+                continue;
+            }
+
+            if (isGenericType(o.getClass())) {
+                ps.setString(i++, JacksonUtil.toJSONString(o));
+                continue;
+            }
+
+            logger.info("SetParameter PreparedStatement SQL  type:{},value:{}", o.getClass().getName(), JacksonUtil.toJSONString(o));
+//            throw new IllegalArgumentException("unsupported type:" + o.getClass());
+            ps.setString(i++,JacksonUtil.toJSONString(o));
+        }
+    }
+
+
+    private static void recursiveFields(Class<?> clz, Set<Field> fieldSet) {
+        if(null==clz){
+            return;
+        }
+        Field[] fields = clz.getDeclaredFields();
+        if (!isEmptyOrNull(fields)) {
+            for (Field f : fields) {
+                if (isUsageField(f)) {
+                    fieldSet.add(f);
+                }
+            }
+        }
+        Entity annotation = clz.getDeclaredAnnotation(Entity.class);
+        if (null == annotation) {
+            recursiveFields(clz.getSuperclass(), fieldSet);
+        }
+    }
+
+    public static Field getField(Object bean, String fieldName) throws NoSuchFieldException {
+        Field field = null;
+        int loop = 0;
+        Class<?> clz = bean.getClass();
+        while (loop < 10) {
+            try {
+                loop++;
+                return clz.getDeclaredField(fieldName);
+            } catch (Exception e) {
+                clz = clz.getSuperclass();
+            }
+            if(null==clz){
+                throw new NoSuchFieldException(fieldName);
+            }
+        }
+        throw new NoSuchFieldException(fieldName);
+    }
+
+    public static Set<Field> getEntityFields(Class<?> clz) {
+        Set<Field> fields = new HashSet<>();
+        recursiveFields(clz, fields);
+        return fields;
+    }
+
+
+    public static Set<Field> getQueryFields(Class<?> clz) {
+        Set<Field> fields = new HashSet<>();
+        if (clz.getName().equals("BasicQuery")) {
+            return new HashSet<>(Arrays.asList(clz.getFields()));
+        }
+        QE annotation = clz.getDeclaredAnnotation(QE.class);
+        if (null != annotation) {
+            Field[] fields1 = clz.getFields();
+            if (!isEmptyOrNull(fields1)) {
+                for (Field f : fields1) {
+                    if (!isStaticFinalField(f)) {
+                        fields.add(f);
+                    }
+                }
+            }
+            Field[] fields2 = clz.getDeclaredFields();
+            if (!isEmptyOrNull(fields2)) {
+                for (Field f : fields2) {
+                    if (!isStaticFinalField(f)) {
+                        fields.add(f);
+                    }
+                }
+            }
+        }
+        return fields;
+    }
+
+    public static void main(String[] args) {
+        System.out.println(fitlerSql("droptable t_state"));
+        System.out.println(fitlerSql("update t_state set "));
+        System.out.println(fitlerSql("drop table t_state"));
+        System.out.println(fitlerSql("DROP table t_state"));
+        System.out.println(fitlerSql("Drop Table t_state"));
+        System.out.println(fitlerSql("alter table t_state"));
+        System.out.println(fitlerSql("delete from  table t_state"));
+        System.out.println(fitlerSql(".lete from  -- table t_state"));
+        System.out.println(fitlerSql("truncate table t_state -- '' delete from t"));
+    }
+}

+ 175 - 0
car-wash-jdbc/src/main/java/com/kym/JacksonUtil.java

@@ -0,0 +1,175 @@
+package com.kym;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
+
+/**
+ * jackson工具类,替换fastjson漏洞太多频繁升级
+ *
+ * @author yaop
+ */
+public class JacksonUtil {
+
+    private static final Logger log = LoggerFactory.getLogger(JacksonUtil.class);
+
+    private static final ObjectMapper mapper;
+
+    private JacksonUtil() {
+    }
+
+    static {
+        mapper = new ObjectMapper();
+        // 如果json中有新增的字段并且是实体类类中不存在的,不报错
+        // mapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
+        // 如果存在未知属性,则忽略不报错
+        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+        // 允许key没有双引号
+        mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
+        // 允许key有单引号
+        mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
+        // 允许整数以0开头
+        mapper.configure(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS, true);
+        // 允许字符串中存在回车换行控制符
+        mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
+    }
+
+    public static String toJSONString(Object obj) {
+        return obj != null ? toJSONString(obj, () -> "") : "";
+    }
+
+    public static String toJSONString(Object obj, Supplier<String> defaultSupplier) {
+        try {
+            return obj != null ? mapper.writeValueAsString(obj) : defaultSupplier.get();
+        } catch (Throwable e) {
+            log.error(String.format("toJSONString %s", obj != null ? obj.toString() : "null"), e);
+        }
+        return defaultSupplier.get();
+    }
+
+    public static <T> T toJavaObject(String value, Class<T> tClass) {
+        return !DbUtil.isEmptyOrNull(value) ? toJavaObject(value, tClass, () -> null) : null;
+    }
+
+    public static <T> T toJavaObject(Object obj, Class<T> tClass) {
+        return obj != null ? toJavaObject(toJSONString(obj), tClass, () -> null) : null;
+    }
+
+    public static <T> T toJavaObject(String value, Class<T> tClass, Supplier<T> defaultSupplier) {
+        try {
+            if (DbUtil.isEmptyOrNull(value)) {
+                return defaultSupplier.get();
+            }
+            return mapper.readValue(value, tClass);
+        } catch (Throwable e) {
+            log.error(String.format("toJavaObject exception: \n %s\n %s", value, tClass), e);
+        }
+        return defaultSupplier.get();
+    }
+
+    public static <T> List<T> toJavaObjectList(String value, Class<T> tClass) {
+        return !DbUtil.isEmptyOrNull(value) ? toJavaObjectList(value, tClass, () -> null) : null;
+    }
+
+    public static <T> List<T> toJavaObjectList(Object obj, Class<T> tClass) {
+        return obj != null ? toJavaObjectList(toJSONString(obj), tClass, () -> null) : null;
+    }
+
+    public static <T> List<T> toJavaObjectList(String value, Class<T> tClass, Supplier<List<T>> defaultSupplier) {
+        try {
+            if (DbUtil.isEmptyOrNull(value)) {
+                return defaultSupplier.get();
+            }
+           /* if(value.contains("\"")){
+                value = value.replaceAll("\"","\\\\\"");
+            }*/
+            JavaType javaType = mapper.getTypeFactory().constructParametricType(List.class, tClass);
+            return mapper.readValue(value, javaType);
+        } catch (Throwable e) {
+            log.error(String.format("toJavaObjectList exception value: \n%s\n class:%s", value, tClass), e);
+        }
+        return defaultSupplier.get();
+    }
+
+    /**
+     * 简单地直接用json复制或者转换(Cloneable)
+     *
+     */
+    public static <T> T jsonCopy(Object obj, Class<T> tClass) {
+        return obj != null ? toJavaObject(toJSONString(obj), tClass) : null;
+    }
+
+    public static Map<String, Object> toMap(String value) {
+        return !DbUtil.isEmptyOrNull(value) ? toMap(value, () -> null) : null;
+    }
+
+    public static Map<String, Object> toMap(Object value) {
+        return value != null ? toMap(value, () -> null) : null;
+    }
+
+    public static Map<String, Object> toMap(Object value, Supplier<Map<String, Object>> defaultSupplier) {
+        if (value == null) {
+            return defaultSupplier.get();
+        }
+        try {
+            if (value instanceof Map) {
+                return (Map<String, Object>) value;
+            }
+        } catch (Exception e) {
+            log.info("fail to convert" + toJSONString(value), e);
+        }
+        return toMap(toJSONString(value), defaultSupplier);
+    }
+
+    public static Map<String, Object> toMap(String value, Supplier<Map<String, Object>> defaultSupplier) {
+        if (DbUtil.isEmptyOrNull(value)) {
+            return defaultSupplier.get();
+        }
+        try {
+            return toJavaObject(value, LinkedHashMap.class);
+        } catch (Exception e) {
+            log.error(String.format("toMap exception\n%s", value), e);
+        }
+        return defaultSupplier.get();
+    }
+
+
+    public static List<Object> toList(String value) {
+        return !DbUtil.isEmptyOrNull(value) ? toList(value, () -> null) : null;
+    }
+
+    public static List<Object> toList(Object value) {
+        return value != null ? toList(value, () -> null) : null;
+    }
+
+    public static List<Object> toList(String value, Supplier<List<Object>> defaultSuppler) {
+        if (DbUtil.isEmptyOrNull(value)) {
+            return defaultSuppler.get();
+        }
+        try {
+            return toJavaObject(value, List.class);
+        } catch (Exception e) {
+            log.error("toList exception\n" + value, e);
+        }
+        return defaultSuppler.get();
+    }
+
+    public static List<Object> toList(Object value, Supplier<List<Object>> defaultSuppler) {
+        if (value == null) {
+            return defaultSuppler.get();
+        }
+        if (value instanceof List) {
+            return (List<Object>) value;
+        }
+        return toList(toJSONString(value), defaultSuppler);
+    }
+
+}

+ 9 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/BasicData.java

@@ -0,0 +1,9 @@
+package com.kym.jdbc;
+
+import java.io.Serializable;
+
+
+public class BasicData implements Serializable {
+    public String key;
+    public Object value;
+}

+ 25 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/BasicEntity.java

@@ -0,0 +1,25 @@
+package com.kym.jdbc;
+
+import com.kym.jdbc.annotations.DBF;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * build by yaop at 2018/12/23 20:56
+ *
+ * @author zuypeng
+ */
+public class BasicEntity implements Serializable {
+    @DBF(comment = "ID")
+    public long id;
+
+    @DBF(comment = "租户ID")
+    public long orgId;
+
+    @DBF(comment = "创建时间")
+    public Date createAt;
+
+    @DBF(comment = "更新时间")
+    public Date updateAt;
+}

+ 32 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/BasicQuery.java

@@ -0,0 +1,32 @@
+package com.kym.jdbc;
+
+
+import com.kym.jdbc.annotations.QF;
+
+
+public class BasicQuery {
+    public static final int SORT_ASC = 1;
+    public static final int SORT_DESC = 2;
+
+    public Long orgId;
+
+    @QF(ignore = true)
+    public int pageIndex = 1;
+
+    @QF(ignore = true)
+    public int pageSize = 20;
+
+    @QF(ignore = true)
+    public String[] sortFields;
+
+    public String[] includeFields;
+
+    public String[] excludeFields;
+
+    public boolean skipHook; //跳过钩子
+
+
+    public BasicQuery() {
+
+    }
+}

+ 37 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/Bean.java

@@ -0,0 +1,37 @@
+package com.kym.jdbc;
+
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.List;
+
+
+/**
+ * 自定义分页对象
+ *
+ * @author yaopeng
+ * @date 2018/1/17 16:28
+ */
+public class Bean<T> implements Serializable {
+    @Serial
+    private static final long serialVersionUID = 1L;
+    public long count;
+    public int pageIndex;
+    public int pageSize;
+    public List<T> list;
+
+    public Bean() {
+    }
+
+    public Bean(List<T> list) {
+        this.list = list;
+        this.count = list.size();
+    }
+
+    public Bean(List<T> list, long count, int pageIndex, int pageSize) {
+        this.pageIndex = pageIndex;
+        this.pageSize = pageSize;
+        this.list = list;
+        this.count = count;
+    }
+}

+ 9 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/DbException.java

@@ -0,0 +1,9 @@
+package com.kym.jdbc;
+
+import org.springframework.dao.DataAccessException;
+
+public class DbException extends DataAccessException {
+    public DbException(String msg) {
+        super(msg);
+    }
+}

+ 15 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/ExtQuery.java

@@ -0,0 +1,15 @@
+package com.kym.jdbc;
+
+import java.util.List;
+
+public class ExtQuery {
+   public List<ExtQueryWrapper> wrappers;
+   public int joins;
+
+
+    public static class ExtQueryWrapper{
+        public String key;
+        public Object[] values;
+        public int operation;
+    }
+}

+ 7 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/IBuilder.java

@@ -0,0 +1,7 @@
+package com.kym.jdbc;
+
+/**
+ * 查询构造器基类
+ */
+public class IBuilder {
+}

+ 30 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/IHandler.java

@@ -0,0 +1,30 @@
+package com.kym.jdbc;
+
+/**
+ * sql handler类型
+ */
+public interface IHandler {
+    String MYBATIS = "mybatis";
+    String JPA = "jpa";
+    String HIBERNATE = "hibernate";
+    String JDBC = "jdbcTemplate";
+    String MONGO = "mongoTemplate";
+
+
+     int DIALECT_MYSQL = 1;
+     int DIALECT_MSSQL = 2;
+     int DIALECT_ORACLE = 3;
+     int DIALECT_PGSQL = 4;
+     int DIALECT_DB2 = 5;
+     int DIALECT_H2 = 6;
+     int DIALECT_SQLITE = 7;
+     int DIALECT_RDJC = 8;
+     int DIALECT_DAME = 9;
+
+    /**
+     * 获取当前持久层框架类型
+     *
+     * @return
+     */
+    String getHandlerType();
+}

+ 542 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/MBuilder.java

@@ -0,0 +1,542 @@
+package com.kym.jdbc;
+
+import com.kym.DbUtil;
+import com.kym.jdbc.annotations.QF;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 查询条件builder,针对非结构化查询数据库mongodb
+ *
+ * @author asynll
+ */
+public class MBuilder extends IBuilder {
+    private static Logger logger = LoggerFactory.getLogger(MBuilder.class);
+
+    private static final String SORT = "Sort";
+    /**
+     * 查询条件
+     */
+    private List<Where> wheres = new LinkedList<>();
+    /**
+     * 分组字段
+     */
+    private List<String> groupBys = new ArrayList<>();
+    /**
+     * 排序map
+     */
+    private static Map<Integer, String> sortMap = new HashMap<>(16);
+    /**
+     * 排序集合,奇数 -升序   偶数-降序  值越大越靠后
+     */
+    private List<String> orderBys = new ArrayList<>();
+    /**
+     * left join xxx  t1 on t1.xx  = l.f1
+     */
+    private List<String> joins = new ArrayList<>();
+    /**
+     * 查询分组  () or/and ()
+     */
+    private List<Group> groups = new ArrayList<>();
+    /**
+     * 分页索引起点
+     */
+    private int limitStart = 0;
+    private int limitDelta = -1;
+    /**
+     * 互斥锁,使用时注意锁索引,否则会增大死锁几率
+     */
+    public boolean forUpdate;
+    /**
+     * 共享锁,不建议使用
+     */
+    public boolean forShare;
+
+
+    private MBuilder() {
+    }
+
+    public static MBuilder build() {
+        sortMap = new HashMap<>();
+        return new MBuilder();
+    }
+
+    public static MBuilder toBuilder(BasicQuery query) {
+        MBuilder builder = new MBuilder();
+        Field[] fields = query.getClass().getFields();
+        for (Field field : fields) {
+            if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
+                continue;
+            }
+            Object val = DbUtil.getFieldValue(query, field);
+            if (!DbUtil.isEmptyOrNull(val)) {
+                Class<?> type = field.getType();
+                String fieldName = field.getName();
+                if (fieldName.endsWith(SORT) && type.equals(Integer.class)) {
+                    fieldName = fieldName.split(SORT)[0];
+                    int sortValue = DbUtil.null2Int(val);
+                    if (sortValue % 2 == BasicQuery.SORT_ASC) {
+//                        builder.asc(fieldName);
+                        sortMap.put(DbUtil.null2Int(val), String.format(" %s asc ", columnName(fieldName)));
+                    } else {
+//                        builder.desc(fieldName);
+                        sortMap.put(DbUtil.null2Int(val), String.format(" %s desc ", columnName(fieldName)));
+                    }
+
+                } else if (type.equals(String.class)) {
+                    builder.like(field.getName(), val);
+                } else if (type.equals(Integer.class) || type.equals(Short.class) || type.equals(Long.class)) {
+                    builder.eq(field.getName(), val);
+                } else {
+                    boolean annoPersent = field.isAnnotationPresent(QF.class);
+                    if (annoPersent) {
+                        QF qf = field.getAnnotation(QF.class);
+                        String op = qf.op();
+                        String sql = qf.sql();
+                        if (!DbUtil.isEmptyOrNull(op)) {
+                            builder.and(qf.tf(), op, val);
+                        } else if (!DbUtil.isEmptyOrNull(sql)) {
+                            builder.sql(sql, val);
+                        } else if ("sortFields".equals(fieldName)) {
+                            String[] sorts = (String[]) val;
+                            for (String sort : sorts) {
+                                int sortVal = DbUtil.null2Int(DbUtil.getFieldValue(query, sort));
+                                if (sortVal > 0) {
+                                    if (sortVal % 2 == BasicQuery.SORT_ASC) {
+                                        sortMap.put(DbUtil.null2Int(val), String.format(" %s asc ", DbUtil.getColumnName(sort.substring(0, sort.length() - 4))));
+                                    } else {
+                                        sortMap.put(DbUtil.null2Int(sortVal), String.format(" %s desc ", DbUtil.getColumnName(sort.substring(0, sort.length() - 4))));
+                                    }
+                                }
+                               /* if (null!=sortVal) {
+                                    if(BasicQuery.SORT_ASC==Integer.parseInt(sortVal.toString())){
+                                        builder.orderBy(String.format("%s asc ",));
+                                    }else if(BasicQuery.SORT_DESC==Integer.parseInt(sortVal.toString())){
+                                        builder.orderBy(String.format("%s desc ", DbUtil.getColumnName(sort.substring(0, sort.length() - 4))));
+                                    }
+                                }*/
+                            }
+                        } else if ("pageSize".equals(fieldName)) {
+                            int pageSize = (int) val;
+                            if (pageSize > 0) {
+                                int pageIndex = (int) DbUtil.getFieldValue(query, "pageIndex");
+                                builder.limit(Math.max(0, (pageIndex - 1) * pageSize), pageSize);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if (!DbUtil.isEmptyOrNull(sortMap)) {
+            Set<Integer> integers = sortMap.keySet();
+            List<Integer> sorts = new ArrayList<>(integers);
+            sorts.sort(Comparator.comparingInt(o -> o));
+            sorts.forEach(k -> builder.orderByNative(sortMap.get(k)));
+        }
+        return builder;
+    }
+
+
+    public String toSql() {
+        StringBuilder sbr = new StringBuilder(" and 1=1 ");
+        //where
+        if (!DbUtil.isEmptyOrNull(wheres)) {
+            for (Where wh : wheres) {
+                if (DbUtil.isEmptyOrNull(wh.sql)) {
+                    sbr.append(DbUtil.getColumnName(wh.key)).append(" ").append(wh.op);
+                    String op = wh.op;
+                    Object value = wh.value;
+                    if ("in".equals(op.trim()) || "not in".equals(op.trim())) {
+                        sbr.append("(");
+                        for (int i = 0; i < Array.getLength(value); i++) {
+                            Object v = Array.get(value, i);
+                            if (v.getClass() == String.class) {
+                                sbr.append("'").append(v).append("'");
+                            } else {
+                                sbr.append(v);
+                            }
+                            if (i != Array.getLength(value) - 1) {
+                                sbr.append(",");
+                            }
+                        }
+                        sbr.append(")");
+                    } else {
+                        if (value.getClass() == String.class) {
+                            sbr.append("'").append(value).append("'");
+                        } else if (wh.value.getClass() == Date.class) {
+                            sbr.append("'").append(new Timestamp(((Date) value).getTime())).append("'");
+                        } else {
+                            sbr.append(value);
+                        }
+                    }
+                } else {
+                    //sql
+                    String whereSql = wh.sql;
+                    sbr.append(" (");
+                    List<Object> values = wh.sqlValues;
+                    if (!DbUtil.isEmptyOrNull(values)) {
+                        if (whereSql.contains("?")) {
+                            String regex = "\\?";
+                            for (Object o : values) {
+                                if (o.getClass() == String.class) {
+                                    whereSql = whereSql.replaceFirst(regex, "'" + o + "'");
+                                } else if (o.getClass() == Date.class) {
+                                    whereSql = whereSql.replaceFirst(regex, "'" + (new Timestamp(((Date) o).getTime())) + "'");
+                                } else {
+                                    whereSql = whereSql.replaceFirst(regex, o.toString());
+                                }
+                            }
+                        }
+                    }
+                    sbr.append(whereSql);
+                    sbr.append(") ");
+                }
+            }
+        }
+
+        //group by
+        if (!DbUtil.isEmptyOrNull(groupBys)) {
+            sbr.append(" group by ");
+            groupBys.forEach(group -> sbr.append(group).append(" ,"));
+            sbr.deleteCharAt(sbr.length() - 1);
+        }
+        //order by
+        if (!DbUtil.isEmptyOrNull(orderBys)) {
+            sbr.append(" order by ");
+            orderBys.forEach(order -> sbr.append(order).append(" ,"));
+            sbr.deleteCharAt(sbr.length() - 1);
+        }
+        //limit
+        if (limitDelta > 0) {
+            sbr.append(" limit ");
+            if (limitStart > 0) {
+                sbr.append(limitStart).append(",");
+            }
+            sbr.append(limitDelta);
+        }
+
+        //for update
+        if (forUpdate) {
+            sbr.append(" for update ");
+        }
+
+        if (forShare) {
+            sbr.append(" with share mode ");
+        }
+
+        return sbr.toString();
+    }
+
+    public String toSql(BasicQuery query) {
+        return toBuilder(query).toSql();
+    }
+
+
+    public MBuilder ne(String key, Object value) {
+        return this.and(key, "<>", value);
+    }
+
+    public MBuilder eq(String key, Object value) {
+        return this.and(key, "=", value);
+    }
+
+
+    private MBuilder and(String key, String op, Object value) {
+        Where w = new Where();
+        w.key = key;
+        w.op = " " + op + " ";
+        w.value = value;
+        boolean isExist = false;
+        for (Where where : wheres) {
+            if (where.key.equals(key)) {
+                where.op = op;
+                where.value = value;
+                isExist = true;
+                break;
+            }
+        }
+        if (!isExist) {
+            wheres.add(w);
+        }
+        return this;
+    }
+
+
+    public MBuilder in(String key, Object[] values) {
+        Where w = new Where();
+        w.key = key;
+        w.op = "in";
+        w.value = values;
+        wheres.add(w);
+        return this;
+    }
+
+
+    public MBuilder notIn(String key, Object[] values) {
+        Where w = new Where();
+        w.key = key;
+        w.op = "not in";
+        w.value = values;
+        wheres.add(w);
+        return this;
+    }
+
+    public MBuilder llike(String key, Object value) {
+        return this.and(key, "like", value.toString() + "%");
+    }
+
+    /**
+     * 模糊匹配,不走索引慎重使用
+     */
+    public MBuilder like(String key, Object value) {
+        return this.and(key, "like", "%" + value.toString() + "%");
+    }
+
+    /**
+     * 右匹配,不走索引慎重使用
+     */
+    public MBuilder rlike(String key, Object value) {
+        return this.and(key, "like", "%" + value.toString());
+    }
+
+    public MBuilder lt(String key, Object value) {
+        return this.and(key, "<", value);
+    }
+
+    public MBuilder lte(String key, Object value) {
+        return this.and(key, "<=", value);
+    }
+
+    public MBuilder gte(String key, Object value) {
+        return this.and(key, ">=", value);
+    }
+
+    /**
+     * 大于
+     */
+    public MBuilder gt(String key, Object value) {
+        return this.and(key, ">", value);
+    }
+
+    public MBuilder between(String key, Object start, Object end) {
+        Where w = new Where();
+        w.sql = " " + columnName(key) + " between ? and ? ";
+        w.sqlValues.add(start);
+        w.sqlValues.add(end);
+        wheres.add(w);
+        return this;
+    }
+
+    public MBuilder or(String key, String op, Object value) {
+        Where w = new Where();
+        w.sql = " or (" + columnName(key) + " " + op + " ?)";
+        w.sqlValues.addAll(Collections.singletonList(value));
+        wheres.add(w);
+        return this;
+    }
+
+    public MBuilder or(String sql, Object... values) {
+        Where w = new Where();
+        w.sql = " or (" + sql + " )";
+        w.sqlValues.addAll(Arrays.asList(values));
+        wheres.add(w);
+        return this;
+    }
+
+    /**
+     * 构造sql片段查询条件
+     *
+     * @param sql    sql片段,必须符合数据库语法,缺省参数已?作为通配符
+     * @param values sql缺省参数的值,有序
+     */
+    public MBuilder sql(String sql, Object... values) {
+        Where w = new Where();
+        w.sql = sql;
+        w.sqlValues.addAll(Arrays.asList(values));
+        wheres.add(w);
+        return this;
+    }
+
+    private MBuilder orderByNative(String orderBy) {
+        orderBys.add(orderBy);
+        return this;
+    }
+
+    public MBuilder orderBy(String orderBy) {
+        String[] splits = orderBy.split(" ");
+        StringBuilder sbr = new StringBuilder(" ");
+        for (String split : splits) {
+            sbr.append(columnName(split).trim()).append(" ");
+        }
+        orderBys.add(sbr.toString());
+        return this;
+    }
+
+    public MBuilder asc(String key) {
+        orderBys.add(columnName(key) + " asc ");
+        return this;
+    }
+
+    public MBuilder desc(String key) {
+        orderBys.add(columnName(key) + " desc ");
+        return this;
+    }
+
+    /**
+     * 分组字段,仅在使用聚合函数{@link com.kym.jdbc.ibatis.BoxMapper#selectFunc(Class, String, MBuilder)}时有效
+     */
+    public MBuilder groupBy(String orderBy) {
+        groupBys.add(columnName(orderBy));
+        return this;
+    }
+
+    /**
+     * 分页
+     *
+     * @param pos   分页起点
+     * @param delta 分页条数
+     * @return
+     */
+    public MBuilder limit(int pos, int delta) {
+        limitStart = pos;
+        limitDelta = delta;
+        return this;
+    }
+
+    public MBuilder limit(int delta) {
+        limitStart = 0;
+        limitDelta = delta;
+        return this;
+    }
+
+    public MBuilder forUpdate() {
+        forUpdate = true;
+        return this;
+    }
+
+    public MBuilder forShare() {
+        forShare = true;
+        return this;
+    }
+
+    public MBuilder group(boolean orGroup, List<Where> wheres) {
+        groups.add(new Group(orGroup, wheres));
+        return this;
+    }
+
+
+    public List<String> getOrderBys() {
+        return orderBys;
+    }
+
+    public List<String> getGroupBys() {
+        return groupBys;
+    }
+
+    public int limitStart() {
+        return limitStart;
+    }
+
+    public int limitDelta() {
+        return limitDelta;
+    }
+
+    public List<Where> getWheres() {
+        return wheres;
+    }
+
+
+    private static String columnName(String field) {
+        StringBuilder sbr = new StringBuilder();
+        char[] chars = field.toCharArray();
+        for (char ch : chars) {
+            if (Character.isUpperCase(ch)) {
+                sbr.append("_").append(Character.toLowerCase(ch));
+            } else {
+                sbr.append(ch);
+            }
+        }
+        return sbr.toString();
+    }
+
+    public static class Group {
+        public boolean orConnector;
+        public List<Where> wheres;
+
+        public Group(boolean orConnector, List<Where> wheres) {
+            this.orConnector = orConnector;
+            this.wheres = wheres;
+        }
+
+    }
+
+    public static class Where {
+        public boolean isGroup;
+        public Group group;
+        public String key;
+        public Object value;
+        public String op = "=";
+        public String sql;
+        /**
+         * sql结构化数据库的查询语句
+         */
+        public LinkedList<Object> sqlValues = new LinkedList<>();
+
+        Where() {
+        }
+
+        public Where(String key, Object value, String op) {
+            this.key = key;
+            this.value = value;
+            this.op = op;
+        }
+
+        public Where(String key, Object value, String op, String sql, LinkedList<Object> sqlValues) {
+            this.key = key;
+            this.value = value;
+            this.op = op;
+            this.sql = sql;
+            this.sqlValues = sqlValues;
+        }
+
+        @Override
+        public String toString() {
+            return "Where{" +
+                    "key='" + key + '\'' +
+                    ", value=" + value +
+                    ", op='" + op + '\'' +
+                    ", sql='" + sql + '\'' +
+                    ", sqlValues=" + sqlValues +
+                    '}';
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "Builder{" +
+                "wheres=" + wheres +
+                ", groupBys=" + groupBys +
+                ", orderBys=" + orderBys +
+                ", limitStart=" + limitStart +
+                ", limitDelta=" + limitDelta +
+                ", forUpdate=" + forUpdate +
+                ", forShare=" + forShare +
+                '}';
+    }
+}

+ 879 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/OBuilder.java

@@ -0,0 +1,879 @@
+package com.kym.jdbc;
+
+import com.kym.DbUtil;
+import com.kym.jdbc.annotations.QF;
+import com.kym.jdbc.lambda.ColumnFunc;
+import com.kym.jdbc.lambda.DefaultLambdaParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+
+/**
+ * 查询条件builder,针对结构化查询关系数据库
+ *
+ * @author asynll
+ */
+public class OBuilder extends IBuilder {
+    private static Logger logger = LoggerFactory.getLogger(OBuilder.class);
+
+    public static int dialect;
+
+    private static final String SORT = "Sort";
+    /**
+     * 查询条件
+     */
+    private List<Where> wheres = new LinkedList<>();
+    /**
+     * 分组字段
+     */
+    private List<String> groupBys = new ArrayList<>();
+    /**
+     * 排序map
+     */
+    private static Map<Integer, String> sortMap = new HashMap<>(16);
+    /**
+     * 排序集合,奇数 -升序   偶数-降序  值越大越靠后
+     */
+    private List<String> orderBys = new ArrayList<>();
+    /**
+     * left join xxx  t1 on t1.xx  = l.f1
+     */
+    private List<String> joins = new ArrayList<>();
+    /**
+     * 查询分组  () or/and ()
+     */
+    private List<Group> groups = new ArrayList<>();
+    /**
+     * 分页索引起点
+     */
+    private int limitStart = 0;
+    private int limitDelta = -1;
+    /**
+     * 互斥锁,使用时注意锁索引,否则会增大死锁几率
+     */
+    public boolean forUpdate;
+    /**
+     * 共享锁,不建议使用
+     */
+    public boolean forShare;
+
+
+    private OBuilder() {
+    }
+
+    public static OBuilder build() {
+        sortMap = new HashMap<>();
+        return new OBuilder();
+    }
+
+    public static OBuilder toBuilder(BasicQuery query) {
+        OBuilder builder = new OBuilder();
+        Set<Field> fields = DbUtil.getQueryFields(query.getClass());
+//        Field[] fields = query.getClass().getFields();
+        for (Field field : fields) {
+            if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
+                continue;
+            }
+            Object val = DbUtil.getFieldValue(query, field);
+            if (!DbUtil.isEmptyOrNull(val)) {
+                Class<?> type = field.getType();
+                String fieldName = field.getName();
+                if (fieldName.endsWith(SORT) && type.equals(Integer.class)) {
+                    fieldName = fieldName.split(SORT)[0];
+                    int sortValue = DbUtil.null2Int(val);
+                    if (sortValue % 2 == BasicQuery.SORT_ASC) {
+//                        builder.asc(fieldName);
+                        sortMap.put(DbUtil.null2Int(val), String.format(" %s asc ", columnName(fieldName)));
+                    } else {
+//                        builder.desc(fieldName);
+                        sortMap.put(DbUtil.null2Int(val), String.format(" %s desc ", columnName(fieldName)));
+                    }
+
+                } else if (type.equals(String.class)) {
+                    builder.like(fieldName, val);
+                } else if (type.equals(Integer.class) || type.equals(Short.class) || type.equals(Long.class)) {
+                    builder.eq(fieldName, val);
+                } else {
+                    boolean annoPersent = field.isAnnotationPresent(QF.class);
+                    if (annoPersent) {
+                        QF qf = field.getAnnotation(QF.class);
+                        String op = qf.op();
+                        String sql = qf.sql();
+                        if (!DbUtil.isEmptyOrNull(op)) {
+                            builder.and(qf.tf(), op, val);
+                        } else if (!DbUtil.isEmptyOrNull(sql)) {
+                            builder.sql(sql, val);
+                        } else if ("sortFields".equals(fieldName)) {
+                            String[] sorts = (String[]) val;
+                            for (String sort : sorts) {
+                                int sortVal = DbUtil.null2Int(DbUtil.getFieldValue(query, sort));
+                                if (sortVal > 0) {
+                                    if (sortVal % 2 == BasicQuery.SORT_ASC) {
+                                        sortMap.put(DbUtil.null2Int(val), String.format(" %s asc ", DbUtil.getColumnName(sort.substring(0, sort.length() - 4))));
+                                    } else {
+                                        sortMap.put(DbUtil.null2Int(sortVal), String.format(" %s desc ", DbUtil.getColumnName(sort.substring(0, sort.length() - 4))));
+                                    }
+                                }
+                               /* if (null!=sortVal) {
+                                    if(BasicQuery.SORT_ASC==Integer.parseInt(sortVal.toString())){
+                                        builder.orderBy(String.format("%s asc ",));
+                                    }else if(BasicQuery.SORT_DESC==Integer.parseInt(sortVal.toString())){
+                                        builder.orderBy(String.format("%s desc ", DbUtil.getColumnName(sort.substring(0, sort.length() - 4))));
+                                    }
+                                }*/
+                            }
+                        } else if ("pageSize".equals(fieldName)) {
+                            int pageSize = (int) val;
+                            if (pageSize > 0) {
+                                int pageIndex = (int) DbUtil.getFieldValue(query, "pageIndex");
+                                builder.limit(Math.max(0, (pageIndex - 1) * pageSize), pageSize);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if (!DbUtil.isEmptyOrNull(sortMap)) {
+            Set<Integer> integers = sortMap.keySet();
+            List<Integer> sorts = new ArrayList<>(integers);
+            sorts.sort(Comparator.comparingInt(o -> o));
+            sorts.forEach(k -> builder.orderByNative(sortMap.get(k)));
+        }
+        return builder;
+    }
+
+
+    public String toSql() {
+        StringBuilder sbr = new StringBuilder(" and 1=1 ");
+        //where
+        if (!DbUtil.isEmptyOrNull(wheres)) {
+            for (Where wh : wheres) {
+                if (DbUtil.isEmptyOrNull(wh.sql)) {
+                    sbr.append(DbUtil.getColumnName(wh.key)).append(" ").append(wh.op);
+                    String op = wh.op;
+                    Object value = wh.value;
+                    if ("in".equals(op.trim()) || "not in".equals(op.trim())) {
+                        sbr.append("(");
+                        for (int i = 0; i < Array.getLength(value); i++) {
+                            Object v = Array.get(value, i);
+                            if (v.getClass() == String.class) {
+                                sbr.append("'").append(v).append("'");
+                            } else {
+                                sbr.append(v);
+                            }
+                            if (i != Array.getLength(value) - 1) {
+                                sbr.append(",");
+                            }
+                        }
+                        sbr.append(")");
+                    } else {
+                        if (value.getClass() == String.class) {
+                            sbr.append("'").append(value).append("'");
+                        } else if (wh.value.getClass() == Date.class) {
+                            sbr.append("'").append(new Timestamp(((Date) value).getTime())).append("'");
+                        } else {
+                            sbr.append(value);
+                        }
+                    }
+                } else {
+                    //sql
+                    String whereSql = wh.sql;
+                    sbr.append(" (");
+                    List<Object> values = wh.sqlValues;
+                    if (!DbUtil.isEmptyOrNull(values)) {
+                        if (whereSql.contains("?")) {
+                            String regex = "\\?";
+                            for (Object o : values) {
+                                if (o.getClass() == String.class) {
+                                    whereSql = whereSql.replaceFirst(regex, "'" + o + "'");
+                                } else if (o.getClass() == Date.class) {
+                                    whereSql = whereSql.replaceFirst(regex, "'" + (new Timestamp(((Date) o).getTime())) + "'");
+                                } else {
+                                    whereSql = whereSql.replaceFirst(regex, o.toString());
+                                }
+                            }
+                        }
+                    }
+                    sbr.append(whereSql);
+                    sbr.append(") ");
+                }
+            }
+        }
+
+        //group by
+        if (!DbUtil.isEmptyOrNull(groupBys)) {
+            sbr.append(" group by ");
+            groupBys.forEach(group -> sbr.append(group).append(" ,"));
+            sbr.deleteCharAt(sbr.length() - 1);
+        }
+        //order by
+        if (!DbUtil.isEmptyOrNull(orderBys)) {
+            sbr.append(" order by ");
+            orderBys.forEach(order -> sbr.append(order).append(" ,"));
+            sbr.deleteCharAt(sbr.length() - 1);
+        }
+        //limit
+        if (limitDelta > 0) {
+            sbr.append(" limit ");
+            if (limitStart > 0) {
+                sbr.append(limitStart).append(",");
+            }
+            sbr.append(limitDelta);
+        }
+
+        //for update
+        if (forUpdate) {
+            sbr.append(" for update ");
+        }
+
+        if (forShare) {
+            sbr.append(" with share mode ");
+        }
+
+        return sbr.toString();
+    }
+
+    public String toSql(BasicQuery query) {
+        return toBuilder(query).toSql();
+    }
+
+
+    public <T> OBuilder ne(ColumnFunc<T, ?> keyExtractor, Object value) {
+        return this.and(DefaultLambdaParser.getPropertyName(keyExtractor), "<>", value);
+    }
+
+    public <T> OBuilder ne(boolean expect, ColumnFunc<T, ?> keyExtractor, Object value) {
+        return expect ? this.and(DefaultLambdaParser.getPropertyName(keyExtractor), "<>", value) : this;
+    }
+
+    public OBuilder ne(String key, Object value) {
+        return this.and(key, "<>", value);
+    }
+
+    //TODO 转换表达式
+    public OBuilder eq(String key, Object value) {
+        return this.and(key, "=", value);
+    }
+
+    public <T> OBuilder eq(boolean expect, ColumnFunc<T, ?> keyExtractor, Object value) {
+        String key = DefaultLambdaParser.getPropertyName(keyExtractor);
+        return expect ? this.and(key, "=", value) : this;
+    }
+
+    public <T> OBuilder eq(ColumnFunc<T, ?> keyExtractor, Object value) {
+        String key = DefaultLambdaParser.getPropertyName(keyExtractor);
+        return this.and(key, "=", value);
+    }
+
+    private OBuilder and(String key, String op, Object value) {
+        Where w = new Where();
+        w.key = columnName(key);
+        w.op = " " + op + " ";
+        w.value = value;
+        boolean isExist = false;
+        for (Where where : wheres) {
+            if (!DbUtil.isEmptyOrNull(where.key)&&where.key.equals(key)) {
+                where.op = op;
+                where.value = value;
+                isExist = true;
+                break;
+            }
+        }
+        if (!isExist) {
+            wheres.add(w);
+        }
+        return this;
+    }
+
+
+    public OBuilder in(String key, Object[] values) {
+        if (DbUtil.isEmptyOrNull(values)) {
+            throw new IllegalArgumentException("empty values for in operation");
+        }
+        Where w = new Where();
+        w.key = key;
+        w.op = " in ";
+        w.value = values;
+        wheres.add(w);
+        return this;
+    }
+
+
+    public <T> OBuilder in(ColumnFunc<T, ?> keyExtractor, Object[] values) {
+        if (DbUtil.isEmptyOrNull(values)) {
+            throw new IllegalArgumentException("empty values for in operation");
+        }
+        Where w = new Where();
+        w.key = DefaultLambdaParser.getPropertyName(keyExtractor);
+        w.op = " in ";
+        w.value = values;
+        wheres.add(w);
+        return this;
+    }
+
+    public <T> OBuilder in(boolean expect, ColumnFunc<T, ?> keyExtractor, Object[] values) {
+        if (!expect) {
+            return this;
+        }
+        if (DbUtil.isEmptyOrNull(values)) {
+            throw new IllegalArgumentException("empty values for in operation");
+        }
+        Where w = new Where();
+        w.key = DefaultLambdaParser.getPropertyName(keyExtractor);
+        w.op = " in ";
+        w.value = values;
+        wheres.add(w);
+        return this;
+    }
+
+
+    public <T> OBuilder jex(boolean expect, ColumnFunc<T, ?> keyExtractor, String path, Object value) {
+        if (!expect) {
+            return this;
+        }
+        return jex(keyExtractor, path, value);
+    }
+
+    /**
+     * json 查询字段值等于 json_extract
+     * <p>
+     * select * from t  where cfg::jsonb->>'ww' ='6'
+     *
+     * @param keyExtractor
+     * @param <T>
+     * @return
+     */
+    public <T> OBuilder jex(ColumnFunc<T, ?> keyExtractor, String path, Object value) {
+        if (DbUtil.isEmptyOrNull(value)) {
+            throw new IllegalArgumentException("empty value for in operation");
+        }
+        Where w = new Where();
+        String key = DefaultLambdaParser.getPropertyName(keyExtractor);
+        key = DbUtil.getColumnName(key);
+        w.sqlValues = new LinkedList<>();
+        String baseSQL = " json_unquote(json_extract(`" + key + "`,?))  = '?' ";
+        if (dialect == IHandler.DIALECT_MYSQL) {
+            //默认的方言类型
+        } else if (dialect == IHandler.DIALECT_PGSQL) {
+            baseSQL = "  " + key + "::jsonb ->> ? = '?' ";
+        } else {
+            throw new IllegalStateException("unsupported dialect");
+        }
+        w.sqlValues.add(path);
+        w.sqlValues.add(value);
+        w.sql = baseSQL;
+        wheres.add(w);
+        return this;
+    }
+
+
+    public <T> OBuilder jin(boolean expect, ColumnFunc<T, ?> keyExtractor, List<Object> values) {
+        if (!expect) {
+            return this;
+        }
+        return jin(keyExtractor, values, true);
+    }
+
+    public <T> OBuilder jin(ColumnFunc<T, ?> keyExtractor, List<Object> values) {
+        return jin(keyExtractor, values, true);
+    }
+
+    public <T> OBuilder jin(boolean expect, ColumnFunc<T, ?> keyExtractor, Collection<?> values, boolean and) {
+        if (!expect) {
+            return this;
+        }
+        return jin(keyExtractor, values, and);
+    }
+
+    /**
+     * json数组查询 json_contains 判断是否包含在数组中
+     *
+     * @param keyExtractor
+     * @param values
+     * @param and          多参数的连接方式 ,默认and连接
+     * @param <T>
+     * @return
+     */
+    public <T> OBuilder jin(ColumnFunc<T, ?> keyExtractor, Collection<?> values, boolean and) {
+        if (DbUtil.isEmptyOrNull(values)) {
+            throw new IllegalArgumentException("empty values for in operation");
+        }
+        Where w = new Where();
+        String key = DefaultLambdaParser.getPropertyName(keyExtractor);
+        key = DbUtil.getColumnName(key);
+        int length = values.size();
+        StringBuilder sbr = new StringBuilder();
+        w.sqlValues = new LinkedList<>();
+        String baseSQL;
+        if (dialect == IHandler.DIALECT_MYSQL) {
+            baseSQL = " json_contains(`" + key + "`,JSON_ARRAY('?')) ";
+            //默认的方言类型
+        } else if (dialect == IHandler.DIALECT_PGSQL) {
+            baseSQL = "  " + key + "::jsonb @> '?' ";
+        } else {
+            baseSQL = " json_contains(`" + key + "`,JSON_ARRAY('?')) ";
+            throw new IllegalStateException("unsupported dialect");
+        }
+        w.sqlValues.addAll(values);
+        w.sql = values.stream().map(k -> baseSQL).collect(Collectors.joining(and ? "AND" : "OR"));
+       /* for (int i = 0; i < length; i++) {
+            for (Object value : values) {
+                w.sqlValues.add(value);
+                if (i != length - 1) {
+                    sbr.append(baseSQL).append(and ? "AND" : "OR");
+                } else {
+                    sbr.append(baseSQL);
+                }
+            }
+        }*/
+//        w.sql = sbr.toString();
+        wheres.add(w);
+        return this;
+    }
+
+
+    public <T> OBuilder jov(boolean expect, ColumnFunc<T, ?> keyExtractor, Collection<?> values) {
+        if (!expect) {
+            return this;
+        }
+        return jov(keyExtractor, values);
+    }
+
+    /**
+     * json数组查询 JSON_OVERLAPS 判断是否有数组 交集
+     *
+     * @param keyExtractor
+     * @param values
+     * @param <T>
+     * @return
+     */
+    public <T> OBuilder jov(ColumnFunc<T, ?> keyExtractor, Collection<?> values) {
+        if (DbUtil.isEmptyOrNull(values)) {
+            throw new IllegalArgumentException("empty values for in operation");
+        }
+        Where w = new Where();
+        String key = DefaultLambdaParser.getPropertyName(keyExtractor);
+        key = DbUtil.getColumnName(key);
+        StringBuilder sbr = new StringBuilder();
+        w.sqlValues = new LinkedList<>();
+
+        //MYSQL> 8.0.17
+        if (dialect == IHandler.DIALECT_MYSQL) {
+            String baseSQL = " JSON_OVERLAPS(`" + key + "`,JSON_ARRAY('?'))  = 1";
+            //默认的方言类型
+            sbr.append(baseSQL);
+        } else if (dialect == IHandler.DIALECT_PGSQL) {
+            String baseSQL = "  " + key + "::jsonb && jsonb_build_array('?') ";
+            sbr.append(baseSQL);
+        } else {
+            throw new IllegalStateException("unsupported dialect");
+        }
+        w.sqlValues = new LinkedList<>(values);
+        w.sql = sbr.toString();
+        wheres.add(w);
+        return this;
+    }
+
+
+    public OBuilder notIn(String key, Object[] values) {
+        Where w = new Where();
+        w.key = key;
+        w.op = " not in ";
+        w.value = values;
+        wheres.add(w);
+        return this;
+    }
+
+    public <T> OBuilder notIn(boolean expect, ColumnFunc<T, ?> keyExtractor, Object[] values) {
+        if (!expect) {
+            return this;
+        }
+        return notIn(keyExtractor, values);
+    }
+
+
+    public <T> OBuilder notIn(ColumnFunc<T, ?> keyExtractor, Object[] values) {
+        Where w = new Where();
+        w.key = DbUtil.getColumnName(DefaultLambdaParser.getPropertyName(keyExtractor));
+        w.op = " not in ";
+        w.value = values;
+        wheres.add(w);
+        return this;
+    }
+
+    public OBuilder llike(String key, Object value) {
+        return this.and(key, "like", value.toString() + "%");
+    }
+
+
+    public <T> OBuilder llike(boolean expect, ColumnFunc<T, ?> keyExtractor, Object value) {
+        if (!expect) {
+            return this;
+        }
+        return llike(keyExtractor, value);
+    }
+
+    public <T> OBuilder llike(ColumnFunc<T, ?> keyExtractor, Object value) {
+        return this.and(DefaultLambdaParser.getPropertyName(keyExtractor), "like", value.toString() + "%");
+    }
+
+    /**
+     * 模糊匹配,不走索引慎重使用
+     */
+    public OBuilder like(String key, Object value) {
+        return this.and(key, "like", "%" + value.toString() + "%");
+    }
+
+    public <T> OBuilder like(boolean expect, ColumnFunc<T, ?> keyExtractor, Object value) {
+        if (!expect) {
+            return this;
+        }
+        return like(keyExtractor, value);
+    }
+
+    public <T> OBuilder like(ColumnFunc<T, ?> keyExtractor, Object value) {
+        return this.and(DefaultLambdaParser.getPropertyName(keyExtractor), "like", "%" + value.toString() + "%");
+    }
+
+
+    /**
+     * 右匹配,不走索引慎重使用
+     */
+    public OBuilder rlike(String key, Object value) {
+        return this.and(key, "like", "%" + value.toString());
+    }
+
+    public <T> OBuilder rlike(boolean expect, ColumnFunc<T, ?> keyExtractor, Object value) {
+        if (!expect) {
+            return this;
+        }
+        return rlike(keyExtractor, value);
+    }
+
+    public <T> OBuilder rlike(ColumnFunc<T, ?> keyExtractor, Object value) {
+        return this.and(DefaultLambdaParser.getPropertyName(keyExtractor), "like", "%" + value.toString());
+    }
+
+
+    public OBuilder lt(String key, Object value) {
+        return this.and(key, "<", value);
+    }
+
+    public <T> OBuilder lt(boolean expect, ColumnFunc<T, ?> keyExtractor, Object value) {
+        if (!expect) {
+            return this;
+        }
+        return lt(keyExtractor, value);
+    }
+
+    public <T> OBuilder lt(ColumnFunc<T, ?> keyExtractor, Object value) {
+        return this.and(DefaultLambdaParser.getPropertyName(keyExtractor), "<", value);
+    }
+
+    public OBuilder lte(String key, Object value) {
+        return this.and(key, "<=", value);
+    }
+
+    public <T> OBuilder lte(boolean expect, ColumnFunc<T, ?> keyExtractor, Object value) {
+        if (!expect) {
+            return this;
+        }
+        return lte(keyExtractor, value);
+    }
+
+    public <T> OBuilder lte(ColumnFunc<T, ?> keyExtractor, Object value) {
+        return this.and(DefaultLambdaParser.getPropertyName(keyExtractor), "<=", value);
+    }
+
+    public OBuilder gte(String key, Object value) {
+        return this.and(key, ">=", value);
+    }
+
+    public <T> OBuilder gte(boolean expect, ColumnFunc<T, ?> keyExtractor, Object value) {
+        if (!expect) {
+            return this;
+        }
+        return gte(keyExtractor, value);
+    }
+
+    public <T> OBuilder gte(ColumnFunc<T, ?> keyExtractor, Object value) {
+        return this.and(DefaultLambdaParser.getPropertyName(keyExtractor), ">=", value);
+    }
+
+    /**
+     * 大于
+     */
+    public OBuilder gt(String key, Object value) {
+        return this.and(key, ">", value);
+    }
+
+    public <T> OBuilder gt(boolean expect, ColumnFunc<T, ?> keyExtractor, Object value) {
+        if (!expect) {
+            return this;
+        }
+        return gt(keyExtractor, value);
+    }
+
+    public <T> OBuilder gt(ColumnFunc<T, ?> keyExtractor, Object value) {
+        return this.and(columnName(DefaultLambdaParser.getPropertyName(keyExtractor)), ">", value);
+    }
+
+    public <T> OBuilder between(boolean expect, ColumnFunc<T, ?> keyExtractor, Object start, Object end) {
+        if (!expect) {
+            return this;
+        }
+        return between(keyExtractor, start, end);
+    }
+
+    public <T> OBuilder between(ColumnFunc<T, ?> keyExtractor, Object start, Object end) {
+        Where w = new Where();
+        w.sql = " " + columnName(DefaultLambdaParser.getPropertyName(keyExtractor)) + " between ? and ? ";
+        w.sqlValues.add(start);
+        w.sqlValues.add(end);
+        wheres.add(w);
+        return this;
+    }
+
+    public OBuilder or(String key, String op, Object value) {
+        Where w = new Where();
+        w.sql = " or (" + columnName(key) + " " + op + " ?)";
+        w.sqlValues.addAll(Collections.singletonList(value));
+        wheres.add(w);
+        return this;
+    }
+
+    public <T> OBuilder or(boolean expect, ColumnFunc<T, ?> keyExtractor, String op, Object value) {
+        if (!expect) {
+            return this;
+        }
+        return or(keyExtractor, op, value);
+    }
+
+    public <T> OBuilder or(ColumnFunc<T, ?> keyExtractor, String op, Object value) {
+        Where w = new Where();
+        w.sql = " or (" + columnName(DefaultLambdaParser.getPropertyName(keyExtractor)) + " " + op + " ?)";
+        w.sqlValues.addAll(Collections.singletonList(value));
+        wheres.add(w);
+        return this;
+    }
+
+    public OBuilder or(String sql, Object... values) {
+        Where w = new Where();
+        w.sql = " or (" + sql + " )";
+        w.sqlValues.addAll(Arrays.asList(values));
+        wheres.add(w);
+        return this;
+    }
+
+    /**
+     * 拼接sql片段查询条件
+     *
+     * @param sql    sql片段,必须符合数据库语法,缺省参数已?作为通配符
+     * @param values sql缺省参数的值,有序
+     */
+    public OBuilder sql(String sql, Object... values) {
+        Where w = new Where();
+        w.sql = sql;
+        w.sqlValues.addAll(Arrays.asList(values));
+        wheres.add(w);
+        return this;
+    }
+
+    private OBuilder orderByNative(String orderBy) {
+        orderBys.add(orderBy);
+        return this;
+    }
+
+    public OBuilder orderBy(String orderBy) {
+        String[] splits = orderBy.split(" ");
+        StringBuilder sbr = new StringBuilder(" ");
+        for (String split : splits) {
+            sbr.append(columnName(split).trim()).append(" ");
+        }
+        orderBys.add(sbr.toString());
+        return this;
+    }
+
+
+    public <T> OBuilder orderBy(ColumnFunc<T, ?> keyExtractor) {
+        orderBys.add(columnName(DefaultLambdaParser.getPropertyName(keyExtractor)));
+        return this;
+    }
+
+    public <T> OBuilder asc(ColumnFunc<T, ?> keyExtractor) {
+        orderBys.add(columnName(DefaultLambdaParser.getPropertyName(keyExtractor)) + " asc ");
+        return this;
+    }
+
+    public OBuilder asc(String key) {
+        orderBys.add(columnName(key) + " asc ");
+        return this;
+    }
+
+
+    public <T> OBuilder desc(ColumnFunc<T, ?> keyExtractor) {
+        orderBys.add(columnName(DefaultLambdaParser.getPropertyName(keyExtractor)) + " desc ");
+        return this;
+    }
+
+    public OBuilder desc(String key) {
+        orderBys.add(columnName(key) + " desc ");
+        return this;
+    }
+
+    /**
+     * 分组字段,仅在使用聚合函数{@link com.kym.jdbc.ibatis.BoxMapper#selectFunc(Class, String, OBuilder)}时有效
+     */
+    public OBuilder groupBy(String orderBy) {
+        groupBys.add(columnName(orderBy));
+        return this;
+    }
+
+    public <T> OBuilder groupBy(ColumnFunc<T, ?> keyExtractor) {
+        groupBys.add(columnName(DefaultLambdaParser.getPropertyName(keyExtractor)));
+        return this;
+    }
+
+    /**
+     * 分页
+     *
+     * @param pos   分页起点
+     * @param delta 分页条数
+     * @return
+     */
+    public OBuilder limit(int pos, int delta) {
+        limitStart = pos;
+        limitDelta = delta;
+        return this;
+    }
+
+    public OBuilder limit(int delta) {
+        limitStart = 0;
+        limitDelta = delta;
+        return this;
+    }
+
+    public OBuilder forUpdate() {
+        forUpdate = true;
+        return this;
+    }
+
+    public OBuilder forShare() {
+        forShare = true;
+        return this;
+    }
+
+    public OBuilder group(boolean orGroup, List<Where> wheres) {
+        groups.add(new Group(orGroup, wheres));
+        return this;
+    }
+
+
+    public List<String> getOrderBys() {
+        return orderBys;
+    }
+
+    public List<String> getGroupBys() {
+        return groupBys;
+    }
+
+    public int limitStart() {
+        return limitStart;
+    }
+
+    public int limitDelta() {
+        return limitDelta;
+    }
+
+    public List<Where> getWheres() {
+        return wheres;
+    }
+
+
+    private static String columnName(String field) {
+        StringBuilder sbr = new StringBuilder();
+        char[] chars = field.toCharArray();
+        for (char ch : chars) {
+            if (Character.isUpperCase(ch)) {
+                sbr.append("_").append(Character.toLowerCase(ch));
+            } else {
+                sbr.append(ch);
+            }
+        }
+        return sbr.toString();
+    }
+
+    public static class Group {
+        public boolean orConnector;
+        public List<Where> wheres;
+
+        public Group(boolean orConnector, List<Where> wheres) {
+            this.orConnector = orConnector;
+            this.wheres = wheres;
+        }
+
+    }
+
+    public static class Where {
+        public String key;
+        public Object value;
+        public String op = "=";
+        public String sql;
+        /**
+         * sql结构化数据库的查询语句
+         */
+        public LinkedList<Object> sqlValues = new LinkedList<>();
+
+        Where() {
+        }
+
+
+        public Where(String key, Object value) {
+            this.key = key;
+            this.value = value;
+            this.op = "=";
+        }
+
+        public Where(String key, Object value, String op) {
+            this.key = key;
+            this.value = value;
+            this.op = op;
+        }
+
+        public Where(String key, Object value, String op, String sql, LinkedList<Object> sqlValues) {
+            this.key = key;
+            this.value = value;
+            this.op = op;
+            this.sql = sql;
+            this.sqlValues = sqlValues;
+        }
+
+        @Override
+        public String toString() {
+            return "Where{" + "key='" + key + '\'' + ", value=" + value + ", op='" + op + '\'' + ", sql='" + sql + '\'' + ", sqlValues=" + sqlValues + '}';
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "Builder{" + "wheres=" + wheres + ", groupBys=" + groupBys + ", orderBys=" + orderBys + ", limitStart=" + limitStart + ", limitDelta=" + limitDelta + ", forUpdate=" + forUpdate + ", forShare=" + forShare + '}';
+    }
+}

+ 15 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/ResultHandler.java

@@ -0,0 +1,15 @@
+package com.kym.jdbc;
+
+import java.util.Map;
+
+/**
+ * @author yaop
+ * @date 2019/9/7 14:47
+ */
+@FunctionalInterface
+public interface ResultHandler<T> {
+    /**
+     * SQL结果集处理方法
+     */
+    T handle(Map<?, ?> t) throws Exception;
+}

+ 16 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/SQLEntity.java

@@ -0,0 +1,16 @@
+package com.kym.jdbc;
+
+
+/**
+ * sql构造实体
+ *
+ * @since 2022.05.03
+ */
+public class SQLEntity {
+    /**
+     * sql或nosql语句,if we use english input method,they will be normal.
+     */
+    public String sql;
+    public Object[] parameters;
+
+}

+ 11 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/CNT.java

@@ -0,0 +1,11 @@
+package com.kym.jdbc.annotations;
+
+/**
+ * 条件连接符常量
+ */
+public interface CNT {
+
+    String AND = "and";
+    String OR = "or";
+    String ALL = "all";
+}

+ 52 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/DBF.java

@@ -0,0 +1,52 @@
+package com.kym.jdbc.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 数据表字段与实体类字段映射注解
+ * DataBaseField
+ *
+ * @author asynll
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface DBF {
+    /**
+     * 注释
+     */
+    String comment();
+
+    String dict() default "";
+
+    /**
+     * 必选
+     */
+    boolean required() default false;
+
+    /**
+     * 最小值(如果是字符串就是最小长度)
+     */
+    int min() default -1;
+
+    /**
+     * 最大值(如果是字符串就是最大长度)
+     */
+    int max() default -1;
+
+    /**
+     * 能否更新
+     */
+    boolean canUpdate() default true;
+
+    /**
+     * 排序
+     */
+    int order() default 0;
+}

+ 33 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/Entity.java

@@ -0,0 +1,33 @@
+package com.kym.jdbc.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 实体类映射数据表注解
+ * @author asynll
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface Entity{
+    /**
+     * 表名注释
+     */
+    String comment() default "";
+
+    /**
+     * 映射的表名,强制指定后忽略默认的构造表名方法
+     */
+    String tbName() default "";
+
+    /**
+     * 关联实体类对象
+     */
+    Class<?> clz() default void.class;
+}

+ 28 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/FK.java

@@ -0,0 +1,28 @@
+package com.kym.jdbc.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 外键关联
+ * Foreign Key
+ *
+ * @author yaop
+ */
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface FK {
+    /**
+     * 关联表类对象
+     */
+    Class<?> clz();
+
+    /**
+     * 关联表的外键字段
+     */
+    String tkf() default "id";
+}

+ 9 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/JoinType.java

@@ -0,0 +1,9 @@
+package com.kym.jdbc.annotations;
+
+/**
+ * join类型
+ * build by yaop at 2019/5/2 14:46
+ */
+public enum JoinType {
+    INNER, LEFT, RIGHT
+}

+ 39 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/Many.java

@@ -0,0 +1,39 @@
+package com.kym.jdbc.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 关联一对多
+ *
+ * @author yaop at 2019/1/31 16:44
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface Many {
+    /**
+     * 外键关联表实体类对象
+     */
+    Class<?> te() default void.class;
+
+    /**
+     * 关联表外键字段
+     */
+    String tkf() default "id";
+
+    /**
+     * 主表外键关联字段
+     */
+    String mkf() default "id";
+
+    /**
+     * 字段不查询(优先级高于include字段)
+     */
+    boolean noQuery() default false;
+}

+ 28 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/OP.java

@@ -0,0 +1,28 @@
+package com.kym.jdbc.annotations;
+
+/**
+ * 操作符常量
+ */
+public interface OP {
+
+    String EQ = "=";
+    String LK = "like '%?%'";
+    String LLK = "like '?%'";
+    String RLK = "like '%?'";
+    String IN = "in";
+    /**
+     * json in
+     */
+    String JIN = "JSON_CONTAINS";
+    /**
+     * json 交集
+     */
+    String JINT = "JSON_OVERLAPS";
+    String NIN = "not in";
+    String GT = ">";
+    String GTE = ">=";
+    String LT = "<";
+    String LTE = "<=";
+    String NE = "<>";
+    String OR = "or";
+}

+ 53 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/One.java

@@ -0,0 +1,53 @@
+package com.kym.jdbc.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 关联一对一
+ *
+ * @author: yoap
+ */
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface One {
+
+    /**
+     * 注释
+     * @return
+     */
+    String comment() default "";
+
+    /**
+     * 主表外键
+     */
+    String mkf() default "id";
+
+    /**
+     * 外键关联表实体类对象
+     */
+    Class<?> te() default void.class;
+
+    /**
+     * 被关联表的键
+     */
+    String tkf() default "id";
+    /**
+     * 关联实体类查询的字段
+     */
+    String tf() default "id";
+
+
+
+    boolean noQuery() default false;
+
+    boolean persist() default false;
+
+    JoinType join() default JoinType.LEFT;
+}

+ 22 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/QE.java

@@ -0,0 +1,22 @@
+package com.kym.jdbc.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * 查询的实体类注解
+ * Query Entity
+ * @author: yaop
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface QE {
+    Class<?> clz();
+}

+ 62 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/QF.java

@@ -0,0 +1,62 @@
+package com.kym.jdbc.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 查询字段注解
+ * Query Field
+ *
+ * @author asynll
+ */
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface QF {
+    /**
+     * sql操作符 {@link OP}
+     */
+    String op() default "";
+
+    /**
+     * 自定义sql语句,变量使用?通配占位符
+     */
+    String sql() default "";
+
+    /**
+     * sql循环连接方式,or ,and ,""
+     * @return
+     */
+    String each() default CNT.AND;
+
+    /**
+     * 是否忽略查询
+     */
+    boolean ignore() default false;
+
+    /**
+     * i.有关联查询时表示被关联表查询字段<br>
+     *  ii.无关联查询时表示主表的字段
+     */
+    String tf() default "";
+
+    /**
+     * 主表外键字段
+     */
+    String pkf() default "";
+
+    /**
+     * 被关联表外键字段
+     */
+    String tkf() default "id";
+
+    /**
+     * 查询条件的字段(主表字段、关联表字段)
+     */
+    QueryType qt() default QueryType.ALIGN;
+}

+ 8 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/annotations/QueryType.java

@@ -0,0 +1,8 @@
+package com.kym.jdbc.annotations;
+
+/**
+ * 查询字段的关联类型
+ */
+public enum QueryType {
+    MAIN,ALIGN,IGNORE
+}

+ 16 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/express/DdlSQLExpress.java

@@ -0,0 +1,16 @@
+package com.kym.jdbc.express;
+
+import com.kym.jdbc.SQLEntity;
+
+
+/**
+ * sql表达式 -ddl语句 alter create drop
+ */
+public class DdlSQLExpress implements SQLExpress<SQLEntity> {
+    @Override
+    public SQLEntity toSQL() {
+
+        //TODO
+        return null;
+    }
+}

+ 82 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/express/DeleteSQLExpress.java

@@ -0,0 +1,82 @@
+package com.kym.jdbc.express;
+
+import com.kym.DbUtil;
+import com.kym.jdbc.DbException;
+import com.kym.jdbc.OBuilder;
+import com.kym.jdbc.OBuilder.Where;
+import com.kym.jdbc.SQLEntity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * sql表达式 -delete
+ */
+public class DeleteSQLExpress implements SQLExpress<SQLEntity> {
+
+    private final Logger logger = LoggerFactory.getLogger(DeleteSQLExpress.class);
+
+    /**
+     * insert 的表名
+     */
+    private String table;
+
+    /**
+     * insert的字段名
+     */
+    private List<Where> conditions = new ArrayList<>();
+
+
+    @Override
+    public SQLEntity toSQL() {
+        if (strict && DbUtil.isEmptyOrNull(this.conditions)) {
+            logger.error("delete error, because conditions  is null");
+            throw new DbException("delete error, because conditions  is null");
+        }
+        SQLEntity entity = new SQLEntity();
+        List<Object> params = new ArrayList<>();
+        StringBuilder sbr = new StringBuilder("DELETE FROM ").append(table);
+        if (!DbUtil.isEmptyOrNull(conditions)) {
+            sbr.append(" WHERE ");
+            conditions.forEach(where -> {
+                if(!DbUtil.isEmptyOrNull(where.key)){
+                    sbr.append(getComma()).append(where.key).append(getComma()).append(" ").append(where.op).append(" ");
+                    if (where.op.contains("in") || where.op.contains("not in")) {
+                        sbr.append(" (?) AND ");
+                    } else {
+                        sbr.append(" ? AND ");
+                    }
+                    params.add(where.value);
+                    //拼接SQL
+                }else{
+//TODO
+                }
+
+            });
+        }
+        entity.sql = sbr.toString();
+        entity.parameters = params.toArray();
+        return entity;
+    }
+
+
+    //region delete
+    public SQLEntity deleteById(Class<?> clz, Object id) {
+        table = getTableName(clz, clz.getSimpleName());
+        conditions.add(new Where("id", id));
+        return toSQL();
+    }
+
+
+    public SQLEntity delete(Class<?> clz, OBuilder OBuilder) {
+         table = getTableName(clz, clz.getSimpleName());
+         conditions  = OBuilder.getWheres();
+        return toSQL();
+    }
+
+    //endregion delete
+
+}

+ 133 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/express/InsertSQLExpress.java

@@ -0,0 +1,133 @@
+package com.kym.jdbc.express;
+
+
+import com.kym.DbUtil;
+import com.kym.jdbc.DbException;
+import com.kym.jdbc.SQLEntity;
+import com.kym.jdbc.annotations.DBF;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+
+/**
+ * sql表达式 -insert
+ */
+public class InsertSQLExpress implements SQLExpress<SQLEntity> {
+
+    private final Logger logger = LoggerFactory.getLogger(InsertSQLExpress.class);
+
+    /**
+     * insert 的表名
+     */
+    private String table;
+
+    /**
+     * insert的字段名
+     */
+    private final List<String> columns = new ArrayList<>();
+
+    /**
+     * insert的值
+     */
+    private final List<Object> values = new ArrayList<>();
+
+    @Override
+    public SQLEntity toSQL() {
+        if (DbUtil.isEmptyOrNull(this.columns)) {
+            logger.error("columns is null");
+            throw new DbException("insert parse error because columns is null");
+        }
+        SQLEntity entity = new SQLEntity();
+        StringBuilder sbr = new StringBuilder("INSERT INTO ").append(table).append(" (");
+        String collect = columns.stream().map(k -> getComma() + k + getComma()).collect(Collectors.joining(","));
+        sbr.append(collect).append(") VALUES (");
+        String collect1 = columns.stream().map(k -> "?").collect(Collectors.joining(","));
+        sbr.append(collect1).append(")");
+        entity.sql = sbr.toString();
+        entity.parameters = values.toArray();
+        return entity;
+    }
+
+
+    /**
+     * --------------insert method begin ---------------
+     */
+    public SQLEntity insertSelective(Object o, List<String> exclude) {
+        return insertWithCheck(o, true, false, exclude);
+    }
+
+    public SQLEntity insert(Object o, List<String> exclude) {
+        return insertWithCheck(o, false, false, exclude);
+    }
+
+    public SQLEntity insertWithGenKey(Object o, List<String> exclude) {
+        return insertWithCheck(o, false, true, exclude);
+    }
+
+    /***
+     * 插入方法
+     * @param o 插入实体对象
+     * @param ignoreNull 忽略空字段
+     * @param withAutoIncreamentKey 对象是否已赋值自增主键
+     * @param exclude 忽略字段
+     * @return
+     */
+    private SQLEntity insertWithCheck(Object o, boolean ignoreNull, boolean withAutoIncreamentKey, List<String> exclude) {
+        table = getTableName(o.getClass(), o.getClass().getSimpleName());
+
+        //自带主键
+        if (withAutoIncreamentKey) {
+            columns.add("id");
+            try {
+                values.add(DbUtil.getFieldValue(o, DbUtil.getField(o, "id")));
+            } catch (NoSuchFieldException e) {
+                logger.error("SQL2Helper ERR# insert with primary key,but without a value!!");
+                throw new IllegalStateException("no primary key value exist.");
+            }
+        }
+        exclude.add("id");
+        Set<Field> fields = DbUtil.getEntityFields(o.getClass());
+        for (Field field : fields) {
+            if (DbUtil.isUsageField(field)) {
+                String fieldName = field.getName();
+                if (!DbUtil.isEmptyOrNull(exclude)) {
+                    //忽略属性
+                    if (exclude.contains(fieldName)) {
+                        continue;
+                    }
+                }
+                Type type = field.getGenericType();
+                DBF DBF = field.getAnnotation(DBF.class);
+                if (null != DBF) {
+                    Object value = DbUtil.getFieldValue(o, field);
+                    if (ignoreNull) {
+                        if (null != value) {
+                            //TODO 校验值
+                            checkFieldValue(field, value, true);
+                            columns.add(fieldName);
+                            values.add(value);
+                        }
+                    } else {
+                        if (type == Date.class && null == value) {
+                            continue;
+                        }
+                        columns.add(fieldName);
+                        values.add(value);
+                    }
+                }
+            }
+        }
+        return toSQL();
+    }
+
+    //endregion insert
+
+}

+ 265 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/express/SQLExpress.java

@@ -0,0 +1,265 @@
+package com.kym.jdbc.express;
+
+import com.kym.DbUtil;
+import com.kym.jdbc.OBuilder;
+import com.kym.jdbc.annotations.DBF;
+import com.kym.jdbc.annotations.Entity;
+import com.kym.jdbc.template.JdbcHelper;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * sql表达式构造
+ * <p>
+ *     mysql\oracle\postgresql\sqlserver\db2\h2\sqlite\人大金仓\达梦\
+ * </p>
+ */
+public interface SQLExpress<T> {
+
+    T toSQL();
+
+
+    default String getComma() {
+        return "\"";
+    }
+
+    boolean strict = false;
+
+
+    default void appendValue(StringBuilder sbr, Type type, Object value, List<Object> parameters) {
+        sbr.append("?").append(",");
+        parameters.add(value);
+    }
+
+    default void appendValue(StringBuilder sbr, Object value, List<Object> parameters) {
+        sbr.append(" ? ");
+        parameters.add(value);
+    }
+
+
+    default String getTableName(Class<?> clz, String tbName) {
+        if (clz.isAnnotationPresent(Entity.class)) {
+            Entity entity = clz.getAnnotation(Entity.class);
+            if (!DbUtil.isEmptyOrNull(entity.tbName())) {
+                return DbUtil.getColumnName(entity.tbName());
+            }
+        } else if (clz.getSuperclass().isAnnotationPresent(Entity.class)) {
+            Class<?> superClz = clz.getSuperclass();
+            Entity entity = superClz.getAnnotation(Entity.class);
+            if (!DbUtil.isEmptyOrNull(entity.tbName())) {
+                return DbUtil.getColumnName(entity.tbName());
+            }
+        }
+        if (tbName.endsWith("Info")) {
+            tbName = tbName.substring(0, tbName.length() - 4);
+        }
+        return JdbcHelper.tbPrefix + DbUtil.getColumnName(tbName);
+    }
+
+    default String getTableName(Entity anno) {
+        String tbName = anno.tbName();
+        if (!DbUtil.isEmptyOrNull(tbName)) {
+            return DbUtil.getColumnName(tbName);
+        }
+        Class<?> clz = anno.clz();
+       /* if (tbName.endsWith("Info")) {
+            tbName = tbName.substring(0, tbName.length() - 4);
+        }*/
+        return JdbcHelper.tbPrefix + DbUtil.getColumnName(clz.getSimpleName());
+    }
+
+
+    /**
+     * whereSql拼接,需要注意json类型处理
+     *
+     * @param whereSql
+     * @param val
+     * @param tbAlias
+     * @param parameters
+     * @return
+     */
+    default String appendWhereSql(String whereSql, List<Object> val, String tbAlias, List<Object> parameters) {
+//        parameters.addAll(val);
+        if (!DbUtil.isEmptyOrNull(whereSql)) {
+            if (!DbUtil.isEmptyOrNull(val)) {
+                if (whereSql.contains("?")) {
+                    String regex = "\\?";
+                    for (Object o : val) {
+                        if (o.getClass() == String.class) {
+                            whereSql = whereSql.replaceFirst(regex, "'" + DbUtil.injectDefend(o.toString()) + "'");
+                        } else if (o.getClass() == Date.class) {
+                            whereSql = whereSql.replaceFirst(regex, "'" + (new Timestamp(((Date) o).getTime())) + "'");
+                        } else {
+                            whereSql = whereSql.replaceFirst(regex, DbUtil.injectDefend(o.toString()));
+                        }
+                    }
+                    while (whereSql.contains("?")) {
+                        for (Object o : val) {
+                            if (o.getClass() == String.class) {
+                                whereSql = whereSql.replaceFirst(regex, "'" + DbUtil.injectDefend(o.toString()) + "'");
+                            } else if (o.getClass() == Date.class) {
+                                whereSql = whereSql.replaceFirst(regex, "'" + (new Timestamp(((Date) o).getTime())) + "'");
+                            } else {
+                                whereSql = whereSql.replaceFirst(regex, DbUtil.injectDefend(o.toString()));
+                            }
+                        }
+                    }
+                } else {
+                    if (val.size() == 1 && val.get(0) instanceof Boolean) {
+                        if (!(Boolean) val.get(0)) {
+                            return null;
+                        }
+                    }
+                }
+            }
+            String[] splits = whereSql.split(" AND ");
+            boolean containsComma = false;
+            if (splits.length > 1) {
+                StringBuilder sbd = new StringBuilder();
+                for (int i = 0; i < splits.length; i++) {
+                    if (splits[i].startsWith("(")) {
+                        containsComma = true;
+                        sbd.append("(");
+                        if (!DbUtil.isEmptyOrNull(tbAlias)) {
+                            sbd.append(tbAlias).append(".");
+                        }
+                        sbd.append(splits[i].trim().substring(1));
+                    } else {
+                        if (!DbUtil.isEmptyOrNull(tbAlias)) {
+                            sbd.append(tbAlias).append(".");
+                        }
+                        sbd.append(splits[i].trim());
+                    }
+                    if (i != splits.length - 1) {
+                        sbd.append(" AND ");
+                    }
+                }
+                whereSql = sbd.toString();
+            }
+            String[] splits1 = whereSql.split(" OR ");
+            if (splits1.length > 1) {
+                StringBuilder sbd = new StringBuilder();
+                for (int i = 0; i < splits1.length; i++) {
+                    if (splits1[i].startsWith("(")) {
+                        if (!containsComma) {
+                            sbd.append("(");
+                            if (!DbUtil.isEmptyOrNull(tbAlias)) {
+                                sbd.append(tbAlias).append(".");
+                            }
+                            sbd.append(splits1[i].trim().substring(1));
+                        }
+                    } else {
+                        if (!DbUtil.isEmptyOrNull(tbAlias)) {
+                            sbd.append(tbAlias).append(".");
+                        }
+                        sbd.append(splits1[i].trim());
+                    }
+                    if (i != splits1.length - 1) {
+                        sbd.append(" OR ");
+                    }
+                }
+                whereSql = sbd.toString();
+            }
+        }
+        return whereSql;
+    }
+
+
+    /**
+     * 拼接Builder 条件
+     */
+    default void appendBuilderWhere(StringBuilder sbr, OBuilder.Where wh, String tbAlias, List<Object> parameters) {
+        if (DbUtil.isEmptyOrNull(wh.sql)) {
+            if (!DbUtil.isEmptyOrNull(tbAlias)) {
+                sbr.append(tbAlias).append(".");
+            }
+            sbr.append(getComma()).append(DbUtil.getColumnName(wh.key)).append(getComma()).append(" ").append(wh.op);
+            String op = wh.op;
+            Object value = wh.value;
+            if ("in".equals(op.trim()) || "not in".equals(op.trim())) {
+                sbr.append("(");
+                for (int i = 0; i < Array.getLength(value); i++) {
+                    Object v = Array.get(value, i);
+                    sbr.append("?");
+                    parameters.add(v);
+                   /* if (v.getClass() == String.class) {
+                        sbr.append("'").append(DbUtil.injectDefend(v.toString())).append("'");
+                    } else {
+                        sbr.append(v);
+                    }*/
+                    if (i != Array.getLength(value) - 1) {
+                        sbr.append(",");
+                    }
+                }
+                sbr.append(")");
+            } else {
+                //转换取值方式
+                sbr.append("?");
+                parameters.add(value);
+           /*     if (value.getClass() == String.class) {
+                    sbr.append("'").append(value).append("'");
+                } else if (wh.value.getClass() == Date.class) {
+                    sbr.append("'").append(new Timestamp(((Date) value).getTime())).append("'");
+                } else {
+
+//                    sbr.append(value);
+                }*/
+            }
+        } else {
+            //别名判断
+            sbr.append(" (");
+            sbr.append(appendWhereSql(wh.sql, wh.sqlValues, tbAlias, parameters));
+            sbr.append(") ");
+        }
+    }
+
+
+    /**
+     * 校验字段的值
+     */
+    default void checkFieldValue(Field field, Object value, boolean insertCheck) {
+        if (!field.isAnnotationPresent(DBF.class)) {
+            return;
+        }
+        DBF dbf = field.getAnnotation(DBF.class);
+
+        //插入SQL校验必填
+        if (insertCheck) {
+            if (dbf.required() && DbUtil.isEmptyOrNull(value)) {
+                throw new IllegalArgumentException("【" + dbf.comment() + "】取值不能为空");
+            }
+        }
+        int min = dbf.min();
+        int max = dbf.max();
+        if (dbf.min() > 0) {
+            if (value instanceof Integer || value instanceof Short || value instanceof Double || value instanceof Long) {
+                if (BigDecimal.valueOf(min).compareTo(new BigDecimal(String.valueOf(value))) > 0) {
+                    throw new IllegalArgumentException("【" + dbf.comment() + "】最小值为" + min);
+                }
+                if (max > 0 && BigDecimal.valueOf(max).compareTo(new BigDecimal(value.toString())) < 0) {
+                    throw new IllegalArgumentException("【" + dbf.comment() + "】最大值为" + max);
+                }
+            } else if (value instanceof String || value instanceof Character) {
+                if (!DbUtil.isEmptyOrNull(value)) {
+                    int length = value.toString().length();
+                    if (min > length) {
+                        throw new IllegalArgumentException("【" + dbf.comment() + "】最小长度为" + min);
+                    }
+                    if (max > 0 && max < length) {
+                        throw new IllegalArgumentException("【" + dbf.comment() + "】最大长度为" + max);
+                    }
+                }
+            }
+        }
+
+
+    }
+
+}

+ 58 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/express/SelectSQLExpress.java

@@ -0,0 +1,58 @@
+package com.kym.jdbc.express;
+
+import com.kym.jdbc.OBuilder.Where;
+import com.kym.jdbc.SQLEntity;
+import com.kym.jdbc.annotations.JoinType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * sql表达式 -select
+ */
+public class SelectSQLExpress implements SQLExpress<SQLEntity> {
+    private final Logger logger = LoggerFactory.getLogger(SelectSQLExpress.class);
+
+
+    private String table;
+    /**
+     * 表名
+     */
+    private Map<String, String> tableAliasMap;
+
+    /**
+     * 字段名
+     */
+    private final List<String> columns = new ArrayList<>();
+
+
+    private final List<Map<String, JoinType>> joins = new ArrayList<>();
+
+    /**
+     * 条件
+     */
+    private final List<Where> wheres = new ArrayList<>();
+
+    private final List<Where> havings = new ArrayList<>();
+
+    /**
+     * 排序
+     */
+    private final List<String> orders = new ArrayList<>();
+
+    /**
+     * 分组
+     */
+    private final List<String> groups = new ArrayList<>();
+
+
+    @Override
+    public SQLEntity toSQL() {
+        //TODO
+        return null;
+    }
+}

+ 15 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/express/UpdateSQLExpress.java

@@ -0,0 +1,15 @@
+package com.kym.jdbc.express;
+
+import com.kym.jdbc.SQLEntity;
+
+
+/**
+ * sql表达式 -update
+ */
+public class UpdateSQLExpress implements SQLExpress<SQLEntity> {
+    @Override
+    public SQLEntity toSQL() {
+        //TODO
+        return null;
+    }
+}

+ 10 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/lambda/ColumnFunc.java

@@ -0,0 +1,10 @@
+package com.kym.jdbc.lambda;
+
+import java.io.Serializable;
+
+
+@FunctionalInterface
+public interface ColumnFunc<T, R> extends Serializable {
+    R apply(T t);
+}
+

+ 48 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/lambda/DefaultLambdaParser.java

@@ -0,0 +1,48 @@
+package com.kym.jdbc.lambda;
+
+
+import com.kym.DbUtil;
+
+import java.lang.invoke.SerializedLambda;
+import java.lang.reflect.Method;
+
+
+/**
+ * 解析字段列名
+ */
+public class DefaultLambdaParser {
+
+    public static <T> String getPropertyName(ColumnFunc<T, ?> property) {
+        if (property == null) {
+            return null;
+        }
+        try {
+            Method declaredMethod = property.getClass().getDeclaredMethod("writeReplace");
+            declaredMethod.setAccessible(Boolean.TRUE);
+            SerializedLambda serializedLambda = (SerializedLambda) declaredMethod.invoke(property);
+
+
+//            //Class.method.method....
+//            if (serializedLambda.getImplMethodKind() == MethodHandleInfo.REF_invokeStatic) {
+//                return getPropertyNameByInvokeStatic(property.getClass().getClassLoader(), serializedLambda);
+//            }
+            //Class::method
+            return getPropertyNameByInvokeVirtual(serializedLambda);
+
+        } catch (ReflectiveOperationException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static String getPropertyNameByInvokeVirtual(SerializedLambda serializedLambda) {
+        String method = serializedLambda.getImplMethodName();
+
+        String attr = null;
+        if (method.startsWith("get")) {
+            attr = method.substring(3);
+        } else {
+            attr = method.substring(2);
+        }
+        return DbUtil.firstToLowerCase(attr);
+    }
+}

+ 1948 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/template/JdbcExecute.java

@@ -0,0 +1,1948 @@
+package com.kym.jdbc.template;
+
+import com.kym.DbUtil;
+import com.kym.JacksonUtil;
+import com.kym.jdbc.BasicQuery;
+import com.kym.jdbc.Bean;
+import com.kym.jdbc.IHandler;
+import com.kym.jdbc.OBuilder;
+import com.kym.jdbc.ResultHandler;
+import com.kym.jdbc.SQLEntity;
+import com.kym.jdbc.annotations.DBF;
+import com.kym.jdbc.annotations.Entity;
+import com.kym.jdbc.annotations.Many;
+import com.kym.jdbc.annotations.One;
+import com.kym.jdbc.annotations.QE;
+import com.kym.jdbc.lambda.ColumnFunc;
+import com.kym.jdbc.lambda.DefaultLambdaParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.dao.DataAccessException;
+import org.springframework.jdbc.core.BatchPreparedStatementSetter;
+import org.springframework.jdbc.core.CallableStatementCallback;
+import org.springframework.jdbc.core.CallableStatementCreator;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.PreparedStatementCreator;
+import org.springframework.jdbc.core.ResultSetExtractor;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.jdbc.support.KeyHolder;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+
+/**
+ * sql处理封装(jdbcTemplate版本)
+ *
+ * @author yaop
+ */
+public class JdbcExecute implements IHandler {
+    private final Logger logger = LoggerFactory.getLogger(JdbcExecute.class);
+
+    private List<String> ignores;
+
+    private JdbcTemplate jdbcTemplate;
+
+    private JdbcHelper helper;
+
+    public static final int FUNC_SUM = 1;
+    public static final int FUNC_AVG = 2;
+    public static final int FUNC_MIN = 3;
+    public static final int FUNC_MAX = 4;
+    public static final int FUNC_COUNT = 5;
+
+    public void setJdbcTemplate(JdbcTemplate jdbcTemplate, int dialect) {
+        this.jdbcTemplate = jdbcTemplate;
+        helper = new JdbcHelper();
+        helper.setDialect(dialect);
+        ignores = new ArrayList<>();
+        ignores.add("richId");
+        ignores.add("rich1Id");
+        ignores.add("rich2Id");
+    }
+
+    public int getJdbcDialect() {
+        return helper.getDialect();
+    }
+
+    protected void beforeInsert(Object bean) {
+        if (null == bean) {
+            throw new IllegalStateException("no bean to insert into db");
+        }
+    }
+
+    protected void beforeUpdate(Object bean) {
+        if (null == bean) {
+            throw new IllegalStateException("no bean update to db");
+        }
+    }
+
+    protected void beforeUpdate(Object bean, OBuilder OBuilder) {
+        if (null == bean) {
+            throw new IllegalStateException("no bean update to db");
+        }
+    }
+
+    protected void beforeDelete(Class<?> clz, OBuilder OBuilder) {
+        if (null == clz) {
+            throw new IllegalStateException("no bean to delete from db");
+        }
+    }
+
+    protected void beforeDelete(Class<?> clz, Object id) {
+        if (null == clz) {
+            throw new IllegalStateException("no bean to delete from db");
+        }
+    }
+
+    protected void beforeSelect(BasicQuery query) {
+        if (null == query) {
+            throw new IllegalStateException("no bean to query from db");
+        }
+    }
+
+    protected void beforeSelect(Class<?> clz, OBuilder OBuilder) {
+        if (null == clz) {
+            throw new IllegalStateException("no bean to query from db");
+        }
+    }
+
+
+    private List<String> addExcludeInSelectList(Class<?> clz, String... excludes) {
+        List<String> exclude = new ArrayList<>();
+        if (!DbUtil.isEmptyOrNull(excludes)) {
+            exclude.addAll(Arrays.asList(excludes));
+        }
+        Field[] fields = clz.getFields();
+        for (Field field : fields) {
+            String fieldName = field.getName();
+            Type type = field.getGenericType();
+            if (type == List.class || type == Set.class) {
+                exclude.add(fieldName);
+            }
+            if (ignores.contains(fieldName)) {
+                exclude.add(fieldName);
+                exclude.add(fieldName.replace("Id", "Content"));
+            }
+        }
+        return exclude;
+    }
+
+    private List<String> addExcludeInSelectList(BasicQuery query, String... excludes) {
+        List<String> exclude = new ArrayList<>();
+        if (!DbUtil.isEmptyOrNull(excludes)) {
+            exclude.addAll(Arrays.asList(excludes));
+        }
+
+        Field[] fields = query.getClass().getAnnotation(QE.class).clz().getFields();
+        for (Field field : fields) {
+            String fieldName = field.getName();
+            Type type = field.getGenericType();
+            if (type == List.class || type == Set.class) {
+                exclude.add(fieldName);
+            }
+            if (ignores.contains(fieldName)) {
+                exclude.add(fieldName);
+                exclude.add(fieldName.replace("Id", "Content"));
+            }
+        }
+        return exclude;
+    }
+
+
+    //region select
+
+    /**
+     * 分页查询
+     */
+    public final Map<String, Object> selectPageList(BasicQuery query) {
+        beforeSelect(query);
+        Map<String, Object> map = new HashMap<>(2);
+        int pageSize = query.pageSize;
+        long count = selectCount(query);
+        if (0 == count) {
+            map.put("list", Collections.emptyList());
+        } else {
+            query.pageSize = pageSize;
+            map.put("list", selectList(query));
+        }
+        map.put("count", count);
+        return map;
+    }
+
+
+    /**
+     * 分页查询
+     */
+    public final Map<String, Object> selectPageList(Class<?> clz, OBuilder OBuilder) {
+        beforeSelect(clz, OBuilder);
+        Map<String, Object> map = new HashMap<>(2);
+        long count = selectCount(clz, OBuilder);
+        if (0 == count) {
+            map.put("list", Collections.emptyList());
+        } else {
+            map.put("list", selectList(clz, OBuilder));
+        }
+        map.put("count", count);
+        return map;
+    }
+
+    /**
+     * 分页查询
+     */
+    public final <T> Bean<T> selectPageBean(BasicQuery query) {
+        Bean<T> pageBean = new Bean<>();
+        pageBean.pageIndex = query.pageIndex;
+        pageBean.pageSize = query.pageSize;
+        beforeSelect(query);
+        long count = selectCount(query);
+        pageBean.count = count;
+        if (0 == count) {
+            pageBean.list = Collections.emptyList();
+        } else {
+            query.pageSize = pageBean.pageSize;
+            pageBean.list = selectList(query);
+        }
+        return pageBean;
+    }
+
+    /**
+     * 分页查询
+     */
+    public final <T> Bean<T> selectPageBean(Class<T> clz, OBuilder OBuilder) {
+        Bean<T> pageBean = new Bean<>();
+        beforeSelect(clz, OBuilder);
+        long count = selectCount(clz, OBuilder);
+        pageBean.count = count;
+        pageBean.pageIndex = OBuilder.limitStart();
+        pageBean.pageSize = OBuilder.limitDelta();
+        if (0 == count) {
+            pageBean.list = Collections.emptyList();
+        } else {
+            pageBean.list = selectList(clz, OBuilder);
+        }
+        return pageBean;
+    }
+
+    public final <T> List<T> selectListExclude(Class<T> clz, String... exclude) {
+        return selectListExclude(clz, OBuilder.build(), exclude);
+    }
+
+    public final <T> List<T> selectList(Class<T> clz, String... include) {
+        return selectList(clz, OBuilder.build(), include);
+    }
+
+    @SuppressWarnings("unchecked")
+    public final <T> List<T> selectListExclude(BasicQuery query, String... exclude) {
+        beforeSelect(query);
+        SQLEntity entity = helper.selectListByQuery(query, null, addExcludeInSelectList(query, exclude));
+        Class<T> clz = (Class<T>) query.getClass().getAnnotation(QE.class).clz();
+        return queryForList(entity, clz);
+    }
+
+
+    /**
+     * 查询列表数据<p>
+     * <b>注意:query查询默认分页20</b></p>
+     */
+    @SuppressWarnings("unchecked")
+    public final <T> List<T> selectList(BasicQuery query, String... include) {
+        beforeSelect(query);
+        SQLEntity entity = helper.selectListByQuery(query, new ArrayList<>(Arrays.asList(include)), addExcludeInSelectList(query));
+        Class<T> clz = (Class<T>) query.getClass().getAnnotation(QE.class).clz();
+        return queryForList(entity, clz);
+    }
+
+    /**
+     * TODO 简单查询不做关联
+     */
+    @SuppressWarnings("unchecked")
+    public final <T> List<T> selectList(BasicQuery query, boolean simple, String... include) {
+        beforeSelect(query);
+        SQLEntity entity = helper.selectListByQuery(query, new ArrayList<>(Arrays.asList(include)), addExcludeInSelectList(query));
+        Class<T> clz = (Class<T>) query.getClass().getAnnotation(QE.class).clz();
+        return queryForList(entity, clz);
+    }
+
+    private <T> List<T> queryForList(SQLEntity entity, Class<T> clz) {
+        long start = System.currentTimeMillis();
+        if (logger.isInfoEnabled()) {
+            logger.info("SQL>>>:\n{} \nP:{}", entity.sql, JacksonUtil.toJSONString(entity.parameters));
+        }
+        List<T> result = jdbcTemplate.query(connection -> {
+            PreparedStatement ps = connection.prepareStatement(entity.sql);
+            DbUtil.setParameter(ps, entity.parameters);
+            return ps;
+        }, (rs, i) -> map2Object(clz, rs));
+
+
+        if (logger.isDebugEnabled()) {
+            logger.info("SQL<<< cost:{}ms", System.currentTimeMillis() - start);
+        }
+        return result;
+    }
+
+
+    public final <T> List<T> selectListExclude(Class<T> clz, OBuilder oBuilder, String... exclude) {
+        beforeSelect(clz, oBuilder);
+        SQLEntity entity = helper.selectListByWhere(clz, oBuilder, null, addExcludeInSelectList(clz, exclude), true);
+        return queryForList(entity, clz);
+    }
+
+    public final <T> List<T> selectList(Class<T> clz, OBuilder oBuilder, String... include) {
+        beforeSelect(clz, oBuilder);
+        SQLEntity entity = helper.selectListByWhere(clz, oBuilder, new ArrayList<>(Arrays.asList(include)), addExcludeInSelectList(clz), true);
+        return queryForList(entity, clz);
+    }
+
+
+    public final <T> List<T> selectSimpleList(Class<T> clz, OBuilder oBuilder) {
+        beforeSelect(clz, oBuilder);
+        List<String> includes = new ArrayList<>();
+        includes.add("id");
+        includes.add("name");
+        SQLEntity entity = helper.selectListByWhere(clz, oBuilder, includes, addExcludeInSelectList(clz), true);
+        return queryForList(entity, clz);
+    }
+
+
+    /**
+     * jdbc查询
+     *
+     * @param sql
+     * @param parameters
+     * @return 未封装的列表
+     */
+    public final List<Map<String, Object>> selectListForMap(String sql, Object... parameters) {
+        long start = System.currentTimeMillis();
+        if (logger.isInfoEnabled()) {
+            logger.info("SQL>>>:\n{} \nP:{}", sql, JacksonUtil.toJSONString(parameters));
+        }
+        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql, parameters);
+
+        if (logger.isDebugEnabled()) {
+            logger.info("SQL<<< cost:{}ms", System.currentTimeMillis() - start);
+        }
+        return maps;
+    }
+
+    public final List<Map<String, Object>> selectListForMapV2(String sql, Object... parameters) {
+//        List<Map<String, Object>> result = new ArrayList<>();
+        long start = System.currentTimeMillis();
+        if (logger.isInfoEnabled()) {
+            logger.info("SQL>>>:\n{} \nP:{}", sql, JacksonUtil.toJSONString(parameters));
+        }
+        List<Map<String, Object>> result = jdbcTemplate.query(new PreparedStatementCreator() {
+            @Override
+            public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
+                PreparedStatement preparedStatement = connection.prepareStatement(sql);
+                DbUtil.setParameter(preparedStatement, parameters);
+                return preparedStatement;
+            }
+        }, new RowMapper<Map<String, Object>>() {
+            @Override
+            public Map<String, Object> mapRow(ResultSet rs, int i) throws SQLException {
+                return resultToOneMap(rs);
+            }
+        });
+
+        if (logger.isDebugEnabled()) {
+            logger.info("SQL<<< cost:{}ms", System.currentTimeMillis() - start);
+        }
+        return result;
+    }
+
+
+    public final <T> List<T> selectList(String sql, ResultHandler<T> handler, Object... parameters) {
+        long start = System.currentTimeMillis();
+        if (logger.isInfoEnabled()) {
+            logger.info("selectList 11 SQL>>>:\n{} \nP:{}", sql, JacksonUtil.toJSONString(parameters));
+        }
+        List<T> result = jdbcTemplate.query(new PreparedStatementCreator() {
+            @Override
+            public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
+                PreparedStatement preparedStatement = connection.prepareStatement(sql);
+                DbUtil.setParameter(preparedStatement, parameters);
+                return preparedStatement;
+            }
+        }, new RowMapper<T>() {
+            @Override
+            public T mapRow(ResultSet rs, int i) throws SQLException {
+                try {
+                    return handler.handle(resultToOneMap(rs));
+                } catch (Exception e) {
+                    logger.error("data convert error," + e.getMessage(), e);
+                }
+                return null;
+            }
+        });
+
+        if (logger.isDebugEnabled()) {
+            logger.info("SQL<<< cost:{}ms", System.currentTimeMillis() - start);
+        }
+        return result;
+    }
+
+    public final <T> List<T> selectList(String sql, Class<T> clz, Object... parameters) {
+        long start = System.currentTimeMillis();
+        if (logger.isInfoEnabled()) {
+            logger.info("selectList 22 SQL>>>:\n{} \nP:{}", sql, JacksonUtil.toJSONString(parameters));
+        }
+
+        List<T> result = jdbcTemplate.query(new PreparedStatementCreator() {
+            @Override
+            public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
+                PreparedStatement preparedStatement = connection.prepareStatement(sql);
+                DbUtil.setParameter(preparedStatement, parameters);
+                return preparedStatement;
+            }
+        }, new RowMapper<T>() {
+            @Override
+            public T mapRow(ResultSet rs, int i) throws SQLException {
+                Map<String, Object> map = resultToOneMap(rs);
+                try {
+                    return map2Object(clz, map);
+                } catch (Exception e) {
+                    logger.error("data convert error," + e.getMessage(), e);
+                }
+                return null;
+            }
+        });
+
+/*        List<T> result = jdbcTemplate.query(connection -> {
+            PreparedStatement preparedStatement = connection.prepareStatement(sql);
+            DbUtil.setParameter(preparedStatement, parameters);
+            return preparedStatement;
+        }, (rs, i) -> {
+            Map<String, Object> map = resultToMap(rs);
+            try {
+                return map2Object(clz, map);
+            } catch (Exception e) {
+                logger.error("data convert error," + e.getMessage(), e);
+            }
+            return null;
+        });*/
+
+        if (logger.isDebugEnabled()) {
+            logger.info("SQL<<< cost:{}ms", System.currentTimeMillis() - start);
+        }
+        return result;
+    }
+
+
+    public final Map<Integer, String> call(String sql, Object... parameters) {
+        int parameterLength = parameters.length;
+        int count = 0;
+        char[] chars = sql.toCharArray();
+        for (char aChar : chars) {
+            if (aChar == '?') {
+                count++;
+            }
+        }
+        long start = System.currentTimeMillis();
+        if (logger.isInfoEnabled()) {
+            logger.info("SQL>>>:\n{} \nP:{}", sql, JacksonUtil.toJSONString(parameters));
+        }
+        int finalCount = count;
+        Map<Integer, String> data = (Map<Integer, String>) jdbcTemplate.execute(new CallableStatementCreator() {
+            @Override
+            public CallableStatement createCallableStatement(Connection conn) throws SQLException {
+                CallableStatement cs = conn.prepareCall(sql);
+                DbUtil.setParameter(cs, parameters);
+                //注册输出参数的类型
+                if (finalCount > 0 && finalCount > parameterLength) {
+                    for (int j = parameterLength + 1; j <= finalCount; j++) {
+                        cs.registerOutParameter(j, Types.VARCHAR);//
+                    }
+                }
+                return cs;
+            }
+        }, new CallableStatementCallback<Map<Integer, String>>() {
+            @Override
+            public Map<Integer, String> doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
+                cs.execute();
+                Map<Integer, String> result = new HashMap<>();
+                if (finalCount > 0 && finalCount > parameterLength) {
+                    for (int j = parameterLength + 1; j <= finalCount; j++) {
+                        cs.registerOutParameter(j, Types.VARCHAR);//
+                        result.put(j, cs.getString(j));
+                    }
+                }
+                return result;// 获取输出参数的值
+            }
+        });
+        if (logger.isDebugEnabled()) {
+            logger.info("SQL<<< cost:{}ms", System.currentTimeMillis() - start);
+        }
+        return data;
+    }
+
+
+    public final Object selectObject(String sql, Object... parameters) {
+        SQLEntity entity = helper.selectOne(sql, parameters);
+        Map<String, Object> m = queryForMap(entity);
+        if (!DbUtil.isEmptyOrNull(m)) {
+            if (m.size() != 1) {
+                throw new IllegalStateException("SQL ERROR#" + sql);
+            }
+            Optional<Object> optional = m.values().stream().findFirst();
+            return optional.get();
+        }
+        return null;
+    }
+
+    /**
+     * 仅适用于查询单一字段的集合
+     */
+    public final List<Object> selectObjects(String sql, Object... parameters) {
+        SQLEntity entity = helper.selectList(sql, parameters);
+        List<Map> rs = queryForList(entity, Map.class);
+        List<Object> result = new ArrayList<>();
+        if (!DbUtil.isEmptyOrNull(rs)) {
+            rs.forEach(item -> {
+                Set<Map.Entry<String, Object>> entrys = item.entrySet();
+                for (Map.Entry<String, Object> entry : entrys) {
+                    result.add(entry.getValue());
+                }
+            });
+
+        }
+     /*   jdbcTemplate.query(connection -> {
+            PreparedStatement ps = connection.prepareStatement(entity.sql);
+            DbUtil.setParameter(ps, entity.parameters);
+            return ps;
+        }, resultSet -> {
+            Map<String, Object> m = resultToMap(resultSet);
+
+        });*/
+        return result;
+    }
+
+
+    public final Integer selectInteger(String sql, Object... parameters) {
+        Object object = selectObject(sql, parameters);
+        if (null != object) {
+            return (int) object;
+        } else {
+            return 0;
+        }
+    }
+
+    public final Long selectLong(String sql, Object... parameters) {
+        Object object = selectObject(sql, parameters);
+        if (null != object) {
+            return (long) object;
+        } else {
+            return 0L;
+        }
+    }
+
+    private Map<String, Object> queryForMap(SQLEntity entity) {
+        long start = System.currentTimeMillis();
+        if (logger.isInfoEnabled()) {
+            logger.info("SQL>>>:\n{} \nP:{}", entity.sql, JacksonUtil.toJSONString(entity.parameters));
+        }
+        //处理json函数
+        Map<String, Object> result = jdbcTemplate.query(new PreparedStatementCreator() {
+            @Override
+            public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
+                PreparedStatement ps = connection.prepareStatement(entity.sql);
+                DbUtil.setParameter(ps, entity.parameters);
+                return ps;
+            }
+        }, new ResultSetExtractor<Map<String, Object>>() {
+            @Override
+            public Map<String, Object> extractData(ResultSet resultSet) throws SQLException, DataAccessException {
+                return extractResultToOneMap(resultSet);
+            }
+        });
+
+        if (logger.isDebugEnabled()) {
+            logger.info("SQL<<< cost:{}ms", System.currentTimeMillis() - start);
+        }
+        return result;
+    }
+
+    public final <T> double selectFunc(int funcType, Class<?> entityClz, ColumnFunc<T, ?> keyExtractor, OBuilder OBuilder) {
+        String func = switch (funcType) {
+            case FUNC_AVG -> "avg";
+            case FUNC_COUNT -> "count";
+            case FUNC_MIN -> "min";
+            case FUNC_MAX -> "max";
+            case FUNC_SUM -> "sum";
+            default -> throw new IllegalArgumentException("illegal argument for func type#:" + funcType);
+        };
+        func += "(" + getColumnName(DefaultLambdaParser.getPropertyName(keyExtractor)) + ")";
+        SQLEntity entity = helper.selectFunc(entityClz, func, OBuilder);
+        Map<String, Object> m = queryForMap(entity);
+        if (!DbUtil.isEmptyOrNull(m)) {
+            if (m.size() != 1) {
+                throw new IllegalStateException("SQL ERROR# result count is not equal one!!!");
+            }
+            Collection<Object> values = m.values();
+            if (DbUtil.isEmptyOrNull(values)) {
+                return 0d;
+            }
+            Optional<Object> optional = values.stream().filter(k -> !DbUtil.isEmptyOrNull(k)).findFirst();
+            if (optional.isPresent()) {
+                Object obj = optional.get();
+                if (obj instanceof BigDecimal) {
+                    return ((BigDecimal) obj).doubleValue();
+                } else {
+                    return Double.parseDouble(obj.toString());
+                }
+            }
+        }
+        return 0d;
+    }
+
+    public final List<Object> selectDistinct(Class<?> clz, String key, OBuilder oBuilder) {
+        SQLEntity entity = helper.selectDistinct(clz, "DISTINCT(" + getColumnName(key) + ")", oBuilder);
+        List<Map> resultList = queryForList(entity, Map.class);
+        List<Object> objects = new ArrayList<>();
+        if (!DbUtil.isEmptyOrNull(resultList)) {
+            for (Map<String, Object> map : resultList) {
+                if (!DbUtil.isEmptyOrNull(map)) {
+                    Set<Map.Entry<String, Object>> entrys = map.entrySet();
+                    for (Map.Entry<String, Object> entry : entrys) {
+                        objects.add(entry.getValue());
+                        break;
+                    }
+                }
+            }
+        }
+        return objects;
+    }
+
+    private <T> T queryForSingleObject(SQLEntity entity, Class<T> clz) {
+        Map<String, Object> map = queryForMap(entity);
+        if (!DbUtil.isEmptyOrNull(map)) {
+            Object value = null;
+            Set<Map.Entry<String, Object>> entrys = map.entrySet();
+            for (Map.Entry<String, Object> entry : entrys) {
+                value = entry.getValue();
+                break;
+            }
+            if (null != value) {
+                return JacksonUtil.toJavaObject(value.toString(), clz);
+            }
+        }
+        return null;
+    }
+
+
+    public final long selectCount(Class<?> clz, OBuilder oBuilder) {
+        beforeSelect(clz, oBuilder);
+        SQLEntity entity = helper.selectCount(clz, oBuilder);
+        Long result = queryForSingleObject(entity, Long.class);
+        if (null == result) {
+            return 0;
+        }
+        return result;
+    }
+
+
+    public final long selectCount(BasicQuery query) {
+        beforeSelect(query);
+        query.pageSize = -1;
+        SQLEntity entity = helper.selectCountByQuery(query);
+        Long result = queryForSingleObject(entity, Long.class);
+        if (null == result) {
+            return 0;
+        }
+        return result;
+    }
+
+    public final Map<String, Object> selectOneForMap(String sql, Object... parameters) {
+        long start = System.currentTimeMillis();
+        if (logger.isInfoEnabled()) {
+            logger.info("SQL>>>:\n{} \nP:{}", sql, JacksonUtil.toJSONString(parameters));
+        }
+        Map<String, Object> result = jdbcTemplate.query(new PreparedStatementCreator() {
+            @Override
+            public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
+                PreparedStatement preparedStatement = connection.prepareStatement(sql);
+                DbUtil.setParameter(preparedStatement, parameters);
+                return preparedStatement;
+            }
+        }, new ResultSetExtractor<Map<String, Object>>() {
+            @Override
+            public Map<String, Object> extractData(ResultSet resultSet) throws SQLException, DataAccessException {
+                return resultToOneMap(resultSet);
+            }
+        });
+        if (logger.isDebugEnabled()) {
+            logger.info("SQL<<< cost:{}ms", System.currentTimeMillis() - start);
+        }
+        return result;
+    }
+
+
+    public final <T> T selectOne(String sql, ResultHandler<T> handler, Object... parameters) {
+        long start = System.currentTimeMillis();
+        if (logger.isInfoEnabled()) {
+            logger.info("SQL>>>:\n{} \nP:{}", sql, JacksonUtil.toJSONString(parameters));
+        }
+        T result = jdbcTemplate.query(new PreparedStatementCreator() {
+            @Override
+            public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
+                PreparedStatement preparedStatement = connection.prepareStatement(sql);
+                DbUtil.setParameter(preparedStatement, parameters);
+                return preparedStatement;
+            }
+        }, new ResultSetExtractor<T>() {
+            @Override
+            public T extractData(ResultSet resultSet) throws SQLException, DataAccessException {
+                Map<String, Object> map = resultToOneMap(resultSet);
+                try {
+                    return handler.handle(map);
+                } catch (Exception e) {
+                    logger.error("data convert error," + e.getMessage(), e);
+                }
+                return null;
+            }
+        });
+        if (logger.isDebugEnabled()) {
+            logger.info("SQL<<< cost:{}ms", System.currentTimeMillis() - start);
+        }
+        return result;
+    }
+
+    private List<Map<String, Object>> resultToListMap(ResultSet rs) throws SQLException {
+        List<Map<String, Object>> result = new ArrayList<>();
+        if (null != rs) {
+            ResultSetMetaData rsmd = rs.getMetaData();
+            while (rs.next()) {
+                Map<String, Object> map = new HashMap<>(16);
+                int columnCount = rsmd.getColumnCount();
+                for (int i = 0; i < columnCount; i++) {
+                    String columnLabel = rsmd.getColumnLabel(i + 1);
+                    map.put(columnLabel, rs.getObject(i + 1));
+                }
+                result.add(map);
+            }
+        }
+        return result;
+    }
+
+    private Map<String, Object> resultToOneMap(ResultSet rs) throws SQLException {
+        Map<String, Object> map = new HashMap<>();
+        if (null != rs) {
+            ResultSetMetaData rsmd = rs.getMetaData();
+            int columnCount = rsmd.getColumnCount();
+//            while (rs.next()) {
+            for (int i = 0; i < columnCount; i++) {
+                String columnLabel = rsmd.getColumnLabel(i + 1);
+                map.put(columnLabel, rs.getObject(i + 1));
+            }
+//            }
+        }
+        return map;
+    }
+
+
+    private Map<String, Object> extractResultToOneMap(ResultSet rs) throws SQLException {
+        Map<String, Object> map = new HashMap<>();
+        if (null != rs) {
+            ResultSetMetaData rsmd = rs.getMetaData();
+            int columnCount = rsmd.getColumnCount();
+            while (rs.next()) {
+                for (int i = 0; i < columnCount; i++) {
+                    String columnLabel = rsmd.getColumnLabel(i + 1);
+                    map.put(columnLabel, rs.getObject(i + 1));
+                }
+            }
+        }
+        return map;
+    }
+
+    public final <T> T selectOne(Class<T> clz, ColumnFunc<T, ?> keyExtractor, Object value) {
+        SQLEntity entity = helper.selectOneByWhere(clz, OBuilder.build().eq(DefaultLambdaParser.getPropertyName(keyExtractor), value), null, null);
+        Map<String, Object> map = queryForMap(entity);
+        T t = map2Object(clz, map);
+        //Annotation @Many
+        if (null != t) {
+            setJoinList(t, clz, null);
+        }
+        return t;
+    }
+
+
+    public final <T> T selectOne(Class<T> clz, String fieldName, Object value) {
+        SQLEntity entity = helper.selectOneByWhere(clz, OBuilder.build().eq(fieldName, value), null, null);
+        Map<String, Object> map = queryForMap(entity);
+        T t = map2Object(clz, map);
+        //Annotation @Many
+        if (null != t) {
+            setJoinList(t, clz, null);
+        }
+        return t;
+    }
+
+    public final <T> T selectOne(Class<T> clz, long id, String... include) {
+        SQLEntity entity = helper.selectById(clz, id, Arrays.asList(include), null);
+        Map<String, Object> map = queryForMap(entity);
+        T t = map2Object(clz, map);
+        //Annotation @Many
+        if (null != t) {
+            setJoinList(t, clz, null);
+        }
+        return t;
+    }
+
+
+    public final <T> T selectOneExclude(Class<T> clz, long id, String... exclude) {
+        SQLEntity entity = helper.selectById(clz, id, null, Arrays.asList(exclude));
+        Map<String, Object> map = queryForMap(entity);
+        T t = map2Object(clz, map);
+        //Annotation @Many
+        if (null != t) {
+            setJoinList(t, clz, Arrays.asList(exclude));
+        }
+        return t;
+    }
+
+    public final <T> T selectOneExist(Class<T> clz, long id) {
+        T bean = selectOne(clz, id);
+        if (null == bean) {
+            throw new IllegalArgumentException(clz.getAnnotation(Entity.class).comment() + "不存在");
+        }
+        return bean;
+    }
+
+    public final <T> T selectOneExist(Class<T> clz, OBuilder builder) {
+        beforeSelect(clz, builder);
+        T bean = selectOne(clz, builder);
+        if (null == bean) {
+            throw new IllegalArgumentException(clz.getAnnotation(Entity.class).comment() + "不存在");
+        }
+        return bean;
+    }
+
+    public final <T> T selectOne(Class<T> clz, OBuilder builder) {
+        beforeSelect(clz, builder);
+        SQLEntity entity = helper.selectOneByWhere(clz, builder, null, null);
+        Map<String, Object> map = queryForMap(entity);
+        T t = map2Object(clz, map);
+        //Annotation @Many
+        if (null != t) {
+            setJoinList(t, clz, null);
+        }
+        return t;
+    }
+
+    public final <T> T selectOne(Class<T> clz, OBuilder builder, List<String> includes) {
+        beforeSelect(clz, builder);
+        SQLEntity entity = helper.selectOneByWhere(clz, builder, includes, null);
+        Map<String, Object> map = queryForMap(entity);
+        T t = map2Object(clz, map);
+        //Annotation @Many
+        if (null != t) {
+            setJoinList(t, clz, null);
+        }
+        return t;
+    }
+
+    @SuppressWarnings("unchecked")
+    public final <T> T selectOne(BasicQuery query, String... include) {
+        beforeSelect(query);
+        SQLEntity entity = helper.selectOneByQuery(query, Arrays.asList(include), null);
+        Map<String, Object> map = queryForMap(entity);
+        Class<T> clz = (Class<T>) query.getClass().getAnnotation(QE.class).clz();
+        T t = map2Object(clz, map);
+        //Annotation @Many
+        if (null != t) {
+            setJoinList(t, clz, null);
+        }
+        return t;
+    }
+
+
+    @SuppressWarnings("unchecked")
+    public final <T> T selectOneExclude(BasicQuery query, String... exclude) {
+        beforeSelect(query);
+        SQLEntity entity = helper.selectOneByQuery(query, null, Arrays.asList(exclude));
+        Map<String, Object> map = queryForMap(entity);
+        Class<T> clz = (Class<T>) query.getClass().getAnnotation(QE.class).clz();
+        T t = map2Object(clz, map);
+        //Annotation @Many
+        if (null != t) {
+            setJoinList(t, clz, Arrays.asList(exclude));
+        }
+        return t;
+    }
+
+    public final <T> T selectField(Class<?> clz, String fieldName, OBuilder OBuilder, Class<T> rspClz) {
+        String sql = "select " + getColumnName(fieldName) + " as f0 from " + getTableName(clz.getSimpleName()) + " where " + OBuilder.toSql();
+        return selectOne(sql, rs -> JacksonUtil.toJavaObject(rs.get("f0"), rspClz));
+    }
+
+    public final <T> T selectField(Class<?> clz, ColumnFunc<T, ?> keyExtractor, OBuilder OBuilder, Class<T> rspClz) {
+        String sql = "select " + getColumnName(DefaultLambdaParser.getPropertyName(keyExtractor)) + " as f0 from " + getTableName(clz.getSimpleName()) + " where " + OBuilder.toSql();
+        return selectOne(sql, rs -> JacksonUtil.toJavaObject(rs.get("f0"), rspClz));
+    }
+
+
+    //endregion
+
+
+    //region insert
+
+    /**
+     * ------------insert------------
+     */
+    public final long insert(Object bean) {
+        beforeInsert(bean);
+        long id = insert(bean, false, false, "id");
+        setFieldValue(bean, "id", id);
+        return id;
+    }
+
+    /**
+     * 插入记录(无id返回)
+     */
+    public final void insertOnly(Object bean) {
+        beforeInsert(bean);
+        insert(bean, false, true, "id");
+    }
+
+    /**
+     * 插入非空值
+     */
+    public final long insertSelective(Object bean, String... exclude) {
+        beforeInsert(bean);
+        List<String> exs = new ArrayList<>();
+        if (!DbUtil.isEmptyOrNull(exclude)) {
+            exs.addAll(Arrays.asList(exclude));
+        }
+        SQLEntity entity = helper.insertSelective(bean, exs);
+        long id = executeInsert(entity, true);
+        setFieldValue(bean, "id", id);
+        return id;
+    }
+
+    private long executeInsert(SQLEntity entity, boolean returnKey) {
+        long result = 0;
+        long start = System.currentTimeMillis();
+        if (logger.isInfoEnabled()) {
+            logger.info("SQL>>>:\n{} \nP:{}", entity.sql, JacksonUtil.toJSONString(entity.parameters));
+        }
+        if (returnKey) {
+            KeyHolder keyHolder = new GeneratedKeyHolder();
+            jdbcTemplate.update(new PreparedStatementCreator() {
+                @Override
+                public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
+                    if (helper.getDialect() == JdbcHelper.DIALECT_PGSQL) {
+                        PreparedStatement ps = connection.prepareStatement(entity.sql, new String[]{"id"});
+                        DbUtil.setParameter(ps, entity.parameters);
+                        return ps;
+                    }
+                    if (helper.getDialect() == JdbcHelper.DIALECT_MYSQL) {
+                        PreparedStatement ps = connection.prepareStatement(entity.sql, PreparedStatement.RETURN_GENERATED_KEYS);
+                        DbUtil.setParameter(ps, entity.parameters);
+                        return ps;
+                    }
+                    return null;
+                }
+            }, keyHolder);
+            Number number = keyHolder.getKey();
+            long id = -1;
+            if (null != number) {
+                id = number.longValue();
+            }
+            result = id;
+        } else {
+            jdbcTemplate.update(connection -> {
+                PreparedStatement ps = connection.prepareStatement(entity.sql);
+                DbUtil.setParameter(ps, entity.parameters);
+                return ps;
+            });
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.info("SQL<<< cost:{}ms", System.currentTimeMillis() - start);
+        }
+        return result;
+    }
+
+
+    /**
+     * 插入非空值(无id返回)
+     */
+    public final void insertSelectiveOnly(Object bean, String... exclude) {
+        beforeInsert(bean);
+        SQLEntity entity = helper.insertSelective(bean, Arrays.asList(exclude));
+        executeInsert(entity, false);
+    }
+
+    /**
+     * @param withGenKey 是否主动生成ID主键<br>
+     *                   主键策略可以使用{@link com.kym.common.utils.sequence.IdWorker.nextId} ,避免高并发情况下通过#lastInsertId获取id错误的情况
+     */
+    public final long insert(Object bean, boolean withGenKey, boolean insertOnly, String... exclude) {
+        beforeInsert(bean);
+        if (!withGenKey) {
+            SQLEntity entity = helper.insert(bean, new ArrayList<>(Arrays.asList(exclude)));
+            return executeInsert(entity, !insertOnly);
+        } else {
+            Object id = getFieldValue(bean, "id");
+            if (DbUtil.isEmptyOrNull(id)) {
+                throw new IllegalArgumentException("without id exist for insert withGenKey");
+            }
+            SQLEntity entity = helper.insertWithGenKey(bean, new ArrayList<>(Arrays.asList(exclude)));
+            executeInsert(entity, false);
+            return (long) getFieldValue(bean, "id");
+        }
+    }
+
+    public final void insertBatch(List<?> dtoList) {
+        if (DbUtil.isEmptyOrNull(dtoList)) {
+            logger.info("insertBatch no dtoList exists!!!");
+            return;
+        }
+        List<String> ignores = Arrays.asList("id".split(","));
+        Set<Field> fields = DbUtil.getEntityFields(dtoList.get(0).getClass());
+        List<String> fieldList = new ArrayList<>();
+        for (Field field : fields) {
+            int modifier = field.getModifiers();
+            if (!Modifier.isFinal(modifier) && !Modifier.isStatic(modifier) && field.isAnnotationPresent(DBF.class)) {
+                String fieldName = field.getName();
+                if (!ignores.contains(fieldName)) {
+                    fieldList.add(field.getName());
+                }
+            }
+        }
+
+        insertBatch(fieldList, dtoList);
+    }
+
+    /**
+     * 需自己组装SQL语句
+     */
+    public final void insertBatch(String sql, List<Object[]> parameters) {
+        long start = System.currentTimeMillis();
+        if (logger.isInfoEnabled()) {
+            logger.info("SQL>>>:\n{} \nP:{}", sql, JacksonUtil.toJSONString(parameters));
+        }
+        int batch = 1;
+        int batchSize = 256;
+        int totalSize = parameters.size();
+        if (totalSize > batchSize) {
+            batch = (int) Math.ceil((double) totalSize / batchSize);
+        }
+        for (int i = 0; i < batch; i++) {
+            List<Object[]> subList = parameters.subList(i * batchSize, Math.min(totalSize, ((i + 1) * batchSize)));
+            jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
+                @Override
+                public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
+                    DbUtil.setParameter(preparedStatement, subList.get(i));
+//                    preparedStatement.addBatch();
+                }
+
+                @Override
+                public int getBatchSize() {
+                    return subList.size();
+                }
+            });
+
+        }
+
+
+        if (logger.isDebugEnabled()) {
+            logger.info("SQL<<< cost:{}ms", System.currentTimeMillis() - start);
+        }
+    }
+
+    /**
+     * 无需自己组装SQL
+     */
+    public final void insertBatch(List<String> fields, List<?> dtoList) {
+        if (DbUtil.isEmptyOrNull(fields)) {
+            throw new IllegalArgumentException("ERROR# insert fields is empty!");
+        }
+        if (DbUtil.isEmptyOrNull(dtoList)) {
+            throw new IllegalArgumentException("ERROR# insert values is empty!");
+        }
+        StringBuilder sql = new StringBuilder("insert into ");
+        String tbName = dtoList.get(0).getClass().getSimpleName();
+        sql.append(getTableName(tbName)).append(" ( ");
+        sql.append(fields.stream().map(this::getColumnName).collect(Collectors.joining(",")));
+        sql.append(" ) values (");
+        sql.append(fields.stream().map(k -> "?").collect(Collectors.joining(",")));
+        sql.append(")");
+
+        List<Object[]> params = new ArrayList<>();
+        int fieldSize = fields.size();
+        for (Object o : dtoList) {
+            Object[] ll = new Object[fieldSize];
+            for (int j = 0; j < fieldSize; j++) {
+                ll[j] = getFieldValue(o, fields.get(j));
+            }
+            params.add(ll);
+        }
+
+        int totalSize = params.size();
+        long start = System.currentTimeMillis();
+        if (logger.isInfoEnabled()) {
+            logger.info("SQL>>>:\n{} \nP:{}", sql, totalSize > 20 ? params.size() : JacksonUtil.toJSONString(params));
+        }
+
+        if (DbUtil.isEmptyOrNull(params)) {
+            return;
+        }
+
+        int batch = 1;
+        int batchSize = 256;
+        if (totalSize > batchSize) {
+            batch = (int) Math.ceil((double) totalSize / batchSize);
+        }
+        for (int i = 0; i < batch; i++) {
+            List<Object[]> subList = params.subList(i * batchSize, Math.min(totalSize, ((i + 1) * batchSize)));
+            jdbcTemplate.batchUpdate(sql.toString(), new BatchPreparedStatementSetter() {
+                @Override
+                public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
+//                    logger.info("i>>>>>>>" + i);
+                    DbUtil.setParameter(preparedStatement, subList.get(i));
+                }
+
+                @Override
+                public int getBatchSize() {
+                    return subList.size();
+                }
+            });
+
+        }
+
+
+        if (logger.isDebugEnabled()) {
+            logger.info("SQL<<< cost:{}ms", System.currentTimeMillis() - start);
+        }
+    }
+
+    //endregion
+
+    //region update
+
+    /**
+     * ------------update------------
+     */
+
+    private int executeUpdate(SQLEntity entity) {
+        long start = System.currentTimeMillis();
+        if (logger.isInfoEnabled()) {
+            logger.info("SQL>>>:\n{} \nP:{}", entity.sql, JacksonUtil.toJSONString(entity.parameters));
+        }
+        int result = jdbcTemplate.update(connection -> {
+            PreparedStatement ps = connection.prepareStatement(entity.sql);
+            DbUtil.setParameter(ps, entity.parameters);
+            return ps;
+        });
+        if (logger.isDebugEnabled()) {
+            logger.info("SQL<<< cost:{}ms", System.currentTimeMillis() - start);
+        }
+        return result;
+    }
+
+    public final int update(Object bean) {
+        checkConditionOnUpdate(bean);
+        beforeUpdate(bean);
+        SQLEntity entity = helper.updateByWhere(bean, OBuilder.build());
+        int count = executeUpdate(entity);
+        if (count == 0) {
+            logger.warn("SQLHandler WARN# update with no record effects");
+        } else {
+            logger.debug("update effect rows:{}", count);
+        }
+        return count;
+    }
+
+    public final int update(String sql, Object... parameters) {
+        SQLEntity entity = helper.update(sql, parameters);
+        int count = executeUpdate(entity);
+        if (count == 0) {
+            logger.warn("SQLHandler WARN# update with no record effects#SQL:{},PARAMS:{}", sql, JacksonUtil.toJSONString(parameters));
+        } else {
+            logger.debug("update effect rows:{}", count);
+        }
+        return count;
+    }
+
+    public final int update(Object bean, OBuilder builder) {
+        checkConditionOnUpdate(builder);
+        beforeUpdate(bean, builder);
+        SQLEntity entity = helper.updateByWhere(bean, builder);
+        int count = executeUpdate(entity);
+        if (count == 0) {
+            logger.warn("SQLHandler WARN# update with no record effects");
+        } else {
+            logger.debug("update effect rows:{}", count);
+        }
+        return count;
+    }
+
+
+    public final <T> int update(Object bean, OBuilder builder, ColumnFunc<T, ?> keyExtractor) {
+        checkConditionOnUpdate(builder);
+        beforeUpdate(bean, builder);
+        List<String> includes = new ArrayList<>();
+        if (!DbUtil.isEmptyOrNull(keyExtractor)) {
+            includes.add(DefaultLambdaParser.getPropertyName(keyExtractor));
+        }
+        SQLEntity entity = helper.updateByWhere(bean, builder, includes);
+        int count = executeUpdate(entity);
+        if (count == 0) {
+            logger.warn("SQLHandler WARN# update with no record effects");
+        } else {
+            logger.debug("update effect rows:{}", count);
+        }
+        return count;
+    }
+
+    /**
+     * 部分字段更新(为空、为0不更新)
+     */
+    public final int updateSelective(Object bean) {
+        checkConditionOnUpdate(bean);
+        beforeUpdate(bean);
+        SQLEntity entity = helper.updateSelective(bean, null, null);
+        int count = executeUpdate(entity);
+        if (count == 0) {
+            logger.warn("SQLHandler WARN# update with no record effects");
+        } else {
+            logger.debug("update effect rows:{}", count);
+        }
+        return count;
+    }
+
+
+    /**
+     * 只更新指定字段
+     */
+    public final int updateSelective(Object bean, String... includes) {
+        checkConditionOnUpdate(bean);
+        beforeUpdate(bean);
+        List<String> ic = new ArrayList<>();
+        if (!DbUtil.isEmptyOrNull(includes)) {
+            ic = Arrays.asList(includes);
+        }
+        SQLEntity entity = helper.updateSelective(bean, ic, null);
+        int count = executeUpdate(entity);
+        if (count == 0) {
+            logger.warn("SQLHandler WARN# update with no record effects");
+        } else {
+            logger.debug("update effect rows:{}", count);
+        }
+        return count;
+    }
+
+
+    @SafeVarargs
+    public final <T> int updateSelective(Object bean, ColumnFunc<T, ?>... keyExtractor) {
+        checkConditionOnUpdate(bean);
+        beforeUpdate(bean);
+        List<String> ic = new ArrayList<>();
+        if (!DbUtil.isEmptyOrNull(keyExtractor)) {
+            for (ColumnFunc<T, ?> func : keyExtractor) {
+                ic.add(DefaultLambdaParser.getPropertyName(func));
+            }
+        }
+        SQLEntity entity = helper.updateSelective(bean, ic, null);
+        int count = executeUpdate(entity);
+        if (count == 0) {
+            logger.warn("SQLHandler WARN# update with no record effects");
+        } else {
+            logger.debug("update effect rows:{}", count);
+        }
+        return count;
+    }
+
+    public final int updateSelectiveExclude(Object bean, String... excludes) {
+        checkConditionOnUpdate(bean);
+        beforeUpdate(bean);
+        List<String> ec = new ArrayList<>();
+        if (!DbUtil.isEmptyOrNull(excludes)) {
+            ec = Arrays.asList(excludes);
+        }
+        SQLEntity entity = helper.updateSelective(bean, null, ec);
+        int count = executeUpdate(entity);
+        if (count == 0) {
+            logger.warn("SQLHandler WARN# update with no record effects");
+        } else {
+            logger.debug("update effect rows:{}", count);
+        }
+        return count;
+    }
+
+
+    public final <T> int updateSelectiveExclude(Object bean, ColumnFunc<T, ?>... exkeyExtractor) {
+        checkConditionOnUpdate(bean);
+        beforeUpdate(bean);
+        List<String> ec = new ArrayList<>();
+        if (!DbUtil.isEmptyOrNull(exkeyExtractor)) {
+            for (ColumnFunc<T, ?> tColumnFunc : exkeyExtractor) {
+                ec.add(DefaultLambdaParser.getPropertyName(tColumnFunc));
+            }
+        }
+        SQLEntity entity = helper.updateSelective(bean, null, ec);
+        int count = executeUpdate(entity);
+        if (count == 0) {
+            logger.warn("SQLHandler WARN# update with no record effects");
+        } else {
+            logger.debug("update effect rows:{}", count);
+        }
+        return count;
+    }
+
+
+    /**
+     * 部分字段更新(为空、为0不更新)
+     */
+    public final int updateSelective(Object bean, OBuilder OBuilder) {
+        checkConditionOnUpdate(OBuilder);
+        beforeUpdate(bean, OBuilder);
+        SQLEntity entity = helper.updateSelectiveByWhere(bean, OBuilder);
+
+        int count = executeUpdate(entity);
+        if (count == 0) {
+            logger.warn("SQLHandler WARN# update with no record effects");
+        } else {
+            logger.debug("update effect rows:{}", count);
+        }
+
+        return count;
+    }
+
+    public final <T> void updateBatchV2(List<ColumnFunc<T, ?>> keyExtractors, List<?> dtoList) {
+        List<String> fields = new ArrayList<>();
+        keyExtractors.forEach(keyExtractor -> fields.add(DefaultLambdaParser.getPropertyName(keyExtractor)));
+        updateBatch(fields, dtoList);
+    }
+
+    /**
+     * 批量更新方法
+     *
+     * @param fields
+     * @param dtoList
+     */
+    public final void updateBatch(List<String> fields, List<?> dtoList) {
+        if (DbUtil.isEmptyOrNull(dtoList)) {
+            logger.error("no records to batch update!!!");
+            return;
+        }
+        if (DbUtil.isEmptyOrNull(fields)) {
+            logger.error("no column to batch update!!!");
+            return;
+        }
+
+        List<Object[]> params = new ArrayList<>();
+        for (int i1 = 0; i1 < dtoList.size(); i1++) {
+            Object obj = dtoList.get(i1);
+            Object[] param = new Object[fields.size() + 1];
+            for (int i = 0; i < fields.size(); i++) {
+                param[i] = DbUtil.getFieldValue(obj, DbUtil.getCamelName(fields.get(i)));
+            }
+            Object id = DbUtil.getFieldValue(obj, "id");
+            if (null == id || Long.parseLong(id.toString()) == 0) {
+                logger.error("id is null ,idx:{}", i1);
+                break;
+            }
+            param[fields.size()] = id;
+            params.add(param);
+        }
+
+
+        String sql = " update " + JdbcHelper.getTableName(dtoList.get(0).getClass(), dtoList.get(0).getClass().getSimpleName()) + " set " + fields.stream().map(k -> DbUtil.getColumnName(k) + "=?").collect(Collectors.joining(",")) + " where id = ?";
+        updateBatch(sql, params);
+    }
+
+    public final void updateBatch(String sql, List<Object[]> params) {
+        long start = System.currentTimeMillis();
+        if (logger.isInfoEnabled()) {
+            logger.info("SQL>>>:\n{} \nP:{}", sql, params.size() > 20 ? params.size() : JacksonUtil.toJSONString(params));
+        }
+
+        int batch = 1;
+        int batchSize = 256;
+        int totalSize = params.size();
+        if (totalSize > batchSize) {
+            batch = (int) Math.ceil((double) totalSize / batchSize);
+        }
+        for (int i = 0; i < batch; i++) {
+            List<Object[]> subList = params.subList(i * batchSize, Math.min(totalSize, ((i + 1) * batchSize)));
+            jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
+                @Override
+                public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
+                    DbUtil.setParameter(preparedStatement, subList.get(i));
+                }
+
+                @Override
+                public int getBatchSize() {
+                    return subList.size();
+                }
+            });
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.info("SQL<<< cost:{}ms", System.currentTimeMillis() - start);
+        }
+
+    }
+
+    //endregion
+
+    /**
+     * 更新数据必须指定where条件
+     */
+    private void checkConditionOnUpdate(Object bean) {
+        Object val = getFieldValue(bean, "id");
+        if (null == val) {
+            throw new IllegalArgumentException("no key exist for update SQL");
+        }
+    }
+
+    /**
+     * 更新数据必须指定条件
+     */
+    private void checkConditionOnUpdate(OBuilder OBuilder) {
+        if (null == OBuilder || DbUtil.isEmptyOrNull(OBuilder.getWheres())) {
+            throw new IllegalArgumentException("no builder condition exist for update SQL");
+        }
+    }
+
+//region delete
+
+    /**
+     * ------------delete------------
+     */
+    public final int delete(Class<?> clz, OBuilder OBuilder) {
+        checkConditionOnDelete(OBuilder);
+        beforeDelete(clz, OBuilder);
+        SQLEntity delete = helper.delete(clz, OBuilder);
+        int count = executeUpdate(delete);
+        if (count == 0) {
+            logger.warn("SQLHandler WARN# no record delete,class:{},builder:{}", clz.getSimpleName(), OBuilder);
+        }
+        return count;
+    }
+
+    public final int delete(Class<?> clz, Object id) {
+        if (DbUtil.isEmptyOrNull(id)) {
+            throw new IllegalArgumentException("delete without id exist!");
+        }
+        beforeDelete(clz, id);
+        SQLEntity delete = helper.deleteById(clz, id);
+        int count = executeUpdate(delete);
+        if (count == 0) {
+            logger.warn("SQLHandler WARN# no record delete,class:{},id:{}", clz.getSimpleName(), id);
+        }
+        return count;
+    }
+
+    //endregion
+
+    /**
+     * 检验条件避免truncate表
+     */
+    private void checkConditionOnDelete(OBuilder OBuilder) {
+        if (null == OBuilder || DbUtil.isEmptyOrNull(OBuilder.getWheres())) {
+            throw new IllegalArgumentException("delete without builder condition exist!");
+        }
+    }
+
+
+    /**
+     * ---------------util tool----------
+     */
+    public final void setFieldValue(Object bean, String fieldName, Object val) {
+        if (null == val) {
+            return;
+        }
+        Field field = null;
+        try {
+            try {
+                field = bean.getClass().getDeclaredField(fieldName);
+            } catch (Exception e) {
+                try {
+                    field = bean.getClass().getField(fieldName);
+                } catch (Exception e1) {
+                    try {
+                        field = bean.getClass().getSuperclass().getDeclaredField(fieldName);
+                    } catch (Exception e2) {
+                        field = bean.getClass().getSuperclass().getField(fieldName);
+                    }
+                }
+            }
+            field.setAccessible(true);
+            Class<?> type = field.getType();
+            if (type == Date.class) {
+                if (val instanceof String) {
+                    //TODO yyyy-mm-dd  HH:mm:ss.SSS
+                    String v = (String) val;
+                    if (v.length() == 10) {
+                        LocalDate time = LocalDate.parse(v, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+                        Date d = Date.from(LocalDateTime.of(time, LocalTime.MIN).atZone(ZoneId.systemDefault()).toInstant());
+                        field.set(bean, d);
+                    } else if (v.length() > 20 && v.length() <= 24) {
+                        LocalDateTime time = LocalDateTime.parse(v, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
+                        Date d = Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
+                        field.set(bean, d);
+                    } else if (v.length() > 24) {
+                        LocalDateTime time = LocalDateTime.parse(v, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS"));
+                        Date d = Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
+                        field.set(bean, d);
+                    } else {
+                        LocalDateTime time = LocalDateTime.parse(v, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+                        Date d = Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
+                        field.set(bean, d);
+                    }
+                } else {
+                    field.set(bean, val);
+                }
+            } else if (type == String.class) {
+                field.set(bean, val.toString());
+            } else if (type == int.class || type == Integer.class) {
+                //db tinyint(1)->boolean
+                if (val instanceof Boolean) {
+                    field.set(bean, (Boolean) val ? 1 : 0);
+                } else {
+                    field.set(bean, Integer.valueOf(val.toString()));
+                }
+            } else if (type == double.class || type == Double.class) {
+                field.set(bean, Double.valueOf(val.toString()));
+            } else if (type == long.class || type == Long.class) {
+                field.set(bean, Long.valueOf(val.toString()));
+            } else if (type == float.class || type == Float.class) {
+                field.set(bean, Float.valueOf(val.toString()));
+            } else if (type == short.class || type == Short.class) {
+                field.set(bean, Short.valueOf(val.toString()));
+            } else if (type == byte.class || type == Byte.class) {
+                field.set(bean, Byte.valueOf(val.toString()));
+            } else if (type == boolean.class || type == Boolean.class) {
+                if (val instanceof String) {
+                    boolean v = "t".equals(val) || "1".equals(val);
+                    field.set(bean, v);
+                } else {
+                    field.set(bean, Boolean.valueOf(val.toString()));
+                }
+            } else if (type == char.class || type == Character.class) {
+                field.set(bean, val);
+            } else if (type == BigDecimal.class) {
+                field.set(bean, new BigDecimal(val.toString()));
+            } else if (type.isArray()) {
+                if (DbUtil.isEmptyOrNull(val)) {
+                    val = "[]";
+                }
+                setFieldArrayValue(bean, field, type, val.toString());
+            } else if (type.isAssignableFrom(List.class) && !val.getClass().isAssignableFrom(ArrayList.class)) {
+                if (DbUtil.isEmptyOrNull(val)) {
+                    val = Collections.emptyList();
+                }
+                Type collClz = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
+                field.set(bean, JacksonUtil.toJavaObjectList(val.toString(), (Class<?>) collClz));
+            } else if (type.isAssignableFrom(Set.class) && !val.getClass().isAssignableFrom(HashSet.class)) {
+                if (DbUtil.isEmptyOrNull(val)) {
+                    val = Collections.emptySet();
+                }
+                Type collClz = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
+                List<?> dataList = JacksonUtil.toJavaObjectList(val.toString(), (Class<?>) collClz);
+                if (DbUtil.isEmptyOrNull(dataList)) {
+                    field.set(bean, Collections.emptySet());
+                } else {
+                    field.set(bean, new HashSet<>(dataList));
+                }
+
+            } else if (type.isAssignableFrom(Map.class)) {
+                if (DbUtil.isEmptyOrNull(val)) {
+                    val = new HashMap<>();
+                }
+                field.set(bean, JacksonUtil.toJavaObject(val.toString(), Map.class));
+            } else {
+                logger.debug("setFieldValue>> {},{}", fieldName, bean.getClass().getName());
+                field.set(bean, JacksonUtil.toJavaObject(val.toString(), type));
+//                field.set(bean, val);
+            }
+            field.setAccessible(false);
+        } catch (Exception e) {
+            logger.warn("SQLHandler ERR# setFieldValue111  field:{},{},{}", e.getClass().getSimpleName(), fieldName, val);
+        }
+    }
+
+    private void setFieldArrayValue(Object bean, Field field, Class<?> clz, String value) {
+
+        try {
+            List<Object> list = JacksonUtil.toList(value);
+            if (null == list) {
+                field.set(bean, Collections.emptyList());
+                return;
+            }
+            if (clz == String[].class) {
+                String[] arr = new String[list.size()];
+                if (!DbUtil.isEmptyOrNull(list)) {
+                    for (int i = 0; i < list.size(); i++) {
+                        arr[i] = ((String) list.get(i)).replaceAll("\u0001", "/");
+                    }
+                }
+                field.set(bean, arr);
+            } else if (clz == int[].class) {
+                int[] arr = new int[list.size()];
+                if (!DbUtil.isEmptyOrNull(list)) {
+                    for (int i = 0; i < list.size(); i++) {
+                        arr[i] = (Integer) list.get(i);
+                    }
+                }
+                field.set(bean, arr);
+            } else if (clz == short[].class) {
+                short[] arr = new short[list.size()];
+                if (!DbUtil.isEmptyOrNull(list)) {
+                    for (int i = 0; i < list.size(); i++) {
+                        arr[i] = (Short) list.get(i);
+                    }
+                }
+                field.set(bean, arr);
+            } else if (clz == long[].class) {
+                long[] arr = new long[list.size()];
+                if (!DbUtil.isEmptyOrNull(list)) {
+                    for (int i = 0; i < list.size(); i++) {
+                        arr[i] = (Long) list.get(i);
+                    }
+                }
+                field.set(bean, arr);
+            } else if (clz == byte[].class) {
+                byte[] arr = new byte[list.size()];
+                if (!DbUtil.isEmptyOrNull(list)) {
+                    for (int i = 0; i < list.size(); i++) {
+                        arr[i] = (Byte) list.get(i);
+                    }
+                }
+                field.set(bean, arr);
+            } else if (clz == double[].class) {
+                double[] arr = new double[list.size()];
+                if (!DbUtil.isEmptyOrNull(list)) {
+                    for (int i = 0; i < list.size(); i++) {
+                        arr[i] = (Double) list.get(i);
+                    }
+                }
+                field.set(bean, arr);
+            } else if (clz == float[].class) {
+                float[] arr = new float[list.size()];
+                if (!DbUtil.isEmptyOrNull(list)) {
+                    for (int i = 0; i < list.size(); i++) {
+                        arr[i] = (Float) list.get(i);
+                    }
+                }
+                field.set(bean, arr);
+            } else if (clz == char[].class) {
+                char[] arr = new char[list.size()];
+                if (!DbUtil.isEmptyOrNull(list)) {
+                    for (int i = 0; i < list.size(); i++) {
+                        arr[i] = (Character) list.get(i);
+                    }
+                }
+                field.set(bean, arr);
+            } else if (clz == boolean[].class) {
+                boolean[] arr = new boolean[list.size()];
+                if (!DbUtil.isEmptyOrNull(list)) {
+                    for (int i = 0; i < list.size(); i++) {
+                        arr[i] = (Boolean) list.get(i);
+                    }
+                }
+                field.set(bean, arr);
+            } else {
+                Object[] arr = new Object[list.size()];
+                if (!DbUtil.isEmptyOrNull(list)) {
+                    for (int i = 0; i < list.size(); i++) {
+                        arr[i] = list.get(i);
+                    }
+                }
+                field.set(bean, arr);
+            }
+        } catch (Exception e) {
+            throw new IllegalArgumentException("convert array field ERR");
+        }
+    }
+
+    public final void setFieldValue(Class<?> type, Object bean, String fieldName, Object val) {
+        try {
+            Field field = bean.getClass().getField(fieldName);
+            field.setAccessible(true);
+            if (type == Date.class) {
+                field.set(bean, val);
+            } else if (type == String.class) {
+                field.set(bean, val.toString());
+            } else if (type == int.class || type == Integer.class) {
+                field.set(bean, Integer.valueOf(val.toString()));
+            } else if (type == double.class || type == Double.class) {
+                field.set(bean, Double.valueOf(val.toString()));
+            } else if (type == long.class || type == Long.class) {
+                field.set(bean, Long.valueOf(val.toString()));
+            } else if (type == float.class || type == Float.class) {
+                field.set(bean, Float.valueOf(val.toString()));
+            } else if (type == short.class || type == Short.class) {
+                field.set(bean, Short.valueOf(val.toString()));
+            } else if (type == byte.class || type == Byte.class) {
+                field.set(bean, Byte.valueOf(val.toString()));
+            } else if (type == boolean.class || type == Boolean.class) {
+                field.set(bean, Boolean.valueOf(val.toString()));
+            } else if (type == char.class || type == Character.class) {
+                field.set(bean, val);
+            } else if (field.getType().isArray()) {
+                field.set(bean, JacksonUtil.toList(val.toString()));
+            } else if (type == BigDecimal.class) {
+                field.set(bean, new BigDecimal(val.toString()));
+            }
+            field.setAccessible(false);
+        } catch (Exception e) {
+            logger.error("SQLHandler ERR# setFieldValue " + e.getMessage(), e);
+        }
+    }
+
+    public final Object getFieldValue(Object bean, String fieldName) {
+        Object defValu = "";
+        try {
+            fieldName = getCamelName(fieldName);
+//            Field field = bean.getClass().getField(fieldName);
+            Field field = DbUtil.getField(bean, fieldName);
+            field.setAccessible(true);
+            defValu = field.get(bean);
+            if (null == defValu) {
+                Class<?> type = field.getType();
+                if (type == String.class) {
+                    defValu = "";
+                } else if (type == int.class || type == Integer.class) {
+                    defValu = 0;
+                } else if (type == double.class || type == Double.class) {
+                    defValu = 0d;
+                } else if (type == long.class || type == Long.class) {
+                    defValu = 0L;
+                } else if (type == float.class || type == Float.class) {
+                    defValu = 0F;
+                } else if (type == short.class || type == Short.class) {
+                    defValu = 0;
+                } else if (type == byte.class || type == Byte.class) {
+                    defValu = 0;
+                } else if (type == boolean.class || type == Boolean.class) {
+                    defValu = 0;
+                } else if (type == char.class || type == Character.class) {
+                    defValu = '0';
+                } else if (field.getType().isArray()) {
+                    defValu = new Object[]{};
+                }
+            }
+            field.setAccessible(false);
+        } catch (Exception e) {
+            logger.warn("SQLHandler ERR# getFieldValue ,field:{}, bean:{} ,msg:{}", fieldName, bean.getClass().getSimpleName(), e.getMessage());
+        }
+        return defValu;
+    }
+
+
+    protected final String getColumnName(String fieldName) {
+        StringBuilder sbr = new StringBuilder();
+        char[] chars = fieldName.toCharArray();
+        for (char ch : chars) {
+            if (Character.isUpperCase(ch)) {
+                sbr.append("_").append(Character.toLowerCase(ch));
+            } else {
+                sbr.append(ch);
+            }
+        }
+        return sbr.toString();
+    }
+
+    /**
+     * 获取驼峰变量名
+     */
+    protected final String getCamelName(String fieldName) {
+        StringBuilder sbr = new StringBuilder();
+        char[] chars = fieldName.toCharArray();
+        boolean preUpper = false;
+        for (char ch : chars) {
+            if ('_' == ch) {
+                preUpper = true;
+            } else {
+                if (preUpper) {
+                    sbr.append(Character.toUpperCase(ch));
+                    preUpper = false;
+                } else {
+                    sbr.append(ch);
+                }
+            }
+        }
+        return sbr.toString();
+    }
+
+    protected final String getTableName(String tbName) {
+        if (tbName.endsWith("Info")) {
+            tbName = tbName.substring(0, tbName.length() - 4);
+        }
+        return JdbcHelper.getTbPrefix() + getColumnName(tbName);
+    }
+
+    private <T> T map2Object(Class<T> clz, ResultSet rs) throws SQLException {
+        Map<String, Object> map = new HashMap<>(16);
+        ResultSetMetaData metaData = rs.getMetaData();
+        int count = metaData.getColumnCount();
+        List<String> dbFields = new ArrayList<>();
+        for (int i = 1; i < count + 1; i++) {
+            dbFields.add(metaData.getColumnLabel(i));
+        }
+        for (String dbField : dbFields) {
+            map.put(dbField, rs.getString(dbField));
+        }
+        if (clz.isAssignableFrom(Map.class)) {
+            return (T) map;
+        }
+        return map2Object(clz, map);
+    }
+
+    private <T> T map2Object(Class<T> clz, Map<String, Object> map) {
+        if (DbUtil.isEmptyOrNull(map)) {
+            return null;
+        }
+        Set<Field> fields = DbUtil.getEntityFields(clz);
+        try {
+            Map<String, Class<?>> join = new HashMap<>(2);
+            T t = clz.newInstance();
+            Map<String, Field> fieldMap = new HashMap<>();
+            for (Field field : fields) {
+                if (DbUtil.isUsageField(field)) {
+                    String fieldName = field.getName();
+                    Class<?> type = field.getType();
+                    if (!type.isArray() && type != List.class && type != Set.class && !DbUtil.isGenericType(type) && type != String.class && type != Date.class && type != BigDecimal.class && type != Map.class && !field.isAnnotationPresent(DBF.class)) {
+                        join.put(fieldName, type);
+                        fieldMap.put(fieldName, field);
+                        continue;
+                    }
+
+
+                    Object val = map.get(getColumnName(fieldName));
+                    if (null == val) {
+                        if (type.isArray()) {
+                            //类型转换
+                            setFieldValue(t, fieldName, new Object[]{});
+                        } else if (type.isAssignableFrom(List.class)) {
+                            setFieldValue(t, fieldName, Collections.emptyList());
+                        } else if (type.isAssignableFrom(Set.class)) {
+                            setFieldValue(t, fieldName, Collections.emptySet());
+                        } else if (type.isAssignableFrom(Map.class)) {
+                            setFieldValue(t, fieldName, Collections.emptyMap());
+                        }
+                    } else {
+                        setFieldValue(t, fieldName, val);
+                    }
+                }
+            }
+            //关联类型
+            if (!DbUtil.isEmptyOrNull(join)) {
+                join.forEach((fieldName, clzType) -> {
+                    //java field => Account account
+                    //sql field =>account_id,account_name
+                    try {
+                        Field field = fieldMap.get(fieldName);
+                        One one = field.getAnnotation(One.class);
+                        if (null != one && !one.noQuery() && !DbUtil.isGenericType(clzType)) {
+                            String prefix = field.getName();
+                            Object o = field.getType().newInstance();
+                            Set<Field> oneFields = DbUtil.getEntityFields(field.getType());
+//                            Field[] oneFields = field.getType().getFields();
+                            boolean oneSet = false;
+                            for (Field oneField : oneFields) {
+                                if (oneField.isAnnotationPresent(DBF.class)) {
+                                    String oneKey = prefix + "_" + getColumnName(oneField.getName());
+                                    Object oneValue = map.get(oneKey);
+                                    if (null == oneValue) {
+                                        continue;
+                                    }
+                                    setFieldValue(o, oneField.getName(), oneValue);
+                                    oneSet = true;
+                                }
+                            }
+                            if (oneSet) {
+                                setFieldValue(t, field.getName(), o);
+                            }
+                        }
+                        if (!DbUtil.isEmptyOrNull(map.get(fieldName))) {
+                            Object joinObj = JacksonUtil.toJavaObject(map.get(fieldName).toString(), clzType);
+                            setFieldValue(t, fieldName, joinObj);
+                        }
+                    } catch (Exception e) {
+                        logger.error("SQLHandler ERR# deal with db join reuslt by map2object,field:" + fieldName, e);
+                    }
+
+                });
+            }
+            return t;
+        } catch (InstantiationException | IllegalAccessException e) {
+            logger.error("SQLHandler ERR# deal with db reuslt by map2object", e);
+            throw new IllegalStateException(e);
+        }
+    }
+
+    private <T> List<T> map2ObjectList(Class<T> clz, List<Map<String, Object>> list) {
+        List<T> retList = new ArrayList<>(list.size());
+        if (!DbUtil.isEmptyOrNull(list)) {
+            list.forEach(map -> {
+                T t = map2Object(clz, map);
+                if (null != t) {
+                    retList.add(t);
+                }
+            });
+        }
+        return retList;
+    }
+
+    /**
+     * 关联集合查询并赋值
+     * 优化方案:一对多可以在联表查询时一并带出,不必进行二次查询
+     */
+    private void setJoinList(Object t, Class<?> clz, List<String> exclude) {
+        Field[] fields = clz.getDeclaredFields();
+        for (Field field : fields) {
+            if (field.isAnnotationPresent(Many.class) && !field.getAnnotation(Many.class).noQuery()) {
+                String fieldName = field.getName();
+                if (!DbUtil.isEmptyOrNull(exclude) && exclude.contains(fieldName)) {
+                    continue;
+                }
+                Many manyAnnotation = field.getAnnotation(Many.class);
+                String fkField = manyAnnotation.mkf();
+                Object value = getFieldValue(t, fkField);
+                List<?> list = selectList(manyAnnotation.te(), OBuilder.build().eq(manyAnnotation.tkf(), value));
+                setFieldValue(t, fieldName, list);
+            }
+        }
+    }
+
+
+    @Override
+    public String getHandlerType() {
+        return IHandler.JDBC;
+    }
+}

+ 1733 - 0
car-wash-jdbc/src/main/java/com/kym/jdbc/template/JdbcHelper.java

@@ -0,0 +1,1733 @@
+package com.kym.jdbc.template;
+
+import com.kym.DbUtil;
+import com.kym.JacksonUtil;
+import com.kym.jdbc.BasicQuery;
+import com.kym.jdbc.IHandler;
+import com.kym.jdbc.OBuilder;
+import com.kym.jdbc.SQLEntity;
+import com.kym.jdbc.annotations.DBF;
+import com.kym.jdbc.annotations.Entity;
+import com.kym.jdbc.annotations.FK;
+import com.kym.jdbc.annotations.JoinType;
+import com.kym.jdbc.annotations.Many;
+import com.kym.jdbc.annotations.OP;
+import com.kym.jdbc.annotations.One;
+import com.kym.jdbc.annotations.QE;
+import com.kym.jdbc.annotations.QF;
+import com.kym.jdbc.annotations.QueryType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+
+/**
+ * SQL动态解析器
+ * <p>
+ * jdbcTemplate模式
+ *
+ * @author yaop
+ */
+public class JdbcHelper {
+    private final Logger logger = LoggerFactory.getLogger(JdbcHelper.class);
+
+    public static final int DIALECT_MYSQL = 1;
+    public static final int DIALECT_MSSQL = 2;
+    public static final int DIALECT_ORACLE = 3;
+    public static final int DIALECT_PGSQL = 4;
+
+    public static final String[] BASE_IGNORE = {"createAt", "updateAt", "createBy", "updateBy"};
+
+    private static final String DEFAULT_MKF = "id";
+
+    private String comma = "`";
+
+    private int dialect = 1;
+    /***
+     * 表名前缀
+     */
+    public static String tbPrefix = "t";
+
+    private static Function<String, String> func;
+
+
+    /**
+     * 设置解析器的SQL方言类型,默认MYSQL
+     */
+    public void setDialect(int dialectType) {
+        this.dialect = dialectType;
+        OBuilder.dialect = dialectType;
+        switch (dialectType) {
+            case IHandler.DIALECT_MYSQL -> this.comma = "`";
+            case IHandler.DIALECT_MSSQL -> this.comma = "'";
+            case IHandler.DIALECT_ORACLE -> this.comma = "\"";
+            case IHandler.DIALECT_PGSQL -> this.comma = "\"";
+            default -> {
+            }
+        }
+    }
+
+    public int getDialect() {
+        return dialect;
+    }
+
+    /**
+     * 设置表名前缀
+     */
+    public static void setFunc(Function<String, String> func) {
+        JdbcHelper.func = func;
+    }
+
+    public static void setTablePrefix(String prefix) {
+        JdbcHelper.tbPrefix = prefix;
+    }
+
+
+    public static String getTbPrefix() {
+        return JdbcHelper.tbPrefix;
+    }
+
+
+    //region json处理
+
+
+    //endregion
+
+    //region select
+    public SQLEntity selectList(String sql, Object... params) {
+        SQLEntity entity = new SQLEntity();
+      /*  if (!DbUtil.isEmptyOrNull(params)) {
+            for (Object param : params) {
+                if (param.getClass() == String.class) {
+                    sql = sql.replaceFirst("\\?", "'" + DbUtil.injectDefend(param.toString()) + "'");
+                } else if (param.getClass() == Date.class) {
+                    sql = sql.replaceFirst("\\?", "'" + new Timestamp(((Date) param).getTime()) + "'");
+                } else {
+                    sql = sql.replaceFirst("\\?", DbUtil.injectDefend(param.toString()));
+                }
+            }
+        }*/
+        entity.sql = sql;
+        entity.parameters = params;
+        return entity;
+    }
+
+
+    /**
+     * 聚合函数查询<p>
+     * 注意:聚合函数的查询需要使用子查询</p>
+     *
+     * @param clz
+     * @param func    聚合查询字段 count(1) sum(amt)
+     * @param builder
+     * @return
+     */
+    public SQLEntity selectFunc(Class<?> clz, String func, OBuilder builder) {
+        SQLEntity entity = new SQLEntity();
+        List<Object> parameters = new ArrayList<>(16);
+        boolean distinct = distinctAggerate(func);
+        StringBuilder sbr = new StringBuilder();
+        boolean originEntity = !clz.getSimpleName().endsWith("Info");
+        if (originEntity) {
+            String funcField = func;
+            if (!distinct) {
+                funcField = func.contains("COUNT(") ? "1" : func.trim().split("\\(")[1].split("\\)")[0].trim();
+            }
+            sbr.append("SELECT ").append(funcField).append(" FROM ").append(getTableName(clz, clz.getSimpleName()));
+            if (null != builder) {
+                if (!DbUtil.isEmptyOrNull(builder.getWheres())) {
+                    List<OBuilder.Where> wheres = builder.getWheres();
+                    if (!DbUtil.isEmptyOrNull(wheres)) {
+                        sbr.append(" \r\n WHERE 1=1");
+                        wheres.forEach(wh -> {
+                            sbr.append(" AND ");
+                            appendBuilderWhere(sbr, wh, "", parameters);
+                        });
+                    }
+                }
+                //GROUP BY
+                if (!DbUtil.isEmptyOrNull(builder.getGroupBys())) {
+                    sbr.append("  \r\n GROUP BY ");
+                    builder.getGroupBys().forEach(group -> {
+                        sbr.append(group).append(" ,");
+                    });
+                    sbr.deleteCharAt(sbr.length() - 1);
+                }
+                //ORDER BY
+                if (!DbUtil.isEmptyOrNull(builder.getOrderBys())) {
+                    sbr.append(" ORDER BY ");
+                    builder.getOrderBys().forEach(order -> sbr.append(order).append(" ,"));
+                    sbr.deleteCharAt(sbr.length() - 1);
+                }
+
+                //count不分页
+                if (!func.contains("COUNT(")) {
+                    //分页
+                    String result = appendWhereLimit(builder, sbr);
+                    if (null != result) {
+                        if (!distinct) {
+                            entity.sql = "SELECT " + func + " FROM (" + result + ") temp";
+                        } else {
+                            entity.sql = result;
+                        }
+                    }
+                }
+            }
+            if (!distinct) {
+                entity.sql = "SELECT " + func + " FROM (" + sbr.toString() + ") temp";
+            } else {
+                entity.sql = sbr.toString();
+            }
+        } else {
+            //支持聚合查询的关联
+            String queryDomain = clz.getSuperclass().getCanonicalName() + "$" + clz.getSuperclass().getSimpleName() + "BasicQuery";
+            try {
+                Object obj = Class.forName(queryDomain).newInstance();
+                if (!DbUtil.isEmptyOrNull(func)) {
+                    DbUtil.setFieldValue(obj, obj.getClass().getField("pageSize"), -1);
+                }
+                return select((BasicQuery) obj, builder, null, null, func, true);
+            } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | NoSuchFieldException e) {
+                logger.error("SQL2Helper ERR# initialize class,class:{}", queryDomain);
+                throw new IllegalStateException(e);
+            }
+
+        }
+        entity.parameters = parameters.toArray();
+        return entity;
+    }
+
+    /**
+     * 聚合函数查询
+     */
+    public SQLEntity selectDistinct(Class<?> clz, String func, OBuilder OBuilder) {
+        return selectFunc(clz, func, OBuilder);
+    }
+
+    public SQLEntity selectListByWhere(Class<?> clz, OBuilder OBuilder, List<String> include, List<String> exclude, boolean isList) {
+        return selectListByWhereV2(clz, OBuilder, include, exclude, null, isList);
+    }
+
+
+    public SQLEntity selectListByWhereV2(Class<?> clz, OBuilder OBuilder, List<String> include, List<String> exclude, String aggregate, boolean isList) {
+        SQLEntity sqlEntity = new SQLEntity();
+        List<Object> parameters = new ArrayList<>();
+        Set<Field> fields = DbUtil.getEntityFields(clz);
+        boolean isSimple = !clz.getSimpleName().endsWith("Info");
+        if (isSimple) {
+            Entity entity = clz.getAnnotation(Entity.class);
+            StringBuilder sbr = new StringBuilder();
+            if (!DbUtil.isEmptyOrNull(aggregate)) {
+                sbr.append("SELECT ").append(aggregate).append(" FROM ").append(getTableName(entity));
+            } else {
+                if (DbUtil.isEmptyOrNull(include)) {
+                    if (DbUtil.isEmptyOrNull(exclude)) {
+                        sbr.append("SELECT * FROM ").append(getTableName(entity));
+                    } else {
+                        sbr.append("SELECT ");
+                        for (Field field : fields) {
+                            if (DbUtil.isUsageField(field)) {
+                                //richId  rich2Id 不关联查
+                                String fileName = field.getName();
+                                if (!exclude.contains(fileName)) {
+                                    sbr.append(this.comma).append(DbUtil.getColumnName(fileName)).append(this.comma).append(" ,");
+                                }
+                            }
+                        }
+                        sbr.deleteCharAt(sbr.length() - 1);
+                        sbr.append("  \n FROM ").append(getTableName(entity));
+                    }
+                } else {
+                    sbr.append("SELECT ");
+                    for (Field field : fields) {
+                        if (DbUtil.isUsageField(field)) {
+                            String fileName = field.getName();
+                            if (include.contains(fileName)) {
+                                sbr.append(this.comma).append(DbUtil.getColumnName(fileName)).append(this.comma).append(" ,");
+                            }
+                        }
+                    }
+                    sbr.deleteCharAt(sbr.length() - 1);
+                    sbr.append("  \n FROM ").append(getTableName(entity));
+                }
+            }
+            if (null != OBuilder && !DbUtil.isEmptyOrNull(OBuilder.getWheres())) {
+                List<OBuilder.Where> wheres = OBuilder.getWheres();
+                if (!DbUtil.isEmptyOrNull(wheres)) {
+                    sbr.append(" \r\n WHERE 1=1");
+                    wheres.forEach(wh -> {
+                        sbr.append(" AND ");
+                        appendBuilderWhere(sbr, wh, "", parameters);
+                    });
+                }
+            }
+
+
+            if (isList) {
+                //ORDER BY
+                if (null != OBuilder && !DbUtil.isEmptyOrNull(OBuilder.getOrderBys())) {
+                    sbr.append(" ORDER BY ");
+                    OBuilder.getOrderBys().forEach(order -> sbr.append(order).append(" ,"));
+                    sbr.deleteCharAt(sbr.length() - 1);
+                } else {
+                    sbr.append(" ORDER BY update_at DESC ");
+                }
+
+                //limit
+                String result = appendWhereLimit(OBuilder, sbr);
+                if (null != result) {
+                    sqlEntity.sql = result;
+                }
+            }
+
+            sbr.append(";");
+            sqlEntity.sql = sbr.toString();
+        } else {
+            String queryDomain = clz.getSuperclass().getCanonicalName() + "$" + clz.getSuperclass().getSimpleName() + "BasicQuery";
+            Object obj;
+            try {
+                obj = Class.forName(queryDomain).newInstance();
+                if (!DbUtil.isEmptyOrNull(aggregate)) {
+                    DbUtil.setFieldValue(obj, obj.getClass().getField("pageSize"), -1);
+                }
+            } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | NoSuchFieldException e) {
+                logger.error("SQL2Helper ERR# initialize class,class:{}", queryDomain);
+                throw new IllegalStateException(e);
+            }
+            return select((BasicQuery) obj, OBuilder, include, exclude, aggregate, isList);
+        }
+        sqlEntity.parameters = parameters.toArray();
+        return sqlEntity;
+    }
+
+
+    public SQLEntity selectListByQuery(BasicQuery query, List<String> include, List<String> exclude) {
+        return select(query, null, include, exclude, null, true);
+    }
+
+
+    /**
+     * 查询SQL解析生成
+     *
+     * @param query     query条件对象
+     * @param OBuilder  where条件对象
+     * @param include   指定查询字段集合
+     * @param exclude   指定过滤不查询字段集合
+     * @param aggregate 聚合查询条件
+     */
+    private SQLEntity select(BasicQuery query, OBuilder OBuilder, List<String> include, List<String> exclude, String aggregate, boolean isList) {
+        SQLEntity sqlEntity = new SQLEntity();
+        List<Object> parameters = new ArrayList<>(16);
+        if (!DbUtil.isEmptyOrNull(query.includeFields)) {
+            if (DbUtil.isEmptyOrNull(include)) {
+                include = new ArrayList<>();
+            }
+            for (String includeField : query.includeFields) {
+                if (!include.contains(includeField)) {
+                    include.add(includeField);
+                }
+            }
+        }
+        if (!DbUtil.isEmptyOrNull(query.excludeFields)) {
+            if (DbUtil.isEmptyOrNull(exclude)) {
+                exclude = new ArrayList<>();
+            }
+            for (String excludeField : query.excludeFields) {
+                if (!exclude.contains(excludeField)) {
+                    exclude.add(excludeField);
+                }
+            }
+        }
+        StringBuilder sbr = new StringBuilder();
+        int aliasIdx = 0;
+        //表别名 @QF->inner @FK、@One->left
+        Map<String, String> alias = new HashMap<>(4);
+        //关联表名
+        Map<String, String> tbs = new HashMap<>(4);
+        QE qEAnnotation = query.getClass().getAnnotation(QE.class);
+        String queryClassName = query.getClass().getSimpleName();
+        if (null == qEAnnotation) {
+            throw new IllegalStateException("query entity of " + queryClassName + " @Query Entity is not defined!");
+        }
+        Class<?> infoClass = qEAnnotation.clz();
+        Entity entity = infoClass.getSuperclass().getAnnotation(Entity.class);
+//        Field[] fields = infoClass.getFields();
+        Set<Field> fields = DbUtil.getEntityFields(infoClass);
+        Set<Field> qfields = DbUtil.getQueryFields(query.getClass());
+//        Field[] qfields = query.getClass().getFields();
+        //关联表别名
+        //------1.查询字段
+        for (Field field : fields) {
+            String fieldName = field.getName();
+            //常量字段过滤
+            if (!DbUtil.isUsageField(field)) {
+                continue;
+            }
+            One oneAnnotation = field.getAnnotation(One.class);
+            Many manyAnnotation = field.getAnnotation(Many.class);
+            boolean isExtenstionField = null != oneAnnotation || null != manyAnnotation;
+            //忽略字段过滤
+            if (!DbUtil.isEmptyOrNull(exclude) && exclude.contains(fieldName) && !isExtenstionField) {
+                continue;
+            }
+            //指定查询
+            if (!DbUtil.isEmptyOrNull(include) && !include.contains(fieldName) && !isExtenstionField) {
+                continue;
+            }
+            DBF DBFAnnotation = field.getAnnotation(DBF.class);
+            if (null != DBFAnnotation) {
+                FK foreignKey = field.getAnnotation(FK.class);
+                if (null != foreignKey) {
+                    Class<?> entityClass = foreignKey.clz();
+                    Entity ey = entityClass.getAnnotation(Entity.class);
+                    for (Field qfield : qfields) {
+                        if (qfield.isAnnotationPresent(QF.class)) {
+                            QF qf = qfield.getAnnotation(QF.class);
+                            String mkf = qf.pkf();
+                            if (!DbUtil.isEmptyOrNull(mkf) && mkf.equals(fieldName)) {
+                                if (null != DbUtil.getFieldValue(query, qfield)) {
+                                    alias.putIfAbsent(fieldName, "i" + aliasIdx++);
+                                    tbs.putIfAbsent(fieldName, getTableName(ey));
+                                }
+                            }
+                        }
+                    }
+                    alias.putIfAbsent(fieldName, "l" + aliasIdx++);
+                    tbs.putIfAbsent(fieldName, getTableName(ey));
+                }
+            } else {
+                if (null != oneAnnotation) {
+                    String mkf = oneAnnotation.mkf();
+                    if (!DbUtil.isGenericType(field.getType()) && DEFAULT_MKF.equals(mkf)) {
+                        mkf = fieldName;
+                    }
+                    JoinType joinType = oneAnnotation.join();
+                    if (JoinType.LEFT == joinType) {
+                        alias.put(mkf, "l" + aliasIdx++);
+                    } else if (JoinType.INNER == joinType) {
+                        alias.put(mkf, "i" + aliasIdx++);
+                    } else if (JoinType.RIGHT == joinType) {
+                        alias.put(mkf, "r" + aliasIdx++);
+                    }
+                }
+            }
+        }
+        //非聚合查询
+        if (DbUtil.isEmptyOrNull(aggregate)) {
+            sbr.append("SELECT");
+            //------1.查询主表字段
+            boolean appendFields = false;
+            for (Field field : fields) {
+                String fieldName = field.getName();
+                //常量字段过滤
+                if (!DbUtil.isUsageField(field)) {
+                    continue;
+                }
+                boolean isExtenstionField = field.isAnnotationPresent(One.class) || field.isAnnotationPresent(Many.class);
+                //忽略字段过滤
+                if (!DbUtil.isEmptyOrNull(exclude) && exclude.contains(fieldName) && !isExtenstionField) {
+                    continue;
+                }
+                //指定查询
+                if (!DbUtil.isEmptyOrNull(include) && !include.contains(fieldName) && !isExtenstionField) {
+                    continue;
+                }
+                DBF DBFAnnotation = field.getAnnotation(DBF.class);
+                if (null != DBFAnnotation) {
+                    if (appendFields) {
+                        sbr.append(",");
+                    }
+                    sbr.append(" l.").append(this.comma).append(DbUtil.getColumnName(fieldName)).append(this.comma);
+                    appendFields = true;
+                }
+            }
+            //-------2.查询关联字段
+            for (Field field : fields) {
+                Class<?> type = field.getType();
+                String fieldName = field.getName();
+                //常量字段过滤
+                if (!DbUtil.isUsageField(field)) {
+                    continue;
+                }
+                boolean isExtenstionField = field.isAnnotationPresent(One.class) || field.isAnnotationPresent(Many.class);
+                //忽略字段过滤
+                if (!DbUtil.isEmptyOrNull(exclude) && exclude.contains(fieldName)) {
+                    continue;
+                }
+                //指定查询
+                if (!DbUtil.isEmptyOrNull(include) && !include.contains(fieldName)) {
+                    continue;
+                }
+                Class<?> fieldClass = field.getType();
+                One oneAnnotation = field.getAnnotation(One.class);
+                if (null != oneAnnotation) {
+                    //忽略查询
+                    if (oneAnnotation.noQuery()) {
+                        continue;
+                    }
+                    String fkField = oneAnnotation.mkf();
+                    if (!DbUtil.isGenericType(field.getType()) && DEFAULT_MKF.equals(fkField)) {
+                        fkField = fieldName;
+                    }
+                    // join字段
+                    if (!DbUtil.isEmptyOrNull(fkField)) {
+                        //1.join查询对象
+                        if (!DbUtil.isGenericType(type) && type != String.class && !type.isArray() && !type.isAssignableFrom(List.class)) {
+//                            alias.putIfAbsent(fkField, "l" + aliasIdx++);
+                            Field[] joinFields = fieldClass.getFields();
+                            for (Field joinField : joinFields) {
+                                if (DbUtil.isUsageField(joinField)) {
+                                    if (!"SELECT".contentEquals(sbr)) {
+                                        sbr.append(",");
+                                    }
+                                    sbr.append(alias.get(fkField)).append(".").append(this.comma).append(DbUtil.getColumnName(joinField.getName())).append(this.comma).append(" AS ").append(this.comma).append(fieldName).append("_").append(DbUtil.getColumnName(joinField.getName())).append(this.comma);
+                                }
+                            }
+
+                            //2.join查询变量
+                        } else {
+                            if (!"SELECT".equals(sbr.toString().trim())) {
+                                sbr.append(",");
+                            }
+                            sbr.append(alias.get(fkField)).append(".").append(this.comma).append(DbUtil.getColumnName(oneAnnotation.tf())).append(this.comma).append(" AS ").append(this.comma).append(DbUtil.getColumnName(fieldName)).append(this.comma);
+                        }
+                    }
+                }
+            }
+        } else {
+           /* //聚合查询需要获取关联表别名
+            for (Field field : fields) {
+                if (field.isAnnotationPresent(Fk.class)) {
+                    alias.putIfAbsent(field.getName(), "l" + aliasIdx++);
+                }
+            }*/
+
+            String aggregateField = aggregate;
+            boolean distinct = distinctAggerate(aggregate);
+            if (!distinct) {
+                aggregateField = aggregate.contains("COUNT(") ? "l.*" : aggregate.trim().split("\\(")[1].split("\\)")[0].trim();
+            }
+            sbr.append("SELECT ").append(aggregateField);
+        }
+
+        sbr.append("   \n FROM ").append(this.comma).append(getTableName(entity)).append(this.comma).append(" AS l");
+        List<String> joined = new ArrayList<>();
+        //-------3.关联对象  JOINS
+        //BUG:涉及到关联查询的对象字段需要inner join @QF
+        Field[] qfs = query.getClass().getDeclaredFields();
+        for (Field qf : qfields) {
+            //qf 内联查询
+            if (qf.isAnnotationPresent(QF.class)) {
+                Object val = DbUtil.getFieldValue(query, qf);
+                if (null != val) {
+                    String amkf = qf.getAnnotation(QF.class).pkf();
+                    if (!DbUtil.isEmptyOrNull(amkf)) {
+//                        if (!joinFields.contains(amkf)) {
+//                            joinFields.add(amkf);
+//                            alias.putIfAbsent(amkf, "i" + aliasIdx++);
+                        if (!joined.contains(alias.get(amkf))) {
+                            joined.add(alias.get(amkf));
+                        } else {
+                            continue;
+                        }
+                        Class<?> aliasClass = getFkDomainClass(qf, infoClass);
+                        sbr.append(" INNER JOIN ").append(getTableName(aliasClass, aliasClass.getSimpleName())).append(" AS ").append(alias.get(amkf)).append(" ON ").append("l.").append(this.comma).append(DbUtil.getColumnName(amkf)).append(this.comma).append("=").append(alias.get(amkf)).append(".").append(this.comma).append(DbUtil.getColumnName(qf.getAnnotation(QF.class).tkf())).append(this.comma);
+                    }
+                }
+//                }
+            }
+        }
+        for (Field field : fields) {
+            String fieldName = field.getName();
+            //忽略查询
+            if (!DbUtil.isEmptyOrNull(exclude) && exclude.contains(fieldName)) {
+                continue;
+            }
+            One oneAnnotation = field.getAnnotation(One.class);
+            if (null != oneAnnotation) {
+                //忽略查询
+                if (oneAnnotation.noQuery()) {
+                    continue;
+                }
+                if (!DbUtil.isUsageField(field)) {
+                    continue;
+                }
+                String mkf = oneAnnotation.mkf();
+                if (!DbUtil.isGenericType(field.getType()) && DEFAULT_MKF.equals(mkf)) {
+                    mkf = fieldName;
+                }
+                //joins
+                if (!DbUtil.isEmptyOrNull(mkf)) {
+                    if (!joined.contains(alias.get(mkf))) {
+                        joined.add(alias.get(mkf));
+                    } else {
+                        continue;
+                    }
+//                    joinFields.add(fkField);
+                    Class<?> aliasClass = getFkDomainClass(field, infoClass);
+//                    alias.putIfAbsent(fkField, "l" + aliasIdx++);
+                    if (oneAnnotation.join() == JoinType.LEFT) {
+                        sbr.append(" \n LEFT JOIN ");
+                    } else if (oneAnnotation.join() == JoinType.INNER) {
+                        sbr.append("  \n  INNER JOIN  ");
+                    } else {
+                        sbr.append("  \n RIGHT JOIN ");
+                    }
+                    sbr.append(getTableName(aliasClass, aliasClass.getSimpleName())).append(" AS ").append(alias.get(mkf)).append(" ON ").append("l.").append(this.comma).append(DbUtil.getColumnName(oneAnnotation.mkf())).append(this.comma).append("=").append(alias.get(mkf)).append(".").append(this.comma).append(DbUtil.getColumnName(oneAnnotation.tkf())).append(this.comma);
+                }
+            }
+        }
+        //-------4.查询条件
+        sbr.append(" \n WHERE 1=1 ");
+        Field[] queryFields = query.getClass().getDeclaredFields();
+        //query条件和Builder条件仅支持一个(查询时只提供一个条件构造实体)
+        if (null != OBuilder && !DbUtil.isEmptyOrNull(OBuilder.getWheres())) {
+            List<OBuilder.Where> wheres = OBuilder.getWheres();
+            wheres.forEach(wh -> {
+                //联表查询字段
+                String aliasName = alias.getOrDefault(getMkf(query, wh), "l");
+                sbr.append(" AND ");
+                appendBuilderWhere(sbr, wh, aliasName, parameters);
+            });
+        } else {
+            for (Field qf : queryFields) {
+                Object val = DbUtil.getFieldValue(query, qf);
+                String fieldName = qf.getName();
+                if (DbUtil.isEmptyOrNull(val)) {
+                    continue;
+                }
+                QF qfAnnotation = qf.getAnnotation(QF.class);
+                if (null == qfAnnotation) {
+                    if (!fieldName.endsWith("Sort")) {
+                        sbr.append(" AND l.").append(this.comma).append(DbUtil.getColumnName(fieldName)).append(this.comma);
+                        if (String.class.equals(qf.getType())) {
+                            if (DIALECT_MYSQL == dialect) {
+                                sbr.append(" LIKE concat('%',?,'%')");
+                            } else if (DIALECT_PGSQL == dialect) {
+                                sbr.append(" like concat('%',?::text,'%')");
+                            } else if (DIALECT_MSSQL == dialect) {
+                                sbr.append(" LIKE '%'+?+'%')");
+                            } else if (DIALECT_ORACLE == dialect) {
+                                sbr.append(" LIKE '%'||?||'%')");
+                            }
+                            parameters.add(val);
+                        } else {
+                            sbr.append(" = ?");
+                            parameters.add(val);
+//                            .append(val);
+                        }
+                    }
+                } else {
+                    //foreignKeys
+                    String mkf = qfAnnotation.pkf();
+                    if (qfAnnotation.qt() == QueryType.IGNORE) {
+                        continue;
+                    }
+                    if (!DbUtil.isEmptyOrNull(mkf)) {
+                        String tf = qfAnnotation.tf();
+                        if (DbUtil.isEmptyOrNull(tf)) {
+                            logger.error("SQL2Helper ERR# [" + fieldName + "] @QF with mkf define,but without tkf exist!!!");
+                        } else {
+                            if (qfAnnotation.qt() == QueryType.MAIN) {
+                                sbr.append(" AND ").append(" l").append(".").append(this.comma).append(DbUtil.getColumnName(mkf)).append(this.comma);
+                            } else {
+                                sbr.append(" AND ").append(alias.get(mkf)).append(".").append(this.comma).append(DbUtil.getColumnName(tf)).append(this.comma);
+                            }
+                            //字符串使用模糊查询
+                            if (String.class.equals(qf.getType())) {
+                                if (DIALECT_MYSQL == dialect) {
+                                    sbr.append(" like concat('%',?,'%')");
+                                } else if (DIALECT_PGSQL == dialect) {
+                                    sbr.append(" like concat('%',?::text,'%')");
+                                } else if (DIALECT_MSSQL == dialect) {
+                                    sbr.append(" like '%'+'?'+'%')");
+                                } else if (DIALECT_ORACLE == dialect) {
+                                    sbr.append(" like '%'||'?'||'%')");
+                                }
+                                parameters.add(val);
+                            } else {
+                                sbr.append(" =");
+//                                parameters.add(val);
+                                getValue(sbr, val, parameters);
+                            }
+                        }
+
+                    }
+                    //sql
+                    String whereSql = qfAnnotation.sql();
+                    String tbAlias = alias.getOrDefault(qfAnnotation.pkf(), "l");
+                    whereSql = appendWhereSql(whereSql, Collections.singletonList(val), tbAlias, parameters);
+                    if (!DbUtil.isEmptyOrNull(whereSql)) {
+                        sbr.append(" AND ").append(whereSql);
+                    }
+                    //op
+                    String operator = qfAnnotation.op();
+                    if (!DbUtil.isEmptyOrNull(operator)) {
+                        String tf = qfAnnotation.tf();
+                        if (DbUtil.isEmptyOrNull(tf)) {
+                            logger.error("SQL2Helper ERR# " + fieldName + "@QF with operator defined,but without tkf exist!!!");
+                        } else {
+                            //JIN  JINT
+                            if (operator.equals(OP.JIN)) {
+                                int loop = 1;
+                                String each = qfAnnotation.each();
+                                if (val instanceof Collection<?>) {
+                                    List<Object> list = JacksonUtil.toList(val);
+                                    loop = list.size();
+
+                                    parameters.addAll((Collection<?>) val);
+                                } else if (val.getClass().isArray()) {
+                                    loop = Array.getLength(val);
+                                    for (int i = 0; i < loop; i++) {
+                                        parameters.add(Array.get(val, i));
+                                    }
+                                } else {
+                                    loop = 1;
+                                    parameters.add(val);
+                                }
+                                // and ( json_contains(l.id_list,'1'))
+                                if (dialect == IHandler.DIALECT_MYSQL) {
+                                    sbr.append(" AND ").append("( ");
+                                    for (int i = 0; i < loop; i++) {
+                                        sbr.append(" (JSON_CONTAINS(").append(tbAlias).append(".").append(this.comma).append(DbUtil.getColumnName(tf)).append(this.comma).append(",").append("JSON_ARRAY(?)) ");
+                                        if (i != loop - 1) {
+                                            sbr.append(each);
+                                        }
+                                    }
+                                    sbr.append(") ");
+                                    //  owner_id_list::jsonb @> jsonb_build_array(2)
+                                    //  owner_id_list::jsonb @> '[2]'
+                                } else if (dialect == IHandler.DIALECT_PGSQL) {
+                                    sbr.append(" AND ").append("( ");
+                                    for (int i = 0; i < loop; i++) {
+                                        sbr.append(" (").append(tbAlias).append(".").append(this.comma).append(DbUtil.getColumnName(tf)).append(this.comma).append("::jsonb @>  ").append("jsonb_build_array(?)) ");
+                                        if (i != loop - 1) {
+                                            sbr.append(each);
+                                        }
+                                    }
+                                    sbr.append(") ");
+                                }
+
+                            } else if (operator.equals(OP.JINT)) {
+
+                                //and (JSON_OVERLAPS(role,'["a","d"]')=1)
+                                if (dialect == IHandler.DIALECT_MYSQL) {
+                                    sbr.append(" AND ").append("(").append(operator).append("JSON_OVERLAPS(").append(tbAlias).append(".").append(this.comma).append(DbUtil.getColumnName(tf)).append(this.comma).append(",").append("'?')=1").append(")");
+                                    parameters.add(JacksonUtil.toJSONString(val));
+                                } else if (dialect == IHandler.DIALECT_PGSQL) {
+                                    //    //ARRAY[1,4,3] && ARRAY[2,1]
+                                    sbr.append(" AND ").append("(").append(tbAlias).append(".").append(this.comma).append(DbUtil.getColumnName(tf)).append(this.comma).append("::jsonb && ").append("jsonb_build_array(?))");
+                                    parameters.add(JacksonUtil.toJSONString(val));
+                                }
+                            } else if (operator.equals(OP.IN) || operator.equals(OP.NIN)) {
+                                sbr.append(" AND ").append("(").append(tbAlias).append(".").append(this.comma).append(DbUtil.getColumnName(tf)).append(this.comma).append(" ").append(operator).append("  (");
+                                if (val instanceof Collection<?>) {
+                                    List<Object> list = JacksonUtil.toList(val);
+                                    String collect = list.stream().map(k -> "?").collect(Collectors.joining(","));
+                                    sbr.append(collect).append(") ) ");
+                                    parameters.addAll((Collection<?>) val);
+                                } else if (val.getClass().isArray()) {
+                                    int length = Array.getLength(val);
+                                    for (int i = 0; i < length; i++) {
+                                        sbr.append("?");
+                                        if (i != length - 1) {
+                                            sbr.append(",");
+                                        }
+                                        parameters.add(Array.get(val, i));
+                                    }
+                                    sbr.append("))");
+                                } else {
+                                    throw new IllegalArgumentException("不支持的参数格式");
+                                }
+
+                            } else if (operator.equals(OP.RLK) || operator.equals(OP.LK) || operator.equals(OP.LLK)) {
+                                sbr.append(" AND ").append(tbAlias).append(".").append(this.comma).append(DbUtil.getColumnName(tf)).append(this.comma).append(" ").append(operator);
+                                parameters.add(val);
+                            } else {
+                                sbr.append(" AND ").append(tbAlias).append(".").append(this.comma).append(DbUtil.getColumnName(tf)).append(this.comma).append(" ").append(operator);
+                                if (qf.getType().isArray()) {
+                                    sbr.append(" (");
+                                    int length = Array.getLength(val);
+                                    for (int i = 0; i < length; i++) {
+                                        Object value = Array.get(val, i);
+                                        getValue(sbr, value, parameters);
+                                        if (i != length - 1) {
+                                            sbr.append(",");
+                                        }
+                                    }
+                                    sbr.append(")");
+                                } else if (val instanceof Collection<?>) {
+                                    List<Object> list = JacksonUtil.toList(val);
+                                    String collect = list.stream().map(k -> "?").collect(Collectors.joining(","));
+                                    sbr.append(collect).append(")  ");
+                                    parameters.addAll((Collection<?>) val);
+                                } else {
+                                    getValue(sbr, val, parameters);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+
+        //-------6.GROUP BY
+        if (null != OBuilder && !DbUtil.isEmptyOrNull(OBuilder.getGroupBys())) {
+            sbr.append(" GROUP BY ");
+            OBuilder.getGroupBys().forEach(group -> {
+                sbr.append(group).append(" ,");
+            });
+            sbr.deleteCharAt(sbr.length() - 1);
+        }
+
+        //-------5.having
+        //todo
+
+        //-------7.ORDER BY(query)
+        boolean ordered = false;
+        //聚合查询不需要排序
+        if (DbUtil.isEmptyOrNull(aggregate) && isList) {
+            //多个排序字段需要确定先后顺序
+            Map<Integer, String> sortMap = new HashMap<>(16);
+            if (DbUtil.isEmptyOrNull(query.sortFields)) {
+                for (Field qf : queryFields) {
+                    Object val = DbUtil.getFieldValue(query, qf);
+                    if (null == val) {
+                        continue;
+                    }
+                    int sortValue = 0;
+                    if (val instanceof Integer) {
+                        sortValue = (int) val;
+                    }
+                    if (sortValue == 0) {
+                        continue;
+                    }
+                    String fieldName = qf.getName();
+
+                    List<Field> sorted = new ArrayList<>();
+                    QF qfAnnotation = qf.getAnnotation(QF.class);
+                    if (null == qfAnnotation) {
+                        if (fieldName.endsWith("Sort")) {
+                            if (!ordered) {
+                                ordered = true;
+                                sbr.append(" ORDER BY ");
+                            }
+
+//                            sbr.append(" l.")
+//                                    .append(this.comma)
+//                                    .append(DbUtil.getColumnName(fieldName.substring(0, fieldName.length() - 4)))
+//                                    .append(this.comma);
+                            if (sortValue % 2 == BasicQuery.SORT_ASC) {
+                                sortMap.put(sortValue, String.format("%s ASC ", DbUtil.getColumnName(fieldName.substring(0, fieldName.length() - 4))));
+//                                sbr.append(" ASC ,");
+                            } else {
+//                                sbr.append(" DESC ,");
+                                sortMap.put(sortValue, String.format("%s DESC ", DbUtil.getColumnName(fieldName.substring(0, fieldName.length() - 4))));
+                            }
+                        }
+                    }
+
+                    if (!DbUtil.isEmptyOrNull(sortMap)) {
+                        Set<Integer> integers = sortMap.keySet();
+                        List<Integer> sorts = new ArrayList<>(integers);
+                        sorts.sort(Comparator.comparingInt(o -> o));
+                        sbr.append(sorts.stream().map(k -> "l." + sortMap.get(k)).collect(Collectors.joining(",")));
+                    }
+               /*     if (null == qfAnnotation) {
+                        if (fieldName.endsWith("Sort")) {
+                            if (!ordered) {
+                                ordered = true;
+                                sbr.append(" ORDER BY ");
+                            }
+                            sbr.append(" l.")
+                                    .append(this.comma)
+                                    .append(DbUtil.getColumnName(fieldName.substring(0, fieldName.length() - 4)))
+                                    .append(this.comma);
+                            if (sortType == BasicQuery.SORT_ASC) {
+                                sbr.append(" ASC ,");
+                            } else if (sortType == BasicQuery.SORT_DESC) {
+                                sbr.append(" desc ,");
+                            }
+                        }
+                    }*/
+                }
+            } else {
+                String[] sorts = query.sortFields;
+                for (String sortField : sorts) {
+                    try {
+                        Field sf = query.getClass().getDeclaredField(sortField + "Sort");
+                        Object val = DbUtil.getFieldValue(query, sf);
+                        if (null == val) {
+                            continue;
+                        }
+                        int sortType = (int) val;
+                        if (!ordered) {
+                            ordered = true;
+                            sbr.append(" ORDER BY ");
+                        }
+
+                        sbr.append(" l.").append(this.comma).append(DbUtil.getColumnName(sortField)).append(this.comma);
+                        if (sortType == BasicQuery.SORT_ASC) {
+                            sbr.append(" ASC ,");
+                        } else if (sortType == BasicQuery.SORT_DESC) {
+                            sbr.append(" DESC ,");
+                        }
+                    } catch (NoSuchFieldException e) {
+                        logger.warn("SQL2Helper warn# sort field is not exist,{}", sortField);
+                    }
+                }
+            }
+        }
+        //-------7.ORDER BY(builder)  聚合查询不需要排序
+        if (DbUtil.isEmptyOrNull(aggregate) && isList) {
+            if (null != OBuilder && !DbUtil.isEmptyOrNull(OBuilder.getOrderBys())) {
+                if (!ordered) {
+                    ordered = true;
+                    sbr.append(" ORDER BY ");
+                }
+                OBuilder.getOrderBys().forEach(order -> sbr.append(" l.").append(order).append(" ,"));
+            }
+            if (ordered && sbr.toString().endsWith(",")) {
+                sbr.deleteCharAt(sbr.length() - 1);
+            }
+            //------7.ORDER BY default update_at DESC
+            if (!ordered) {
+                sbr.append(" ORDER BY l.update_at DESC");
+            }
+        }
+
+        //-------8.limit(query)
+        if (isList) {
+            if (query.pageSize > 0) {
+                if (DIALECT_MYSQL == dialect) {
+                    sbr.append(" LIMIT ");
+                    if (query.pageIndex > 0) {
+                        sbr.append((query.pageIndex - 1) * query.pageSize).append(",");
+                    }
+                    if (query.pageSize > 0) {
+                        sbr.append(query.pageSize);
+                    }
+                } else if (DIALECT_MSSQL == dialect) {
+                    sbr.append(" OFFSET ");
+                    if (query.pageIndex > 0) {
+                        sbr.append((query.pageIndex - 1) * query.pageSize).append(" ROWS ");
+                    }
+                    if (query.pageSize > 0) {
+                        sbr.append(" FETCH NEXT ").append(query.pageSize).append(" ROWS ONLY  ");
+                    }
+                } else if (DIALECT_PGSQL == dialect) {
+                    sbr.append(" LIMIT ").append(query.pageSize);
+                    if (query.pageIndex > 0) {
+                        sbr.append(" OFFSET ").append((query.pageIndex - 1) * query.pageSize);
+                    }
+                } else if (DIALECT_ORACLE == dialect) {
+                    StringBuilder sr = new StringBuilder();
+                    sr.append("SELECT * FROM (SELECT o.*,ROWNUM as rowno FROM (").append(sbr).append(") o ");
+                    if (query.pageSize > 0) {
+                        sr.append(" WHERE ROWNUM<=").append(query.pageSize).append(")  o1");
+                    }
+                    if (query.pageIndex > 0) {
+                        sr.append(" WHERE o1.rowno>").append((query.pageIndex - 1) * query.pageSize).append(";");
+                    }
+                    //聚合类使用子查询
+                    if (!DbUtil.isEmptyOrNull(aggregate)) {
+                        sqlEntity.sql = "SELECT " + aggregate + " FROM (" + sr.toString() + ") temp";
+                    }
+                    sqlEntity.sql = sr.toString();
+                }
+                //-------8.limit(builder)
+            } else {
+                String result = appendWhereLimit(OBuilder, sbr);
+                if (null != result) {
+                    //聚合类使用子查询
+                    if (!DbUtil.isEmptyOrNull(aggregate) && !distinctAggerate(aggregate)) {
+                        sbr.setLength(0);
+                        sbr.append("SELECT ").append(aggregate).append(" FROM (").append(result).append(") temp");
+                    }
+                }
+            }
+
+        }
+
+        sqlEntity.sql = sbr.toString();
+        //聚合类使用子查询
+        if (!DbUtil.isEmptyOrNull(aggregate) && !distinctAggerate(aggregate)) {
+            String tmpSql = sbr.toString();
+            sbr.setLength(0);
+            sbr.append("SELECT ").append(aggregate).append(" FROM (").append(tmpSql).append(") temp");
+        }
+        sbr.append(";");
+
+        sqlEntity.sql = sbr.toString();
+        sqlEntity.parameters = parameters.toArray();
+        return sqlEntity;
+    }
+
+
+    private boolean distinctAggerate(String aggregate) {
+        return aggregate.toLowerCase().contains("distinct(");
+    }
+
+    private String getMkf(BasicQuery query, OBuilder.Where where) {
+        String fieldName = where.key;
+        if (DbUtil.isEmptyOrNull(fieldName)) {
+            if (!DbUtil.isEmptyOrNull(where.sql)) {
+                logger.warn("SQL#builder with sql foreign key is not support yet! {}", where.sql);
+            }
+            return null;
+        }
+        try {
+            //info
+            Class<?> infoClz = query.getClass().getAnnotation(QE.class).clz();
+            Field field = infoClz.getField(DbUtil.getCamelName(fieldName));
+            if (field.isAnnotationPresent(One.class)) {
+                One one = field.getAnnotation(One.class);
+                where.key = one.tf();
+                return one.mkf();
+            }
+        } catch (NoSuchFieldException e) {
+            try {
+                //query
+                Field field = query.getClass().getField(DbUtil.getCamelName(fieldName));
+                if (field.isAnnotationPresent(QF.class)) {
+                    QF qf = field.getAnnotation(QF.class);
+                    where.key = qf.tf();
+                    return qf.pkf();
+                }
+            } catch (NoSuchFieldException e1) {
+                return null;
+            }
+        }
+        return null;
+    }
+
+
+    public SQLEntity selectOne(String sql, Object... params) {
+        return selectList(sql, params);
+    }
+
+    public SQLEntity selectOneByQuery(BasicQuery query, List<String> include, List<String> exclude) {
+        return select(query, null, include, exclude, null, false);
+    }
+
+    public SQLEntity selectById(Class<?> clz, Object id, List<String> include, List<String> exclude) {
+        return selectListByWhere(clz, OBuilder.build().eq("id", id), include, exclude, false);
+    }
+
+    public SQLEntity selectOneByWhere(Class<?> clz, OBuilder OBuilder, List<String> include, List<String> exclude) {
+        return selectListByWhere(clz, OBuilder, include, exclude, false);
+    }
+
+    public SQLEntity selectCount(Class<?> clz, OBuilder OBuilder) {
+        return selectFunc(clz, "COUNT(1)", OBuilder);
+    }
+
+    public SQLEntity selectCountByQuery(BasicQuery query) {
+        return select(query, null, null, null, "COUNT(1)", false);
+    }
+//endregion select
+
+
+    //region insert
+
+    /**
+     * 存储过程
+     *
+     * @param sql
+     * @param params
+     * @return
+     */
+    public SQLEntity call(String sql, Object... params) {
+        SQLEntity sqlEntity = new SQLEntity();
+        sqlEntity.sql = sql;
+        sqlEntity.parameters = params;
+//        if (!DbUtil.isEmptyOrNull(params)) {
+//            sql = String.format(sql, params);
+//        }
+//    "CALL updateRole(#{id,jdbcType=VARCHAR,mode=IN},#{result,jdbcType=INTEGER,mode=OUT});";
+        return sqlEntity;
+    }
+
+    /**
+     * --------------insert method begin ---------------
+     */
+    public SQLEntity insertSelective(Object o, List<String> exclude) {
+        return insertWithCheck(o, true, false, exclude);
+    }
+
+    public SQLEntity insert(Object o, List<String> exclude) {
+        return insertWithCheck(o, false, false, exclude);
+    }
+
+    public SQLEntity insertWithGenKey(Object o, List<String> exclude) {
+        return insertWithCheck(o, false, true, exclude);
+    }
+
+    /***
+     * 插入方法
+     * @param o 插入实体对象
+     * @param ignoreNull 忽略空字段
+     * @param withAutoIncreamentKey 对象是否已赋值自增主键
+     * @param exclude 忽略字段
+     * @return
+     */
+    private SQLEntity insertWithCheck(Object o, boolean ignoreNull, boolean withAutoIncreamentKey, List<String> exclude) {
+        SQLEntity sqlEntity = new SQLEntity();
+        List<Object> parameters = new ArrayList<>();
+        StringBuilder sbr = new StringBuilder("INSERT INTO ");
+        sbr.append(getTableName(o.getClass(), o.getClass().getSimpleName()));
+        sbr.append("(");
+
+        StringBuilder vb = new StringBuilder("(");
+        //自带主键
+        if (withAutoIncreamentKey) {
+            sbr.append("id,");
+            try {
+                parameters.add(DbUtil.getFieldValue(o, DbUtil.getField(o, "id")));
+                vb.append("?").append(",");
+            } catch (NoSuchFieldException e) {
+                logger.error("SQL2Helper ERR# insert with primary key,but without a value!!");
+                throw new IllegalStateException("no primary key value exist.");
+            }
+        }
+        exclude.add("id");
+        Set<Field> fields = DbUtil.getEntityFields(o.getClass());
+    /*    List<Field> fields1 = new ArrayList<>();
+        Entity declaredAnnotation = o.getClass().getDeclaredAnnotation(Entity.class);
+        if (null != declaredAnnotation) {
+            fields = o.getClass().getFields();
+            fields1.addAll(Arrays.asList( o.getClass().getFields()));
+        } else {
+            declaredAnnotation = o.getClass().getSuperclass().getDeclaredAnnotation(Entity.class);
+            if(null==declaredAnnotation){
+                throw new IllegalArgumentException("no @Entity define in class");
+            }
+            fields = o.getClass().getSuperclass().getDeclaredFields();
+            fields1.addAll(Arrays.asList( o.getClass().getFields()));
+            Field[] declaredFields = o.getClass().getSuperclass().getDeclaredFields();
+            fields1.addAll(Arrays.asList( o.getClass().getSuperclass().getDeclaredFields()));
+        }*/
+        for (Field field : fields) {
+            if (DbUtil.isUsageField(field)) {
+                String fieldName = field.getName();
+                if (!DbUtil.isEmptyOrNull(exclude)) {
+                    //忽略属性
+                    if (exclude.contains(fieldName)) {
+                        continue;
+                    }
+                }
+                Type type = field.getGenericType();
+                DBF DBF = field.getAnnotation(DBF.class);
+                if (null != DBF) {
+                    Object value = DbUtil.getFieldValue(o, field);
+                    if (ignoreNull) {
+                        if (null != value) {
+                            //TODO 校验值
+                            checkFieldValue(field, value, true);
+                            sbr.append(this.comma).append(DbUtil.getColumnName(fieldName)).append(this.comma).append(",");
+                            appendValue(vb, type, value, parameters);
+                        }
+                    } else {
+                        if (type == Date.class && null == value) {
+                            continue;
+                        }
+                        sbr.append(this.comma).append(DbUtil.getColumnName(fieldName)).append(this.comma).append(",");
+                        appendValue(vb, type, value, parameters);
+                    }
+                }
+            }
+        }
+        sbr.deleteCharAt(sbr.length() - 1);
+        vb.deleteCharAt(vb.length() - 1);
+        sbr.append(")").append(" VALUES ");
+        sbr.append(vb.toString()).append(")");
+        sbr.append(";");
+        sqlEntity.sql = sbr.toString();
+        sqlEntity.parameters = parameters.toArray();
+        return sqlEntity;
+    }
+
+    //endregion insert
+
+    //region update
+    private SQLEntity updateNative(Object o, OBuilder OBuilder, boolean ignoreNull, List<String> includes, List<String> excludes) {
+        SQLEntity sqlEntity = new SQLEntity();
+        List<Object> parameters = new ArrayList<>();
+
+        StringBuilder sbr = new StringBuilder("UPDATE ");
+        sbr.append(getTableName(o.getClass(), o.getClass().getSimpleName())).append(" SET ");
+        Set<Field> fields = DbUtil.getEntityFields(o.getClass());
+        boolean hasUpdateField = false;
+        for (Field field : fields) {
+            if (DbUtil.isUsageField(field)) {
+                DBF dbf = field.getAnnotation(DBF.class);
+                if (null == dbf) {
+                    continue;
+                }
+                String fieldName = field.getName();
+                if (!DbUtil.isEmptyOrNull(excludes)) {
+                    if (excludes.contains(DbUtil.getColumnName(fieldName)) || excludes.contains(fieldName)) {
+                        continue;
+                    }
+                }
+                if (!DbUtil.isEmptyOrNull(includes)) {
+                    if (!includes.contains(fieldName) && !includes.contains(DbUtil.getColumnName(fieldName))) {
+                        continue;
+                    }
+                }
+                if (dbf.canUpdate() && !isImmutableField(fieldName)) {
+                    Type type = field.getGenericType();
+                    Object value = DbUtil.getFieldValue(o, field);
+                    if (ignoreNull) {
+                        if (!DbUtil.isEmptyOrNull(value) && !"0".equals(value.toString())) {
+                            checkFieldValue(field, value, false);
+                            sbr.append(this.comma).append(DbUtil.getColumnName(fieldName)).append(this.comma).append("=");
+                            appendValue(sbr, type, value, parameters);
+                            hasUpdateField = true;
+                        }
+                    } else {
+                        if (null == value && type == Date.class) {
+                            continue;
+                        }
+                        sbr.append(this.comma).append(DbUtil.getColumnName(fieldName)).append(this.comma).append("=");
+                        appendValue(sbr, type, value, parameters);
+                        hasUpdateField = true;
+                    }
+
+                }
+            }
+        }
+        if (!hasUpdateField) {
+            throw new RuntimeException("no field to UPDATE ,#SQL:{}" + sbr.toString());
+        }
+        if (sbr.toString().endsWith(",")) {
+            sbr.deleteCharAt(sbr.length() - 1);
+        }
+        //where
+        if (null != OBuilder && !DbUtil.isEmptyOrNull(OBuilder.getWheres())) {
+            List<OBuilder.Where> wheres = OBuilder.getWheres();
+            if (!DbUtil.isEmptyOrNull(wheres)) {
+                sbr.append(" WHERE ");
+                wheres.forEach(wh -> {
+                    if (ignoreNull) {
+                        if (null != wh.value) {
+                            sbr.append(this.comma).append(DbUtil.getColumnName(wh.key)).append(this.comma).append(" ").append(wh.op).append(" ");
+                            appendValue(sbr, wh.value, parameters);
+                            sbr.append(" AND ");
+                        }
+                    } else {
+                        sbr.append(this.comma).append(DbUtil.getColumnName(wh.key)).append(this.comma).append(" ").append(wh.op).append(" ");
+                        appendValue(sbr, wh.value, parameters);
+                        sbr.append(" AND ");
+                    }
+                });
+                sbr.delete(sbr.length() - 5, sbr.length());
+            }
+            if (DIALECT_MYSQL == dialect) {
+                //mysql innodb 锁
+                if (OBuilder.forUpdate) {
+                    sbr.append(" FOR UPDATE ");
+                }
+                if (OBuilder.forShare) {
+                    sbr.append(" LOCK IN SHARE MODE");
+                }
+            }
+        } else {
+            try {
+                Field f = DbUtil.getField(o, "id");
+                sbr.append(" WHERE id=?");
+                parameters.add(DbUtil.getFieldValue(o, f));
+            } catch (Exception e) {
+                throw new IllegalArgumentException("tkf id is not exist!!!");
+            }
+        }
+        sbr.append(";");
+        sqlEntity.sql = sbr.toString();
+        sqlEntity.parameters = parameters.toArray();
+        return sqlEntity;
+    }
+
+    public SQLEntity updateByWhere(Object o, OBuilder builder) {
+        return updateNative(o, builder, false, null, null);
+    }
+
+
+    public SQLEntity updateByWhere(Object o, OBuilder builder, List<String> includes) {
+        return updateNative(o, builder, false, includes, null);
+    }
+
+    public SQLEntity updateByWhere(Object o, OBuilder builder, List<String> includes, List<String> excludes) {
+        return updateNative(o, builder, false, includes, excludes);
+    }
+
+    public SQLEntity update(String sql, Object... params) {
+        SQLEntity sqlEntity = new SQLEntity();
+        sqlEntity.sql = sql;
+        sqlEntity.parameters = params;
+        return sqlEntity;
+    }
+
+    public SQLEntity updateSelective(Object o, List<String> includes, List<String> excludes) {
+        return updateNative(o, null, true, includes, excludes);
+    }
+
+    public SQLEntity updateSelectiveByWhere(Object o, OBuilder OBuilder) {
+        return updateNative(o, OBuilder, true, null, null);
+    }
+//endregion update
+
+
+    //region delete
+    public SQLEntity deleteById(Class<?> clz, Object id) {
+        SQLEntity sqlEntity = new SQLEntity();
+        List<Object> parameters = new ArrayList<>();
+
+        if (null != id) {
+            parameters.add(id);
+        } else {
+            throw new IllegalArgumentException("null value with id!!!");
+        }
+        sqlEntity.sql = "DELETE FROM " + getTableName(clz, clz.getSimpleName()) + " WHERE id= ?";
+        sqlEntity.parameters = parameters.toArray();
+        return sqlEntity;
+    }
+
+
+    public SQLEntity delete(Class<?> clz, OBuilder OBuilder) {
+        SQLEntity sqlEntity = new SQLEntity();
+        List<Object> parameters = new ArrayList<>();
+        StringBuilder sbr = new StringBuilder("DELETE FROM ");
+        sbr.append(getTableName(clz, clz.getSimpleName()));
+        if (null != OBuilder) {
+            List<OBuilder.Where> wheres = OBuilder.getWheres();
+            if (!DbUtil.isEmptyOrNull(wheres)) {
+                sbr.append(" WHERE 1=1");
+                wheres.forEach(wh -> {
+                    sbr.append(" AND ");
+                    appendBuilderWhere(sbr, wh, "", parameters);
+                });
+            }
+        } else {
+            throw new IllegalArgumentException(" delete with no where condition exist!!!");
+        }
+        sqlEntity.sql = sbr.toString();
+        sqlEntity.parameters = parameters.toArray();
+        return sqlEntity;
+    }
+
+    //endregion delete
+
+    private String appendWhereLimit(OBuilder OBuilder, StringBuilder sbr) {
+        if (null != OBuilder) {
+            if (OBuilder.limitDelta() > 0) {
+                if (DIALECT_MYSQL == dialect) {
+                    sbr.append(" LIMIT ");
+                    if (OBuilder.limitStart() > 0) {
+                        sbr.append(OBuilder.limitStart()).append(",");
+                    }
+                    if (OBuilder.limitDelta() > 0) {
+                        sbr.append(OBuilder.limitDelta());
+                    }
+                    return null;
+                } else if (DIALECT_MSSQL == dialect) {
+                    sbr.append(" OFFSET ");
+                    if (OBuilder.limitStart() >= 0) {
+                        sbr.append(OBuilder.limitStart()).append(" ROWS ");
+                    }
+                    if (OBuilder.limitDelta() > 0) {
+                        sbr.append(" FETCH NEXT  ").append(OBuilder.limitDelta()).append(" ROWS ONLY  ");
+                    }
+                    return null;
+                } else if (DIALECT_PGSQL == dialect) {
+                    sbr.append(" LIMIT ");
+
+                    if (OBuilder.limitDelta() > 0) {
+                        sbr.append(OBuilder.limitDelta());
+                    }
+                    if (OBuilder.limitStart() >= 0) {
+                        sbr.append(" OFFSET ").append(OBuilder.limitStart());
+                    }
+                    return null;
+                } else if (DIALECT_ORACLE == dialect) {
+                    StringBuilder sr = new StringBuilder();
+                    sr.append("SELECT * FROM (SELECT o.*,ROWNUM as rowno FROM (").append(sbr.toString()).append(") o ");
+                    if (OBuilder.limitDelta() > 0) {
+                        sr.append(" WHERE ROWNUM<=").append(OBuilder.limitDelta()).append(")  o1");
+                    }
+                    if (OBuilder.limitStart() >= 0) {
+                        sr.append(" WHERE o1.rowno>").append(OBuilder.limitStart());
+                    }
+                    return sr.toString();
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 拼接Builder 条件
+     */
+    private void appendBuilderWhere(StringBuilder sbr, OBuilder.Where wh, String tbAlias, List<Object> parameters) {
+        if (DbUtil.isEmptyOrNull(wh.sql)) {
+            if (!DbUtil.isEmptyOrNull(tbAlias)) {
+                sbr.append(tbAlias).append(".");
+            }
+            sbr.append(this.comma).append(DbUtil.getColumnName(wh.key)).append(this.comma).append(" ").append(wh.op);
+            String op = wh.op;
+            Object value = wh.value;
+            if ("in".equals(op.trim()) || "not in".equals(op.trim())) {
+                sbr.append("(");
+                for (int i = 0; i < Array.getLength(value); i++) {
+                    Object v = Array.get(value, i);
+                    sbr.append("?");
+                    parameters.add(v);
+                   /* if (v.getClass() == String.class) {
+                        sbr.append("'").append(DbUtil.injectDefend(v.toString())).append("'");
+                    } else {
+                        sbr.append(v);
+                    }*/
+                    if (i != Array.getLength(value) - 1) {
+                        sbr.append(",");
+                    }
+                }
+                sbr.append(")");
+            } else {
+                //转换取值方式
+                sbr.append("?");
+                parameters.add(value);
+           /*     if (value.getClass() == String.class) {
+                    sbr.append("'").append(value).append("'");
+                } else if (wh.value.getClass() == Date.class) {
+                    sbr.append("'").append(new Timestamp(((Date) value).getTime())).append("'");
+                } else {
+
+//                    sbr.append(value);
+                }*/
+            }
+        } else {
+            //别名判断
+            sbr.append(" (");
+            sbr.append(appendWhereSql(wh.sql, wh.sqlValues, tbAlias, parameters));
+            sbr.append(") ");
+        }
+    }
+
+    /**
+     * whereSql拼接,需要注意json类型处理
+     *
+     * @param whereSql
+     * @param val
+     * @param tbAlias
+     * @param parameters
+     * @return
+     */
+    private String appendWhereSql(String whereSql, List<Object> val, String tbAlias, List<Object> parameters) {
+//        parameters.addAll(val);
+        if (!DbUtil.isEmptyOrNull(whereSql)) {
+            if (!DbUtil.isEmptyOrNull(val)) {
+                if (whereSql.contains("?")) {
+                    String regex = "\\?";
+                    for (Object o : val) {
+                        if (o.getClass() == String.class) {
+                            whereSql = whereSql.replaceFirst(regex, "'" + DbUtil.injectDefend(o.toString()) + "'");
+                        } else if (o.getClass() == Date.class) {
+                            whereSql = whereSql.replaceFirst(regex, "'" + (new Timestamp(((Date) o).getTime())) + "'");
+                        } else {
+                            whereSql = whereSql.replaceFirst(regex, DbUtil.injectDefend(o.toString()));
+                        }
+                    }
+                    while (whereSql.contains("?")) {
+                        for (Object o : val) {
+                            if (o.getClass() == String.class) {
+                                whereSql = whereSql.replaceFirst(regex, "'" + DbUtil.injectDefend(o.toString()) + "'");
+                            } else if (o.getClass() == Date.class) {
+                                whereSql = whereSql.replaceFirst(regex, "'" + (new Timestamp(((Date) o).getTime())) + "'");
+                            } else {
+                                whereSql = whereSql.replaceFirst(regex, DbUtil.injectDefend(o.toString()));
+                            }
+                        }
+                    }
+                } else {
+                    if (val.size() == 1 && val.get(0) instanceof Boolean) {
+                        if (!(Boolean) val.get(0)) {
+                            return null;
+                        }
+                    }
+                }
+            }
+            String[] splits = whereSql.split(" AND ");
+            boolean containsComma = false;
+            if (splits.length > 1) {
+                StringBuilder sbd = new StringBuilder();
+                for (int i = 0; i < splits.length; i++) {
+                    if (splits[i].startsWith("(")) {
+                        containsComma = true;
+                        sbd.append("(");
+                        if (!DbUtil.isEmptyOrNull(tbAlias)) {
+                            sbd.append(tbAlias).append(".");
+                        }
+                        sbd.append(splits[i].trim().substring(1));
+                    } else {
+                        if (!DbUtil.isEmptyOrNull(tbAlias)) {
+                            sbd.append(tbAlias).append(".");
+                        }
+                        sbd.append(splits[i].trim());
+                    }
+                    if (i != splits.length - 1) {
+                        sbd.append(" AND ");
+                    }
+                }
+                whereSql = sbd.toString();
+            }
+            String[] splits1 = whereSql.split(" OR ");
+            if (splits1.length > 1) {
+                StringBuilder sbd = new StringBuilder();
+                for (int i = 0; i < splits1.length; i++) {
+                    if (splits1[i].startsWith("(")) {
+                        if (!containsComma) {
+                            sbd.append("(");
+                            if (!DbUtil.isEmptyOrNull(tbAlias)) {
+                                sbd.append(tbAlias).append(".");
+                            }
+                            sbd.append(splits1[i].trim().substring(1));
+                        }
+                    } else {
+                        if (!DbUtil.isEmptyOrNull(tbAlias)) {
+                            sbd.append(tbAlias).append(".");
+                        }
+                        sbd.append(splits1[i].trim());
+                    }
+                    if (i != splits1.length - 1) {
+                        sbd.append(" OR ");
+                    }
+                }
+                whereSql = sbd.toString();
+            }
+        }
+        return whereSql;
+    }
+
+
+    private void appendValue(StringBuilder sbr, Type type, Object value, List<Object> parameters) {
+        sbr.append("?").append(",");
+        parameters.add(value);
+//        if (DbUtil.isGenericType(type)) {
+//            parameters.add(value);
+//        } else {
+//            parameters.add(JacksonUtil.toJSONString(value));
+//        }
+     /*   if (type == String.class) {
+            if (null == value) {
+                sbr.append("?").append(", ");
+                parameters.add("");
+            } else {
+                sbr.append("?").append(", ");
+                parameters.add(value);
+//                sbr.append("'");
+             *//*   String v = (String) value;
+                if (v.contains("'")) {
+                    sbr.append(DbUtil.injectDefend(v.replaceAll("'", "\\\\'")));
+                } else {
+                    sbr.append(DbUtil.injectDefend(v));
+                }
+                sbr.append("'").append(",");*//*
+            }
+        } else if (DbUtil.isGenericType(type)) {
+            sbr.append("?").append(", ");
+            parameters.add(value);
+         *//*   if (null == value) {
+                sbr.append(0).append(",");
+            } else {
+                sbr.append(value).append(",");
+            }*//*
+        } else if (type == Date.class) {
+            sbr.append("?").append(", ");
+            parameters.add(value);
+        } else {
+
+        }*/
+    }
+
+    private void appendValue(StringBuilder sbr, Object value, List<Object> parameters) {
+        sbr.append(" ? ");
+        parameters.add(value);
+      /*  if (null != value) {
+            if (value instanceof String) {
+                sbr.append("'");
+                String v = (String) value;
+                if (v.contains("'")) {
+                    sbr.append(DbUtil.injectDefend(v.replaceAll("'", "\\\\'")));
+                } else {
+                    sbr.append(DbUtil.injectDefend(v));
+                }
+                sbr.append("'").append(",");
+            } else if (DbUtil.isGenericType(value)) {
+                sbr.append(value).append(",");
+            } else if (value instanceof Date) {
+                sbr.append("'").append(new Timestamp(((Date) value).getTime())).append("'").append(",");
+            } else {
+                sbr.append("'").append(JacksonUtil.toJSONString(value)).append("'").append(",");
+            }
+        } else {
+            //类型默认值
+            sbr.append(" ").append(",");
+        }*/
+    }
+
+
+    private void getValue(StringBuilder sbr, Object value, List<Object> parameters) {
+        if (null != value) {
+//            if (value instanceof String) {
+//                sbr.append("'").append(value).append("'");
+//            } else if (DbUtil.isGenericType(value)) {
+//                sbr.append(value);
+//            } else if (value instanceof Date) {
+//                sbr.append("'").append(new Timestamp(((Date) value).getTime())).append("'");
+//            } else {
+//                sbr.append("'").append(JacksonUtil.toJSONString(value)).append("'");
+//            }
+            sbr.append("?");
+            parameters.add(value);
+        } else {
+            throw new IllegalArgumentException("append value with NULL");
+        }
+    }
+
+
+    private String getTableName(Entity anno) {
+        String tbName = anno.tbName();
+        if (!DbUtil.isEmptyOrNull(tbName)) {
+            return DbUtil.getColumnName(tbName);
+        }
+        Class<?> clz = anno.clz();
+       /* if (tbName.endsWith("Info")) {
+            tbName = tbName.substring(0, tbName.length() - 4);
+        }*/
+        return JdbcHelper.tbPrefix + DbUtil.getColumnName(clz.getSimpleName());
+    }
+
+    public static String getTableName(Class<?> clz, String tbName) {
+        if (clz.isAnnotationPresent(Entity.class)) {
+            Entity entity = clz.getAnnotation(Entity.class);
+            if (!DbUtil.isEmptyOrNull(entity.tbName())) {
+                return DbUtil.getColumnName(entity.tbName());
+            }
+        } else if (clz.getSuperclass().isAnnotationPresent(Entity.class)) {
+            Class<?> superClz = clz.getSuperclass();
+            Entity entity = superClz.getAnnotation(Entity.class);
+            if (!DbUtil.isEmptyOrNull(entity.tbName())) {
+                return DbUtil.getColumnName(entity.tbName());
+            }
+        }
+        if (tbName.endsWith("Info")) {
+            tbName = tbName.substring(0, tbName.length() - 4);
+        }
+        return JdbcHelper.tbPrefix + DbUtil.getColumnName(tbName);
+    }
+
+
+    /**
+     * 获取外键关联实体类
+     */
+    private Class<?> getFkDomainClass(Field f, Class<?> domainClass) {
+        try {
+            if (f.isAnnotationPresent(FK.class)) {
+                return f.getAnnotation(FK.class).clz();
+            }
+            if (f.isAnnotationPresent(One.class)) {
+                One oneAnnotation = f.getAnnotation(One.class);
+                if (null != oneAnnotation) {
+                    if (oneAnnotation.te() != void.class) {
+                        return oneAnnotation.te();
+                    }
+                    if (!DbUtil.isEmptyOrNull(oneAnnotation.mkf())) {
+                        if (DbUtil.isGenericType(f.getType())) {
+                            Field field = domainClass.getDeclaringClass().getDeclaredField(oneAnnotation.mkf());
+                            if (field.isAnnotationPresent(FK.class)) {
+                                return field.getAnnotation(FK.class).clz();
+                            }
+                        } else {
+                            return f.getDeclaringClass();
+                        }
+                    }
+                }
+            }
+            if (f.isAnnotationPresent(QF.class)) {
+                QF qf = f.getAnnotation(QF.class);
+                if (!DbUtil.isEmptyOrNull(qf.pkf())) {
+                    Field field = domainClass.getDeclaringClass().getDeclaredField(qf.pkf());
+                    return field.getAnnotation(FK.class).clz();
+                }
+            }
+        } catch (NoSuchFieldException e) {
+            logger.error("SQL2Helper ERR# getFkDomainClass ,fileName:{},class:{}", f.getName(), domainClass.getSimpleName());
+        }
+        return Void.class;
+    }
+
+    private boolean isImmutableField(String fieldName) {
+        List<String> immutableFields = new ArrayList<>();
+        immutableFields.add("id");
+        immutableFields.add("createat");
+        immutableFields.add("updateat");
+        return immutableFields.contains(fieldName.toLowerCase());
+    }
+
+    /**
+     * 校验字段的值
+     */
+    private void checkFieldValue(Field field, Object value, boolean insertCheck) {
+        if (!field.isAnnotationPresent(DBF.class)) {
+            return;
+        }
+        DBF dbf = field.getAnnotation(DBF.class);
+
+        //插入SQL校验必填
+        if (insertCheck) {
+            if (dbf.required() && DbUtil.isEmptyOrNull(value)) {
+                throw new IllegalArgumentException("【" + dbf.comment() + "】取值不能为空");
+            }
+        }
+        int min = dbf.min();
+        int max = dbf.max();
+        if (dbf.min() > 0) {
+            if (value instanceof Integer || value instanceof Short || value instanceof Double || value instanceof Long) {
+                if (BigDecimal.valueOf(min).compareTo(new BigDecimal(String.valueOf(value))) > 0) {
+                    throw new IllegalArgumentException("【" + dbf.comment() + "】最小值为" + min);
+                }
+                if (max > 0 && BigDecimal.valueOf(max).compareTo(new BigDecimal(value.toString())) < 0) {
+                    throw new IllegalArgumentException("【" + dbf.comment() + "】最大值为" + max);
+                }
+            } else if (value instanceof String || value instanceof Character) {
+                if (!DbUtil.isEmptyOrNull(value)) {
+                    int length = value.toString().length();
+                    if (min > length) {
+                        throw new IllegalArgumentException("【" + dbf.comment() + "】最小长度为" + min);
+                    }
+                    if (max > 0 && max < length) {
+                        throw new IllegalArgumentException("【" + dbf.comment() + "】最大长度为" + max);
+                    }
+                }
+            }
+        }
+
+
+    }
+}

+ 7 - 0
car-wash-service/pom.xml

@@ -31,6 +31,13 @@
             <version>0.0.1-SNAPSHOT</version>
         </dependency>
 
+        <dependency>
+            <groupId>com.kym</groupId>
+            <artifactId>car-wash-jdbc</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+
+
         <dependency>
             <groupId>com.github.wechatpay-apiv3</groupId>
             <artifactId>wechatpay-java</artifactId>

+ 307 - 0
car-wash-service/src/main/java/com/kym/dao/DbHandler.java

@@ -0,0 +1,307 @@
+package com.kym.dao;
+
+import com.kym.common.ContextHelper;
+import com.kym.common.IUser;
+import com.kym.common.utils.CommUtil;
+import com.kym.DbUtil;
+import com.kym.JacksonUtil;
+import com.kym.jdbc.BasicQuery;
+import com.kym.jdbc.IHandler;
+import com.kym.jdbc.OBuilder;
+import com.kym.jdbc.annotations.DBF;
+import com.kym.jdbc.annotations.Entity;
+import com.kym.jdbc.lambda.ColumnFunc;
+import com.kym.jdbc.lambda.DefaultLambdaParser;
+import com.kym.jdbc.template.JdbcExecute;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+
+/**
+ * jdbc工具类
+ *
+ * @author yaop at 2019/4/27 17:35
+ */
+@Slf4j
+@Component("dbHandler")
+public class DbHandler extends JdbcExecute {
+
+
+  /*  @Autowired
+    public void setExtMapper(ExtMapper mapper) {
+
+        Log.TOTAL.i("------->>> mybatis autowired success <<<-------");
+        super.setMapper(mapper);
+    }*/
+
+    @Autowired
+    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
+        super.setJdbcTemplate(jdbcTemplate, IHandler.DIALECT_MYSQL);
+    }
+
+    //TODO JSON数据的处理(mysql、postgresql)
+
+    @Override
+    protected void beforeInsert(Object bean) {
+        super.beforeInsert(bean);
+        IUser user = ContextHelper.getUser();
+        if (null != user) {
+            setFieldValue(bean, "createBy", user.id);
+            setFieldValue(bean, "updateBy", user.id);
+            setFieldValue(bean, "companyId", user.companyId);
+        }
+    }
+
+
+    @Override
+    protected void beforeDelete(Class<?> clz, Object id) {
+        super.beforeDelete(clz, id);
+    }
+
+    @Override
+    protected void beforeDelete(Class<?> clz, OBuilder OBuilder) {
+        super.beforeDelete(clz, OBuilder);
+    }
+
+    @Override
+    protected void beforeUpdate(Object bean, OBuilder OBuilder) {
+        super.beforeUpdate(bean, OBuilder);
+    }
+
+    @Override
+    protected void beforeUpdate(Object bean) {
+        IUser user = ContextHelper.getUser();
+        if (null != user) {
+            setFieldValue(bean, "updateBy", user.id);
+        }
+
+     /*   else {
+            IAccount account = ContextHelper.getAccount();
+            if (null != account) {
+                setFieldValue(bean, "updateBy", account.id);
+            }
+        }*/
+    }
+
+    @Override
+    protected void beforeSelect(BasicQuery query) {
+        super.beforeSelect(query);
+        IUser user = ContextHelper.getUser();
+        if (null != user) {
+            setFieldValue(query, "companyId", user.companyId);
+        }
+
+        setFieldValue(query, "deleted", false);
+    }
+
+
+    @Override
+    protected void beforeSelect(Class<?> clz, OBuilder OBuilder) {
+        super.beforeSelect(clz, OBuilder);
+    }
+
+
+    /**
+     * 查询是否存在
+     */
+    public final boolean selectExist(Class<?> clz, int id) {
+        Object i = selectObject("select id from " + getTableName(clz.getSimpleName()) + " where id =" + id);
+        return !CommUtil.isEmptyOrNull(i);
+    }
+
+    /**
+     * in 子句
+     */
+    public final <T> List<T> selectListInExclude(Class<T> clz, String field, Object[] values, String... excludes) {
+        if (CommUtil.isEmptyOrNull(values)) {
+            log.warn("query list with no parameters!");
+            return Collections.emptyList();
+        }
+        return selectListExclude(clz, OBuilder.build().in(field, values), excludes);
+    }
+
+
+    public final <T> List<T> selectListIn(Class<T> clz, String field, Object[] values, String... includes) {
+        if (CommUtil.isEmptyOrNull(values)) {
+            log.warn("query list with no parameters!");
+            return Collections.emptyList();
+        }
+        return selectList(clz, OBuilder.build().in(field, values), includes);
+    }
+
+
+    /**
+     * map传参查询集合
+     */
+    public final <T> List<T> selectListExclude(Class<T> clz, Map<String, Object> params, String... ignores) {
+        if (null != params && !params.isEmpty()) {
+            log.warn("query list with no parameters!");
+            return Collections.emptyList();
+        }
+        OBuilder builder = OBuilder.build();
+        params.forEach(builder::eq);
+        return selectList(clz, builder, ignores);
+    }
+
+
+    /**
+     * 校验字段值的唯一性
+     */
+    public <T> T checkUniqueValue(Class<T> clz, OBuilder builder, ColumnFunc<T, ?> columnFunc, long oldPojoId) {
+        T o = selectOne(clz, builder);
+        if (null == o) {
+            return null;
+        }
+        long queryId = CommUtil.null2Long(getFieldValue(o, "id"));
+        String comment = clz.getAnnotation(Entity.class).comment();
+        String fieldComment = null;
+        String fieldName = DefaultLambdaParser.getPropertyName(columnFunc);
+        try {
+            fieldComment = clz.getDeclaredField(fieldName).getAnnotation(DBF.class).comment();
+        } catch (NoSuchFieldException e) {
+            log.error("checkUniqueValue error:{}", e.getMessage());
+        }
+        if (0 == oldPojoId) {
+            CommUtil.asserts(queryId <= 0, comment + fieldComment + "已存在");
+        } else {
+            CommUtil.asserts(queryId == oldPojoId, comment + fieldComment + "已存在");
+        }
+
+        return o;
+    }
+
+    /**
+     * 校验字段值的唯一性
+     */
+    public <T> void checkUniqueValue(Class<T> clz, ColumnFunc<T, ?> columnFunc, Object fieldValue) {
+        String fieldName = DefaultLambdaParser.getPropertyName(columnFunc);
+
+        OBuilder builder = OBuilder.build().eq(fieldName, fieldValue);
+        IUser user = ContextHelper.getUser();
+        if (null != user) {
+            builder.eq("companyId", user.companyId);
+        }
+
+        checkUniqueValue(clz, builder, columnFunc, 0);
+    }
+
+    /**
+     * 校验字段值的唯一性
+     */
+    public <T> void checkUniqueValue(Class<T> clz, ColumnFunc<T, ?> columnFunc, Object fieldValue, long oldPojoId) {
+        String fieldName = DefaultLambdaParser.getPropertyName(columnFunc);
+
+        OBuilder builder = OBuilder.build().eq(fieldName, fieldValue);
+        IUser user = ContextHelper.getUser();
+        if (null != user) {
+            builder.eq("companyId", user.companyId);
+        }
+
+        checkUniqueValue(clz, builder, columnFunc, oldPojoId);
+    }
+
+
+    /**
+     * 可选地更新记录
+     */
+    public void updateSelective(Class<?> clz, Map<String, Object> params, long id) {
+        StringBuilder sbr = new StringBuilder("UPDATE ");
+        sbr.append(getTableName(clz.getSimpleName())).append(" SET ");
+        if (DbUtil.isEmptyOrNull(params)) {
+            throw new IllegalArgumentException("no data to update");
+        }
+
+        sbr.append(params.keySet().stream().map(k -> String.format(" %s=? ", getColumnName(k))).collect(Collectors.joining(",")));
+        List<Object> ll = new ArrayList<>(params.values());
+
+        sbr.append(" WHERE id=").append(id);
+
+        update(sbr.toString(), ll.toArray());
+    }
+
+    public void updateSelective(Class<?> clz, Map<String, Object> params, Map<String, Object> wheres) {
+        StringBuilder sbr = new StringBuilder("UPDATE ");
+        sbr.append(getTableName(clz.getSimpleName())).append(" SET ");
+        if (DbUtil.isEmptyOrNull(params)) {
+            throw new IllegalArgumentException("no data to update");
+        }
+        if (DbUtil.isEmptyOrNull(wheres)) {
+            throw new IllegalArgumentException("no condition exists  for  update operation");
+        }
+        sbr.append(params.keySet().stream().map(k -> String.format(" %s=? ", getColumnName(k))).collect(Collectors.joining(",")));
+        List<Object> ll = new ArrayList<>(params.values());
+
+        sbr.append(" WHERE ");
+        sbr.append(wheres.keySet().stream().map(k -> String.format(" %s=? ", getColumnName(k))).collect(Collectors.joining(" AND ")));
+        ll.addAll(wheres.values());
+
+        update(sbr.toString(), ll.toArray());
+    }
+
+    /**
+     * 无条件查询集合
+     */
+    public <T> List<T> selectAll(Class<T> clz, String... excludes) {
+        List<T> list = selectListExclude(clz, OBuilder.build().limit(-1), excludes);
+        if (CommUtil.isEmptyOrNull(list)) {
+            return Collections.emptyList();
+        } else {
+            return list;
+        }
+    }
+
+    /**
+     * 有条件查询集合
+     */
+    public List<?> selectAll(Class<? extends BasicQuery> queryClz, Object query, String... excludes) {
+        BasicQuery q = null;
+        if (null != query) {
+            q = JacksonUtil.toJavaObject(JacksonUtil.toJSONString(query), queryClz);
+        }
+        if (null == q) {
+            return Collections.emptyList();
+        }
+        q.pageSize = -1;
+        List<?> list = selectListExclude(q, excludes);
+        if (CommUtil.isEmptyOrNull(list)) {
+            return Collections.emptyList();
+        } else {
+            return list;
+        }
+    }
+
+
+    public <T> T selectOneByUuid(Class<T> entityClass, String uid) {
+        return selectOneExist(entityClass, OBuilder.build().eq("uuid", uid));
+    }
+
+
+    /**
+     * 批量插入
+     */
+    private void batchInsertExecute(String sql, List<Object[]> ll) {
+        int batch = 1;
+        int batchSize = 256;
+        int totalSize = ll.size();
+        if (totalSize > batchSize) {
+            batch = (int) Math.ceil((double) totalSize / batchSize);
+        }
+        for (int i = 0; i < batch; i++) {
+            List<Object[]> subList = ll.subList(i * batchSize, Math.min(totalSize, ((i + 1) * batchSize)));
+            insertBatch(sql, subList);
+        }
+    }
+
+    public void replaceInsert(Object bean) {
+        String sql = "replace into t_seed ()";
+    }
+
+}

+ 19 - 1
pom.xml

@@ -21,6 +21,7 @@
         <module>car-wash-miniapp</module>
         <module>car-wash-service</module>
         <module>car-wash-mapper</module>
+        <module>car-wash-jdbc</module>
     </modules>
     <properties>
         <java.version>17</java.version>
@@ -32,8 +33,15 @@
             <artifactId>lombok</artifactId>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-jdbc</artifactId>
+        </dependency>
 
-
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
     </dependencies>
 
     <repositories>
@@ -58,6 +66,16 @@
                 </plugin>
             </plugins>
         </pluginManagement>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>8</source>
+                    <target>8</target>
+                </configuration>
+            </plugin>
+        </plugins>
     </build>