diff --git a/README.md b/README.md index 4bc31f3..e3ce5bf 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ # veasion-db -veasion-db 是一个轻量级持久层db框架,除slf4j-api外不依赖任何第三方jar,该框架提供丰富灵活的数据库操作, +veasion-db 是一个轻量级持久层ORM框架,除slf4j-api外不依赖任何第三方jar,该框架提供丰富灵活的数据库操作, 单元测试 query/update 目录下有大量示例及demo。 -框架基本支持sql能实现的任意查询或更新,如关联查询、子查询、关联更新、insert select、不同数据库分页扩展等。 +框架无需写任何SQL基本支持sql能实现的任意查询或更新,如关联查询、子查询、关联更新、insert select、不同数据库分页扩展等。 -框架支持自定义拦截器,内置逻辑删除拦截器,可通过SPI或调用InterceptorUtils.addInterceptor方法加入扩展。 +框架支持自定义拦截器,内置逻辑删除、数据隔离拦截器,可通过SPI或调用InterceptorUtils.addInterceptor方法加入扩展。 ## maven 依赖 添加 veasion-db 依赖 ```xml cn.veasion veasion-db - 1.1.8 + 1.1.9 ``` 支持sql解析生成veasion-db代码 @@ -21,7 +21,7 @@ String sql = "select * from t_student where id = 1"; String code = SQLParseUtils.parseSQLConvert(sql); // 直接把SQL转换成对应的代码,示例参考单元测试 SqlDbConvertTest -// 该功能为扩展功能需要加入第三方依赖 +// 该功能为扩展功能需要加入第三方依赖,示例见单元测试 SqlDbConvertTest com.github.jsqlparser jsqlparser @@ -325,7 +325,24 @@ public class InsertTest extends BaseTest { ### 动态查询机制 支持动态查询机制,可通过配置字段注解提前定义查询方式和动态关联、静态关联表。 -非常灵活的实现前端传参后端动态查询,具体参考单元测试 QueryCriteriaTest +非常灵活的实现前端传参后端动态查询,支持后端不需要写任何代码,根据前端传参自动关联表进行各种条件的动态查询 +

