DB: some improvements

- Refactor auto-conversion of custom types
- Ability to create a WHERE ... IN ... expression with a custom left operand
- Typos in Javadoc
This commit is contained in:
Marc Baloup 2024-03-02 23:23:45 +01:00
parent 56632867ec
commit 5d294ea172
4 changed files with 117 additions and 49 deletions

View File

@ -361,7 +361,7 @@ public abstract class SQLElement<E extends SQLElement<E>> {
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<E extends SQLElement<E>> {
return (E) this;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
/* package */ static <E extends SQLElement<E>> void addValueToSQLObjectList(List<Object> list, SQLField<E, ?> 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.

View File

@ -133,7 +133,7 @@ public class SQLField<E extends SQLElement<E>, 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<E> eq(T r) {
return comp(SQLComparator.EQ, r);
@ -142,7 +142,7 @@ public class SQLField<E extends SQLElement<E>, 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<E> geq(T r) {
return comp(SQLComparator.GEQ, r);
@ -151,7 +151,7 @@ public class SQLField<E extends SQLElement<E>, 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<E> gt(T r) {
return comp(SQLComparator.GT, r);
@ -160,7 +160,7 @@ public class SQLField<E extends SQLElement<E>, 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<E> leq(T r) {
return comp(SQLComparator.LEQ, r);
@ -169,7 +169,7 @@ public class SQLField<E extends SQLElement<E>, 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<E> lt(T r) {
return comp(SQLComparator.LT, r);
@ -178,7 +178,7 @@ public class SQLField<E extends SQLElement<E>, 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<E> neq(T r) {
return comp(SQLComparator.NEQ, r);
@ -194,7 +194,7 @@ public class SQLField<E extends SQLElement<E>, 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<E> like(String like) {
return new SQLWhereLike<>(this, like);
@ -206,7 +206,7 @@ public class SQLField<E extends SQLElement<E>, 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<E> in(Collection<T> v) {
return new SQLWhereIn<>(this, v);
@ -216,7 +216,7 @@ public class SQLField<E extends SQLElement<E>, 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<E> isNull() {
return new SQLWhereNull<>(this, true);
@ -225,10 +225,39 @@ public class SQLField<E extends SQLElement<E>, 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<E> 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<Object> fromListJavaTypeToJDBCType(Collection<?> values) throws DBException {
if (values == null)
return null;
List<Object> ret = new ArrayList<>(values.size());
for (Object value : values) {
ret.add(fromJavaTypeToJDBCType(value));
}
return ret;
}
}

View File

@ -74,7 +74,7 @@ public class SQLUpdateBuilder<E extends SQLElement<E>> {
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;
}

View File

@ -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 <E> the table type.
*/
public abstract class SQLWhere<E extends SQLElement<E>> {
@ -29,7 +30,7 @@ public abstract class SQLWhere<E extends SQLElement<E>> {
* 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<E> and(SQLWhere<E> other) {
return SQLWhere.<E>and().and(this).and(other);
@ -39,7 +40,7 @@ public abstract class SQLWhere<E extends SQLElement<E>> {
* 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<E> or(SQLWhere<E> other) {
return SQLWhere.<E>or().or(this).or(other);
@ -48,7 +49,7 @@ public abstract class SQLWhere<E extends SQLElement<E>> {
/**
* 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 <E> the table type.
*/
public static <E extends SQLElement<E>> SQLWhereAndBuilder<E> and() {
@ -57,7 +58,7 @@ public abstract class SQLWhere<E extends SQLElement<E>> {
/**
* 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 <E> the table type.
*/
public static <E extends SQLElement<E>> SQLWhereOrBuilder<E> or() {
@ -67,7 +68,7 @@ public abstract class SQLWhere<E extends SQLElement<E>> {
/**
* 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 <E extends SQLElement<E>> SQLWhere<E> expression(String whereExpr) {
return expression(whereExpr, List.of());
@ -77,12 +78,33 @@ public abstract class SQLWhere<E extends SQLElement<E>> {
* 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 <E extends SQLElement<E>> SQLWhere<E> expression(String whereExpr, List<Object> 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 <E extends SQLElement<E>> SQLWhere<E> 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 <E extends SQLElement<E>> SQLWhere<E> expressionIn(String leftExpr, List<Object> leftParams, Collection<?> valuesIn) {
return new SQLWhereInCustom<>(leftExpr, leftParams, valuesIn);
}
@ -232,9 +254,8 @@ public abstract class SQLWhere<E extends SQLElement<E>> {
@Override
/* package */ ParameterizedSQLString toSQL() throws DBException {
List<Object> 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<E extends SQLElement<E>> {
/* package */ static class SQLWhereIn<E extends SQLElement<E>> extends SQLWhere<E> {
/* package */ static class SQLWhereInCustom<E extends SQLElement<E>> extends SQLWhere<E> {
private final SQLField<E, ?> field;
private final Collection<?> values;
private final String leftExpression;
private final List<Object> leftExpressionParameters;
protected Collection<?> collectionIn;
/* package */ <T> SQLWhereIn(SQLField<E, T> f, Collection<T> v) {
if (f == null || v == null)
throw new IllegalArgumentException("All arguments for SQLWhereIn constructor can't be null");
field = f;
values = v;
/* package */ <T> SQLWhereInCustom(String leftExpr, List<Object> leftExprParams, Collection<T> 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<Object> 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<E extends SQLElement<E>> {
/* package */ static class SQLWhereIn<E extends SQLElement<E>> extends SQLWhereInCustom<E> {
private final SQLField<E, ?> field;
private boolean collectionFiltered = false;
/* package */ <T> SQLWhereIn(SQLField<E, T> f, Collection<T> 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<E extends SQLElement<E>> extends SQLWhere<E> {