From 5d294ea172b7e97b1f814f0abbd44177b51b1a1f Mon Sep 17 00:00:00 2001 From: Marc Baloup Date: Sat, 2 Mar 2024 23:23:45 +0100 Subject: [PATCH] DB: some improvements - Refactor auto-conversion of custom types - Ability to create a WHERE ... IN ... expression with a custom left operand - Typos in Javadoc --- .../java/fr/pandacube/lib/db/SQLElement.java | 16 +-- .../java/fr/pandacube/lib/db/SQLField.java | 49 +++++++-- .../fr/pandacube/lib/db/SQLUpdateBuilder.java | 2 +- .../java/fr/pandacube/lib/db/SQLWhere.java | 99 ++++++++++++++----- 4 files changed, 117 insertions(+), 49 deletions(-) diff --git a/pandalib-db/src/main/java/fr/pandacube/lib/db/SQLElement.java b/pandalib-db/src/main/java/fr/pandacube/lib/db/SQLElement.java index c165f06..6ee4d16 100644 --- a/pandalib-db/src/main/java/fr/pandacube/lib/db/SQLElement.java +++ b/pandalib-db/src/main/java/fr/pandacube/lib/db/SQLElement.java @@ -361,7 +361,7 @@ public abstract class SQLElement> { first = false; concatValues.append(" ? "); concatFields.append("`").append(entry.getKey().getName()).append("`"); - addValueToSQLObjectList(psValues, entry.getKey(), entry.getValue()); + psValues.add(entry.getKey().fromJavaTypeToJDBCType(entry.getValue())); } try (Connection c = db.getConnection(); @@ -389,20 +389,6 @@ public abstract class SQLElement> { return (E) this; } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - /* package */ static > void addValueToSQLObjectList(List list, SQLField field, Object jValue) throws DBException { - if (jValue != null && field.type instanceof SQLCustomType) { - try { - jValue = ((SQLCustomType)field.type).javaToDbConv.apply(jValue); - } catch (Exception e) { - throw new DBException("Error while converting value of field '"+field.getName()+"' with SQLCustomType from "+field.type.getJavaType() - +"(java source) to "+((SQLCustomType)field.type).intermediateJavaType+"(jdbc destination). The original value is '"+jValue+"'", e); - } - } - list.add(jValue); - } - /** * Tells if this entry is currently stored in DB or not. * @return true if this entry is currently stored in DB, or false otherwise. diff --git a/pandalib-db/src/main/java/fr/pandacube/lib/db/SQLField.java b/pandalib-db/src/main/java/fr/pandacube/lib/db/SQLField.java index 061dc1a..e007901 100644 --- a/pandalib-db/src/main/java/fr/pandacube/lib/db/SQLField.java +++ b/pandalib-db/src/main/java/fr/pandacube/lib/db/SQLField.java @@ -133,7 +133,7 @@ public class SQLField, T> { /** * Create a SQL {@code WHERE} expression comparing this field with the provided value using the {@code =} operator. * @param r the value to compare with. - * @return a SQL {@code WHERE} expression. + * @return a new SQL {@code WHERE} expression. */ public fr.pandacube.lib.db.SQLWhere eq(T r) { return comp(SQLComparator.EQ, r); @@ -142,7 +142,7 @@ public class SQLField, T> { /** * Create a SQL {@code WHERE} expression comparing this field with the provided value using the {@code >=} operator. * @param r the value to compare with. - * @return a SQL {@code WHERE} expression. + * @return a new SQL {@code WHERE} expression. */ public fr.pandacube.lib.db.SQLWhere geq(T r) { return comp(SQLComparator.GEQ, r); @@ -151,7 +151,7 @@ public class SQLField, T> { /** * Create a SQL {@code WHERE} expression comparing this field with the provided value using the {@code >} operator. * @param r the value to compare with. - * @return a SQL {@code WHERE} expression. + * @return a new SQL {@code WHERE} expression. */ public fr.pandacube.lib.db.SQLWhere gt(T r) { return comp(SQLComparator.GT, r); @@ -160,7 +160,7 @@ public class SQLField, T> { /** * Create a SQL {@code WHERE} expression comparing this field with the provided value using the {@code <=} operator. * @param r the value to compare with. - * @return a SQL {@code WHERE} expression. + * @return a new SQL {@code WHERE} expression. */ public fr.pandacube.lib.db.SQLWhere leq(T r) { return comp(SQLComparator.LEQ, r); @@ -169,7 +169,7 @@ public class SQLField, T> { /** * Create a SQL {@code WHERE} expression comparing this field with the provided value using the {@code <} operator. * @param r the value to compare with. - * @return a SQL {@code WHERE} expression. + * @return a new SQL {@code WHERE} expression. */ public fr.pandacube.lib.db.SQLWhere lt(T r) { return comp(SQLComparator.LT, r); @@ -178,7 +178,7 @@ public class SQLField, T> { /** * Create a SQL {@code WHERE} expression comparing this field with the provided value using the {@code !=} operator. * @param r the value to compare with. - * @return a SQL {@code WHERE} expression. + * @return a new SQL {@code WHERE} expression. */ public fr.pandacube.lib.db.SQLWhere neq(T r) { return comp(SQLComparator.NEQ, r); @@ -194,7 +194,7 @@ public class SQLField, T> { * Create a SQL {@code WHERE} expression comparing this field with the provided value using the {@code LIKE} * keyword. * @param like the value to compare with. - * @return a SQL {@code WHERE} expression. + * @return a new SQL {@code WHERE} expression. */ public fr.pandacube.lib.db.SQLWhere like(String like) { return new SQLWhereLike<>(this, like); @@ -206,7 +206,7 @@ public class SQLField, T> { * Create a SQL {@code WHERE} expression testing the presence of this field in the provided collection of value * using the {@code IN} keyword. * @param v the value to compare with. - * @return a SQL {@code WHERE} expression. + * @return a new SQL {@code WHERE} expression. */ public fr.pandacube.lib.db.SQLWhere in(Collection v) { return new SQLWhereIn<>(this, v); @@ -216,7 +216,7 @@ public class SQLField, T> { /** * Create a SQL {@code WHERE} expression testing the nullity of this field using the {@code IS NULL} keyword. - * @return a SQL {@code WHERE} expression. + * @return a new SQL {@code WHERE} expression. */ public fr.pandacube.lib.db.SQLWhere isNull() { return new SQLWhereNull<>(this, true); @@ -225,10 +225,39 @@ public class SQLField, T> { /** * Create a SQL {@code WHERE} expression testing the non-nullity of this field using the {@code IS NOT NULL} * keyword. - * @return a SQL {@code WHERE} expression. + * @return a new SQL {@code WHERE} expression. */ public fr.pandacube.lib.db.SQLWhere isNotNull() { return new SQLWhereNull<>(this, false); } + + + + + + @SuppressWarnings({"rawtypes", "unchecked"}) + public Object fromJavaTypeToJDBCType(Object value) throws DBException { + Object ret = value; + if (value != null && type instanceof SQLCustomType customType) { + try { + ret = customType.javaToDbConv.apply(value); + } catch (Exception e) { + throw new DBException("Error while converting value of field '" + name + "' with SQLCustomType from " + type.getJavaType() + + "(java source) to " + customType.intermediateJavaType + "(jdbc destination). The original value is '" + value + "'", e); + } + } + return ret; + } + + public Collection fromListJavaTypeToJDBCType(Collection values) throws DBException { + if (values == null) + return null; + List ret = new ArrayList<>(values.size()); + for (Object value : values) { + ret.add(fromJavaTypeToJDBCType(value)); + } + return ret; + } + } diff --git a/pandalib-db/src/main/java/fr/pandacube/lib/db/SQLUpdateBuilder.java b/pandalib-db/src/main/java/fr/pandacube/lib/db/SQLUpdateBuilder.java index 7363b2c..06c958d 100644 --- a/pandalib-db/src/main/java/fr/pandacube/lib/db/SQLUpdateBuilder.java +++ b/pandalib-db/src/main/java/fr/pandacube/lib/db/SQLUpdateBuilder.java @@ -74,7 +74,7 @@ public class SQLUpdateBuilder> { if (!first) sql.append(", "); sql.append("`").append(entry.getKey().getName()).append("` = ? "); - SQLElement.addValueToSQLObjectList(params, entry.getKey(), entry.getValue()); + params.add(entry.getKey().fromJavaTypeToJDBCType(entry.getValue())); first = false; } diff --git a/pandalib-db/src/main/java/fr/pandacube/lib/db/SQLWhere.java b/pandalib-db/src/main/java/fr/pandacube/lib/db/SQLWhere.java index e677474..bf934df 100644 --- a/pandalib-db/src/main/java/fr/pandacube/lib/db/SQLWhere.java +++ b/pandalib-db/src/main/java/fr/pandacube/lib/db/SQLWhere.java @@ -3,11 +3,12 @@ package fr.pandacube.lib.db; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Objects; import fr.pandacube.lib.util.log.Log; /** - * A SQL {@code WHERE} expression. + * SQL {@code WHERE} expression. * @param the table type. */ public abstract class SQLWhere> { @@ -29,7 +30,7 @@ public abstract class SQLWhere> { * Create a SQL {@code WHERE} expression that is true when this expression {@code AND} the provided expression is * true. * @param other the other expression. - * @return a SQL {@code WHERE} expression. + * @return a new SQL {@code WHERE} expression. */ public SQLWhere and(SQLWhere other) { return SQLWhere.and().and(this).and(other); @@ -39,7 +40,7 @@ public abstract class SQLWhere> { * Create a SQL {@code WHERE} expression that is true when this expression {@code OR} the provided expression is * true. * @param other the other expression. - * @return a SQL {@code WHERE} expression. + * @return a new SQL {@code WHERE} expression. */ public SQLWhere or(SQLWhere other) { return SQLWhere.or().or(this).or(other); @@ -48,7 +49,7 @@ public abstract class SQLWhere> { /** * Create a SQL {@code WHERE} expression builder joining multiple expressions with the {@code AND} operator. - * @return a SQL {@code WHERE} expression. + * @return a new SQL {@code WHERE} expression. * @param the table type. */ public static > SQLWhereAndBuilder and() { @@ -57,7 +58,7 @@ public abstract class SQLWhere> { /** * Create a SQL {@code WHERE} expression builder joining multiple expressions with the {@code OR} operator. - * @return a SQL {@code WHERE} expression. + * @return a new SQL {@code WHERE} expression. * @param the table type. */ public static > SQLWhereOrBuilder or() { @@ -67,7 +68,7 @@ public abstract class SQLWhere> { /** * Create a custom SQL {@code WHERE} expression. * @param whereExpr the raw SQL {@code WHERE} expression. - * @return a SQL {@code WHERE} expression. + * @return a new SQL {@code WHERE} expression. */ public static > SQLWhere expression(String whereExpr) { return expression(whereExpr, List.of()); @@ -77,12 +78,33 @@ public abstract class SQLWhere> { * Create a custom SQL {@code WHERE} expression. * @param whereExpr the raw SQL {@code WHERE} expression. * @param params the parameters of the provided expression. - * @return a SQL {@code WHERE} expression. + * @return a new SQL {@code WHERE} expression. */ public static > SQLWhere expression(String whereExpr, List params) { return new SQLWhereCustomExpression<>(whereExpr, params); } + /** + * Create a SQL {@code WHERE ... IN ...} expression with a custom left operand. + * @param leftExpr the raw SQL left operand. + * @param valuesIn the values on the right of the {@code IN} operator. + * @return a new SQL {@code WHERE} expression. + */ + public static > SQLWhere expressionIn(String leftExpr, Collection valuesIn) { + return expressionIn(leftExpr, List.of(), valuesIn); + } + + /** + * Create a SQL {@code WHERE ... IN ...} expression with a custom left operand. + * @param leftExpr the raw SQL left operand. + * @param leftParams the parameters of the left operand. + * @param valuesIn the values on the right of the {@code IN} operator. + * @return a new SQL {@code WHERE} expression. + */ + public static > SQLWhere expressionIn(String leftExpr, List leftParams, Collection valuesIn) { + return new SQLWhereInCustom<>(leftExpr, leftParams, valuesIn); + } + @@ -232,9 +254,8 @@ public abstract class SQLWhere> { @Override /* package */ ParameterizedSQLString toSQL() throws DBException { - List params = new ArrayList<>(); - SQLElement.addValueToSQLObjectList(params, left, right); - return new ParameterizedSQLString("`" + left.getName() + "` " + comp.sql + " ? ", params); + return new ParameterizedSQLString("`" + left.getName() + "` " + comp.sql + " ? ", + List.of(left.fromJavaTypeToJDBCType(right))); } /* package */ enum SQLComparator { @@ -266,33 +287,39 @@ public abstract class SQLWhere> { - /* package */ static class SQLWhereIn> extends SQLWhere { + /* package */ static class SQLWhereInCustom> extends SQLWhere { - private final SQLField field; - private final Collection values; + private final String leftExpression; + private final List leftExpressionParameters; + protected Collection collectionIn; - /* package */ SQLWhereIn(SQLField f, Collection v) { - if (f == null || v == null) - throw new IllegalArgumentException("All arguments for SQLWhereIn constructor can't be null"); - field = f; - values = v; + /* package */ SQLWhereInCustom(String leftExpr, List leftExprParams, Collection collectionIn) { + if (leftExpr == null) + throw new IllegalArgumentException("leftExpr can't be null"); + if (leftExprParams == null) + leftExprParams = List.of(); + if (collectionIn == null) + collectionIn = List.of(); + leftExpression = leftExpr; + leftExpressionParameters = leftExprParams; + this.collectionIn = collectionIn; } @Override /* package */ ParameterizedSQLString toSQL() throws DBException { List params = new ArrayList<>(); - if (values.isEmpty()) + if (collectionIn.isEmpty()) return new ParameterizedSQLString(" 1=0 ", params); - for (Object v : values) - SQLElement.addValueToSQLObjectList(params, field, v); + params.addAll(leftExpressionParameters); + params.addAll(collectionIn); - char[] questions = new char[values.isEmpty() ? 0 : (values.size() * 2 - 1)]; + char[] questions = new char[collectionIn.size() * 2 - 1]; for (int i = 0; i < questions.length; i++) questions[i] = i % 2 == 0 ? '?' : ','; - return new ParameterizedSQLString("`" + field.getName() + "` IN (" + new String(questions) + ") ", params); + return new ParameterizedSQLString("(" + leftExpression + ") IN (" + new String(questions) + ") ", params); } } @@ -302,6 +329,32 @@ public abstract class SQLWhere> { + /* package */ static class SQLWhereIn> extends SQLWhereInCustom { + + private final SQLField field; + private boolean collectionFiltered = false; + + /* package */ SQLWhereIn(SQLField f, Collection v) { + super("`" + Objects.requireNonNull(f).getName() + "`", List.of(), v); + field = f; + } + + + @Override + ParameterizedSQLString toSQL() throws DBException { + if (!collectionFiltered) { + collectionIn = field.fromListJavaTypeToJDBCType(collectionIn); + collectionFiltered = true; + } + return super.toSQL(); + } + } + + + + + + /* package */ static class SQLWhereLike> extends SQLWhere {