+动态查询说明:
+前端传 { id: 1 } 自动映射成 id = 1
+前端传 { id: [1, 2, 3] } 自动映射成 id in (1,2,3)
+前端传 { start_age: 18 } 自动映射成 age >= 18
+前端传 { end_age: 30 } 自动映射成 age <= 30
+前端传 { name: '罗' } 自动映射成 name = '罗'
+前端传 { name: '罗%' } 自动映射成 name like '罗%'
+前端传 { name: '%罗%' } 自动映射成 name like '%罗%'
+ +动态关联说明:
+有一张学生表 t_student 和一张班级表 t_classes +如果前端传了 className 字段(学生表只有 class_id 关联班级表)就会进行自动关联 t_classes 表去查询,不用写任何代码自动根据参数去动态关联表查询。
+前端传 { id: 1 } 自动映射成 select * from t_student where id = 1
+前端传 { id: 1, className: '三年二班' } 自动映射成 select s.* from t_student s join t_classes c on s.class_id = c.id where s.id = 1 and c.name = '三年二班'
+ +具体参考单元测试 QueryCriteriaTest ### spring 项目接入 veasion-db SPI 实现 cn.veasion.db.jdbc.DataSourceProvider 接口 diff --git a/pom.xml b/pom.xml index bafdf64..4a4df02 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ cn.veasion veasion-db - 1.1.8 + 1.1.9 veasion-db https://github.com/veasion/veasion-db diff --git a/src/main/java/cn/veasion/db/AbstractFilter.java b/src/main/java/cn/veasion/db/AbstractFilter.java index 357fe29..62d827c 100644 --- a/src/main/java/cn/veasion/db/AbstractFilter.java +++ b/src/main/java/cn/veasion/db/AbstractFilter.java @@ -125,6 +125,16 @@ public T filterExpression(String field, Operator operator, Expression expression return addFilters(Filter.expression(field, operator, expression)); } + /** + * SQL过滤 + * + * @param sqlFilterHandler 获取SQL接口,参数支持字段处理 + * @param values 占位符对应值 + */ + public T sqlFilter(Filter.SqlFilterHandler sqlFilterHandler, Object... values) { + return addFilter(Filter.sqlFilter(sqlFilterHandler, values)); + } + public List getFilters() { return filters; } diff --git a/src/main/java/cn/veasion/db/base/Filter.java b/src/main/java/cn/veasion/db/base/Filter.java index 1186ff2..db7ee2e 100644 --- a/src/main/java/cn/veasion/db/base/Filter.java +++ b/src/main/java/cn/veasion/db/base/Filter.java @@ -104,6 +104,16 @@ public static Filter expression(String field, Operator operator, Expression expr return build(field, operator, expression, null).special(); } + /** + * SQL过滤 + * + * @param sqlFilterHandler 获取SQL接口,参数支持字段处理 + * @param values 占位符对应值 + */ + public static Filter sqlFilter(SqlFilterHandler sqlFilterHandler, Object... values) { + return new SqlFilter(sqlFilterHandler, values); + } + public static Filter AND = build("AND"); public static Filter OR = build("OR"); public static Filter LEFT_BRACKET = build("("); @@ -216,4 +226,52 @@ public String toString() { } } + @FunctionalInterface + public interface SqlFilterHandler { + String getSQL(ColumnFieldHandler columnFieldHandler); + } + + @FunctionalInterface + public interface ColumnFieldHandler { + String asField(String columnField); + } + + public static class SqlFilter extends Filter { + + private String tableAs; + private SqlFilterHandler sqlFilterHandler; + + private SqlFilter(SqlFilterHandler sqlFilterHandler, Object... values) { + this.sqlFilterHandler = sqlFilterHandler; + if (values != null && values.length > 0) { + super.value = Arrays.asList(values); + } + } + + @Override + public String getSql() { + if (tableAs != null) { + return sqlFilterHandler.getSQL(field -> FilterUtils.tableAsField(tableAs, field)); + } else { + return sqlFilterHandler.getSQL(field -> field); + } + } + + @Override + public SqlFilter fieldAs(String tableAs) { + this.tableAs = tableAs; + return this; + } + + @Override + public boolean isSpecial() { + return true; + } + + @Override + public String toString() { + return getSql(); + } + } + } diff --git a/src/main/java/cn/veasion/db/base/Operator.java b/src/main/java/cn/veasion/db/base/Operator.java index 4c2420a..626d283 100644 --- a/src/main/java/cn/veasion/db/base/Operator.java +++ b/src/main/java/cn/veasion/db/base/Operator.java @@ -32,4 +32,14 @@ public enum Operator { public String getOpt() { return opt; } + + public static Operator of(String opt) { + for (Operator value : values()) { + if (value.opt.equalsIgnoreCase(opt)) { + return value; + } + } + return null; + } + } diff --git a/src/main/java/cn/veasion/db/jdbc/AbstractSQL.java b/src/main/java/cn/veasion/db/jdbc/AbstractSQL.java index fdb7ee2..be14889 100644 --- a/src/main/java/cn/veasion/db/jdbc/AbstractSQL.java +++ b/src/main/java/cn/veasion/db/jdbc/AbstractSQL.java @@ -84,6 +84,12 @@ protected void appendFilter(Map> entityClassMap, List f sql.append(" ").append(filter.getOperator().getOpt()).append(" "); Expression expression = (Expression) filter.getValue(); appendExpressionValue(entityClassMap, expression); + } else if (filter instanceof Filter.SqlFilter) { + // sql filter + sql.append(filter.getSql()); + if (filter.getValue() instanceof Collection) { + values.addAll((Collection) filter.getValue()); + } } else { throw new FilterException("不支持过滤器:" + filter); } diff --git a/src/main/java/cn/veasion/db/jdbc/InsertSQL.java b/src/main/java/cn/veasion/db/jdbc/InsertSQL.java index fc9bb95..94d952b 100644 --- a/src/main/java/cn/veasion/db/jdbc/InsertSQL.java +++ b/src/main/java/cn/veasion/db/jdbc/InsertSQL.java @@ -63,11 +63,13 @@ private void insert(Class entityClazz, List> fieldValueMa Map fieldColumns = FieldUtils.entityFieldColumns(entityClazz); Set fields = fieldValueMapList.get(0).keySet(); - if (source instanceof EntityInsert && ((EntityInsert) source).isReplace()) { + if ((source instanceof EntityInsert && ((EntityInsert) source).isReplace()) || + (source instanceof BatchEntityInsert && ((BatchEntityInsert) source).isReplace())) { sql.append("REPLACE INTO "); } else { sql.append("INSERT INTO "); } + sql.append(getTableName(entityClazz, null, source)).append(" ("); for (String field : fields) { appendInsertColumn(fieldColumns.get(field)); diff --git a/src/main/java/cn/veasion/db/parser/DbExpressionVisitor.java b/src/main/java/cn/veasion/db/parser/DbExpressionVisitor.java index 3d3d147..9057d54 100644 --- a/src/main/java/cn/veasion/db/parser/DbExpressionVisitor.java +++ b/src/main/java/cn/veasion/db/parser/DbExpressionVisitor.java @@ -478,7 +478,23 @@ private void appendVarFun(String s, boolean line) { private void handleComparisonOperator(ComparisonOperator operator, String opt) { Expression leftExpression = operator.getLeftExpression(); Expression rightExpression = operator.getRightExpression(); - leftRight(leftExpression, opt, rightExpression); + + if (leftExpression instanceof Function) { + if (havingFilter) { + appendVarFun("having(", false); + } else if (onFilter) { + appendVarFun("on(", false); + } else { + appendVarFun("addFilter(", false); + } + sb.append("Filter.sqlFilter(fieldHandler -> \"").append(operator.toString()).append("\")"); + sb.append(")"); + if (!onFilter) { + sb.append(";\r\n"); + } + } else { + leftRight(leftExpression, opt, rightExpression); + } } private void leftRight(Expression leftExpression, String opt, Expression rightExpression) { diff --git a/src/main/java/cn/veasion/db/parser/DbStatementVisitor.java b/src/main/java/cn/veasion/db/parser/DbStatementVisitor.java index 60ce22f..f947434 100644 --- a/src/main/java/cn/veasion/db/parser/DbStatementVisitor.java +++ b/src/main/java/cn/veasion/db/parser/DbStatementVisitor.java @@ -11,6 +11,7 @@ import net.sf.jsqlparser.statement.StatementVisitorAdapter; import net.sf.jsqlparser.statement.delete.Delete; import net.sf.jsqlparser.statement.insert.Insert; +import net.sf.jsqlparser.statement.replace.Replace; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.update.Update; @@ -63,6 +64,32 @@ public void visit(Insert insert) { } } + @Override + public void visit(Replace replace) { + Table table = replace.getTable(); + List columns = replace.getColumns(); + ItemsList itemsList = replace.getItemsList(); + String var = SQLParseUtils.getVarByTable(table); + String entity = SQLParseUtils.getByTable(table); + sb.append(entity).append(" ").append(var); + sb.append(" = new ").append(entity).append("();\r\n"); + if (columns != null && !columns.isEmpty()) { + for (Column column : columns) { + String fieldSetter = FieldUtils.firstCase(SQLParseUtils.columnToField(column.getColumnName()), false); + sb.append(var).append(".set").append(fieldSetter).append("(null);\r\n"); + } + } + sb.append("\r\n"); + if (itemsList instanceof MultiExpressionList) { + sb.append("List<").append(entity); + sb.append("> list = new ArrayList<>();\r\n"); + sb.append("list.add(").append(var).append(");\r\n"); + sb.append("BatchEntityInsert batchInsert = new BatchEntityInsert(list).withReplace();"); + } else { + sb.append("EntityInsert insert = new EntityInsert(").append(var).append(").withReplace();"); + } + } + @Override public void visit(Delete delete) { if (delete.getJoins() != null) { diff --git a/src/main/java/cn/veasion/db/update/BatchEntityInsert.java b/src/main/java/cn/veasion/db/update/BatchEntityInsert.java index 9240fac..85e1611 100644 --- a/src/main/java/cn/veasion/db/update/BatchEntityInsert.java +++ b/src/main/java/cn/veasion/db/update/BatchEntityInsert.java @@ -28,6 +28,7 @@ public class BatchEntityInsert { private boolean useGeneratedKeys = true; private AbstractQuery insertSelectQuery; private List> fieldValueMapList; + private boolean replace; public BatchEntityInsert(List entityList) { this.entityList = Objects.requireNonNull(entityList); @@ -49,6 +50,18 @@ public BatchEntityInsert setUseGeneratedKeys(boolean useGeneratedKeys) { return this; } + public BatchEntityInsert withReplace() { + if (this.insertSelectQuery != null) { + throw new DbException("insert select 方式不支持 replace"); + } + this.replace = true; + return this; + } + + public boolean isReplace() { + return replace; + } + public boolean isUseGeneratedKeys() { return useGeneratedKeys; } diff --git a/src/test/java/cn/veasion/db/parser/SqlDbConvertTest.java b/src/test/java/cn/veasion/db/parser/SqlDbConvertTest.java index 04badc3..930b53e 100644 --- a/src/test/java/cn/veasion/db/parser/SqlDbConvertTest.java +++ b/src/test/java/cn/veasion/db/parser/SqlDbConvertTest.java @@ -13,8 +13,9 @@ public static void main(String[] args) { " where t.age > 0 and t.id in (1,2,3) and (name like '%sss%' or name like 'ss%')" + " and id <= 0 and user_age <> ifnull(age, 0) and `desc` is not null"; System.out.println(sql); + System.out.println(); System.out.println(SQLParseUtils.parseSQLConvert(sql)); - System.out.println("===============================\r\n"); + System.out.println("==============================="); sql = "select s.*, t.name from t_student s, t_classes c left join t_user u " + "on c.id = u.id and c.id > s.age and c.is_deleted = 0" + @@ -22,29 +23,46 @@ public static void main(String[] args) { "having count(s.id) > 1 and avg(c.age) >= 18 order by c.create_time desc, s.id " + "limit 10, 10"; System.out.println(sql); + System.out.println(); System.out.println(SQLParseUtils.parseSQLConvert(sql)); - System.out.println("===============================\r\n"); + System.out.println("==============================="); sql = "select id, name, (select age from t_classes where id = t.class_id) as '_age' " + "from (select id, name from t_student where id > 0) t " + "where t.id in (select id from t_classes where id > 50) and exists (select 1 from t_user) " + "union select id, name, age from t_user where id > 0 order by id desc limit 10"; System.out.println(sql); + System.out.println(); System.out.println(SQLParseUtils.parseSQLConvert(sql)); - System.out.println("===============================\r\n"); + System.out.println("==============================="); + + sql = "select * from t_score where ifnull(score, 0) = score + 0"; + System.out.println(sql); + System.out.println(); + System.out.println(SQLParseUtils.parseSQLConvert(sql)); + System.out.println("==============================="); + + sql = "replace into t_student(id, name, create_time) values(1, 'test', now())"; + System.out.println(sql); + System.out.println(); + System.out.println(SQLParseUtils.parseSQLConvert(sql)); + System.out.println("==============================="); sql = "insert into t_student(id, name, create_time) select id, name, now() from t_classes where id > 0"; System.out.println(sql); + System.out.println(); System.out.println(SQLParseUtils.parseSQLConvert(sql)); - System.out.println("===============================\r\n"); + System.out.println("==============================="); sql = "delete from t_student where id > 0 and age = ifnull(id, 1)"; System.out.println(sql); + System.out.println(); System.out.println(SQLParseUtils.parseSQLConvert(sql)); - System.out.println("===============================\r\n"); + System.out.println("==============================="); sql = "update t_student s, t_classes c set age = 18, c.name = 'sss', c.age = now() where s.class_id = c.id and s.id > 0 and c.id < 10"; System.out.println(sql); + System.out.println(); System.out.println(SQLParseUtils.parseSQLConvert(sql)); } diff --git a/src/test/java/cn/veasion/db/query/OtherTest.java b/src/test/java/cn/veasion/db/query/OtherTest.java index 892384d..b019b8c 100644 --- a/src/test/java/cn/veasion/db/query/OtherTest.java +++ b/src/test/java/cn/veasion/db/query/OtherTest.java @@ -18,11 +18,12 @@ public static void main(String[] args) { .select("id") .selectExpression("ifnull(${id}, ${sno})", "`key`") .filterExpression("id", Operator.EQ, "${id} + 1") + .sqlFilter(handler -> handler.asField("id") + " > ?", 0) )); studentDao.update(new EU(StudentPO.class, "t") .updateExpression("id", "${id}") .filterExpression("id", Operator.EQ, "${id}") - .lt("id", 5) + .sqlFilter(handler -> handler.asField("id") + " < 5") ); }