From 1075eb5000d6954ae5919b0257b782ab708332ba Mon Sep 17 00:00:00 2001 From: Ravi Chodavarapu Date: Fri, 26 Aug 2016 17:00:26 +0100 Subject: [PATCH] Fix support for wrapper values being autounboxed for constructors with primitives in Wirings Remove the need for execute() call on query building Support complex conditions with conjunctions and creating sub-expressions joined by conjunctions --- .../stack/datamill/configuration/Wiring.java | 15 + .../datamill/configuration/impl/Classes.java | 2 +- .../stack/datamill/db/ConditionBuilder.java | 41 ++- .../stack/datamill/db/ConjunctionBuilder.java | 9 + .../stack/datamill/db/TerminalCondition.java | 7 + .../stack/datamill/db/WhereBuilder.java | 5 +- .../db/impl/EmptyUpdateSuffixBuilder.java | 34 +++ .../datamill/db/impl/QueryBuilderImpl.java | 283 ++---------------- .../stack/datamill/db/impl/SqlSyntax.java | 35 +++ .../datamill/db/impl/WhereBuilderImpl.java | 228 ++++++++++++++ .../db/impl/QueryBuilderImplTest.java | 94 ++++-- 11 files changed, 450 insertions(+), 303 deletions(-) create mode 100644 core/src/main/java/foundation/stack/datamill/db/ConjunctionBuilder.java create mode 100644 core/src/main/java/foundation/stack/datamill/db/TerminalCondition.java create mode 100644 core/src/main/java/foundation/stack/datamill/db/impl/EmptyUpdateSuffixBuilder.java create mode 100644 core/src/main/java/foundation/stack/datamill/db/impl/SqlSyntax.java create mode 100644 core/src/main/java/foundation/stack/datamill/db/impl/WhereBuilderImpl.java diff --git a/core/src/main/java/foundation/stack/datamill/configuration/Wiring.java b/core/src/main/java/foundation/stack/datamill/configuration/Wiring.java index ac9b69f..3104122 100644 --- a/core/src/main/java/foundation/stack/datamill/configuration/Wiring.java +++ b/core/src/main/java/foundation/stack/datamill/configuration/Wiring.java @@ -325,6 +325,14 @@ private Object getValueForParameterByType(Parameter parameter) { return value; } + if (type.isPrimitive()) { + Class wrapper = Classes.primitiveToWrapper(type); + value = getObjectOfType(wrapper); + if (value != null) { + return value; + } + } + value = getValueOfType(type); if (value != null) { return value; @@ -396,6 +404,13 @@ private Object getValueForNamedParameter(Parameter parameter, Named name) { return value; } + if (type.isPrimitive()) { + Class wrapper = Classes.primitiveToWrapper(type); + if (wrapper.isInstance(value)) { + return value; + } + } + if (Value.class.isAssignableFrom(value.getClass())) { return castValueToTypeIfPossible((Value) value, type); } diff --git a/core/src/main/java/foundation/stack/datamill/configuration/impl/Classes.java b/core/src/main/java/foundation/stack/datamill/configuration/impl/Classes.java index 19711fd..ab00773 100644 --- a/core/src/main/java/foundation/stack/datamill/configuration/impl/Classes.java +++ b/core/src/main/java/foundation/stack/datamill/configuration/impl/Classes.java @@ -53,7 +53,7 @@ public class Classes { } } - private static Class primitiveToWrapper(final Class clazz) { + public static Class primitiveToWrapper(final Class clazz) { Class convertedClass = clazz; if (clazz != null && clazz.isPrimitive()) { convertedClass = primitiveWrapperMap.get(clazz); diff --git a/core/src/main/java/foundation/stack/datamill/db/ConditionBuilder.java b/core/src/main/java/foundation/stack/datamill/db/ConditionBuilder.java index c989995..dcc72fe 100644 --- a/core/src/main/java/foundation/stack/datamill/db/ConditionBuilder.java +++ b/core/src/main/java/foundation/stack/datamill/db/ConditionBuilder.java @@ -1,22 +1,39 @@ package foundation.stack.datamill.db; import foundation.stack.datamill.reflection.Member; +import rx.functions.Func1; import java.util.Collection; /** * @author Ravi Chodavarapu (rchodava@gmail.com) */ -public interface ConditionBuilder { - WhereBuilder eq(String column, T value); - WhereBuilder eq(String table, String column, T value); - WhereBuilder eq(Member member, T value); - - WhereBuilder is(String column, T value); - WhereBuilder is(String table, String column, T value); - WhereBuilder is(Member member, T value); - - WhereBuilder in(String column, Collection value); - WhereBuilder in(String table, String column, Collection value); - WhereBuilder in(Member member, Collection value); +public interface ConditionBuilder { + ConjunctionBuilder eq(String column, T value); + ConjunctionBuilder eq(String table, String column, T value); + ConjunctionBuilder eq(Member member, T value); + + ConjunctionBuilder lt(String column, T value); + ConjunctionBuilder lt(String table, String column, T value); + ConjunctionBuilder lt(Member member, T value); + + ConjunctionBuilder gt(String column, T value); + ConjunctionBuilder gt(String table, String column, T value); + ConjunctionBuilder gt(Member member, T value); + + ConjunctionBuilder is(String column, T value); + ConjunctionBuilder is(String table, String column, T value); + ConjunctionBuilder is(Member member, T value); + + ConjunctionBuilder in(String column, Collection value); + ConjunctionBuilder in(String table, String column, Collection value); + ConjunctionBuilder in(Member member, Collection value); + + TerminalCondition and( + Func1 left, + Func1 right); + + TerminalCondition or( + Func1 left, + Func1 right); } diff --git a/core/src/main/java/foundation/stack/datamill/db/ConjunctionBuilder.java b/core/src/main/java/foundation/stack/datamill/db/ConjunctionBuilder.java new file mode 100644 index 0000000..c0175ab --- /dev/null +++ b/core/src/main/java/foundation/stack/datamill/db/ConjunctionBuilder.java @@ -0,0 +1,9 @@ +package foundation.stack.datamill.db; + +/** + * @author Ravi Chodavarapu (rchodava@gmail.com) + */ +public interface ConjunctionBuilder extends TerminalCondition { + ConditionBuilder and(); + ConditionBuilder or(); +} diff --git a/core/src/main/java/foundation/stack/datamill/db/TerminalCondition.java b/core/src/main/java/foundation/stack/datamill/db/TerminalCondition.java new file mode 100644 index 0000000..04c4a30 --- /dev/null +++ b/core/src/main/java/foundation/stack/datamill/db/TerminalCondition.java @@ -0,0 +1,7 @@ +package foundation.stack.datamill.db; + +/** + * @author Ravi Chodavarapu (rchodava@gmail.com) + */ +public interface TerminalCondition { +} diff --git a/core/src/main/java/foundation/stack/datamill/db/WhereBuilder.java b/core/src/main/java/foundation/stack/datamill/db/WhereBuilder.java index 19e8558..d785ad7 100644 --- a/core/src/main/java/foundation/stack/datamill/db/WhereBuilder.java +++ b/core/src/main/java/foundation/stack/datamill/db/WhereBuilder.java @@ -1,15 +1,14 @@ package foundation.stack.datamill.db; import foundation.stack.datamill.reflection.Outline; +import rx.functions.Func1; /** * @author Ravi Chodavarapu (rchodava@gmail.com) */ public interface WhereBuilder { R all(); - R execute(); - ConditionBuilder where(); - ConditionBuilder and(); + R where(Func1 conditionBuilder); JoinBuilder leftJoin(String table); JoinBuilder leftJoin(Outline outline); } diff --git a/core/src/main/java/foundation/stack/datamill/db/impl/EmptyUpdateSuffixBuilder.java b/core/src/main/java/foundation/stack/datamill/db/impl/EmptyUpdateSuffixBuilder.java new file mode 100644 index 0000000..a46e11b --- /dev/null +++ b/core/src/main/java/foundation/stack/datamill/db/impl/EmptyUpdateSuffixBuilder.java @@ -0,0 +1,34 @@ +package foundation.stack.datamill.db.impl; + +import foundation.stack.datamill.db.InsertSuffixBuilder; +import foundation.stack.datamill.db.RowBuilder; +import foundation.stack.datamill.db.UpdateQueryExecution; +import rx.Observable; + +import java.util.Map; +import java.util.function.Function; + +/** + * @author Ravi Chodavarapu (rchodava@gmail.com) + */ +class EmptyUpdateSuffixBuilder implements InsertSuffixBuilder { + @Override + public Observable count() { + return Observable.just(0); + } + + @Override + public Observable getIds() { + return Observable.empty(); + } + + @Override + public UpdateQueryExecution onDuplicateKeyUpdate(Function> rowConstructor) { + return this; + } + + @Override + public UpdateQueryExecution onDuplicateKeyUpdate(Map values) { + return this; + } +} diff --git a/core/src/main/java/foundation/stack/datamill/db/impl/QueryBuilderImpl.java b/core/src/main/java/foundation/stack/datamill/db/impl/QueryBuilderImpl.java index 29b47ea..ae81f42 100644 --- a/core/src/main/java/foundation/stack/datamill/db/impl/QueryBuilderImpl.java +++ b/core/src/main/java/foundation/stack/datamill/db/impl/QueryBuilderImpl.java @@ -1,22 +1,10 @@ package foundation.stack.datamill.db.impl; import com.google.common.base.Joiner; -import foundation.stack.datamill.db.InsertBuilder; -import foundation.stack.datamill.db.QueryBuilder; -import foundation.stack.datamill.db.SelectBuilder; +import foundation.stack.datamill.db.*; import foundation.stack.datamill.reflection.Member; -import foundation.stack.datamill.db.ConditionBuilder; -import foundation.stack.datamill.db.InsertSuffixBuilder; -import foundation.stack.datamill.db.JoinBuilder; -import foundation.stack.datamill.db.Row; -import foundation.stack.datamill.db.RowBuilder; -import foundation.stack.datamill.db.UpdateBuilder; -import foundation.stack.datamill.db.UpdateQueryExecution; -import foundation.stack.datamill.db.WhereBuilder; import foundation.stack.datamill.reflection.Outline; import foundation.stack.datamill.values.Times; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import rx.Observable; import java.sql.Timestamp; @@ -25,7 +13,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -40,44 +27,20 @@ * @author Ravi Chodavarapu (rchodava@gmail.com) */ public abstract class QueryBuilderImpl implements QueryBuilder { - private static final Logger logger = LoggerFactory.getLogger(QueryBuilderImpl.class); private static final InsertSuffixBuilder EMPTY_UPDATE_BUILDER = new EmptyUpdateSuffixBuilder(); - private static final String SQL_ASSIGNMENT = " = "; - private static final String SQL_DELETE_FROM = "DELETE FROM "; - private static final String SQL_DELETE = "DELETE "; - private static final String SQL_EQ = " = "; - private static final String SQL_FROM = " FROM "; - private static final String SQL_INSERT_INTO = "INSERT INTO "; - private static final String SQL_LEFT_JOIN = " LEFT JOIN "; - private static final String SQL_NULL = "NULL"; - private static final String SQL_ON = " ON "; - private static final String SQL_ON_DUPLICATE_KEY_UPDATE = " ON DUPLICATE KEY UPDATE "; - private static final String SQL_PARAMETER_PLACEHOLDER = "?"; - private static final String SQL_SELECT = "SELECT "; - private static final String SQL_SET = " SET "; - private static final String SQL_WHERE = " WHERE "; - private static final String SQL_UPDATE = "UPDATE "; - private static final String SQL_IS = " IS "; - private static final String SQL_AND = " AND "; - private static final String SQL_IN = " IN "; - - private static final String OPEN_PARENTHESIS = "("; - private static final String CLOSE_PARENTHESIS = ")"; - private static final String COMMA = ","; - private static void appendUpdateAssignments(StringBuilder query, List parameters, Map values) { List setters = new ArrayList<>(values.size()); for (Map.Entry column : values.entrySet()) { StringBuilder setter = new StringBuilder(column.getKey()); - setter.append(SQL_ASSIGNMENT); + setter.append(SqlSyntax.SQL_ASSIGNMENT); Object value = column.getValue(); if (value == null) { - setter.append(SQL_NULL); + setter.append(SqlSyntax.SQL_NULL); } else { - setter.append(SQL_PARAMETER_PLACEHOLDER); + setter.append(SqlSyntax.SQL_PARAMETER_PLACEHOLDER); parameters.add(value); } @@ -92,9 +55,9 @@ private class UpdateQuery implements UpdateBuilder { private final StringBuilder query = new StringBuilder(); public UpdateQuery(String table) { - query.append(SQL_UPDATE); + query.append(SqlSyntax.SQL_UPDATE); query.append(table); - query.append(SQL_SET); + query.append(SqlSyntax.SQL_SET); } @Override @@ -118,7 +81,7 @@ private class InsertQuery implements InsertBuilder { private final StringBuilder query = new StringBuilder(); public InsertQuery(String table) { - query.append(SQL_INSERT_INTO); + query.append(SqlSyntax.SQL_INSERT_INTO); query.append(table); } @@ -168,9 +131,9 @@ public InsertSuffixBuilder values(Map... rows) { for (String column : columns) { Object value = row.get(column); if (value == null) { - values.append(SQL_NULL); + values.append(SqlSyntax.SQL_NULL); } else { - values.append(SQL_PARAMETER_PLACEHOLDER); + values.append(SqlSyntax.SQL_PARAMETER_PLACEHOLDER); if (value instanceof Temporal) { value = new Timestamp(Times.toEpochMillis((Temporal) value)); } @@ -226,14 +189,14 @@ public UpdateQueryExecution onDuplicateKeyUpdate(Function values) { if (!values.isEmpty()) { - query.append(SQL_ON_DUPLICATE_KEY_UPDATE); + query.append(SqlSyntax.SQL_ON_DUPLICATE_KEY_UPDATE); appendUpdateAssignments(query, parameters, values); } return this; } } - private class UpdateWhereClause extends WhereClause { + private class UpdateWhereClause extends WhereBuilderImpl { public UpdateWhereClause(StringBuilder query) { super(query); } @@ -247,39 +210,17 @@ public UpdateQueryExecution all() { return QueryBuilderImpl.this.update(query.toString(), parameters.toArray(new Object[parameters.size()])); } - @Override - public WhereBuilder eq(String column, T value) { - addEqualityClause(column, value); - return this; - } - - @Override - public WhereBuilder is(String column, T value) { - addIsClause(column, value); - return this; - } - - @Override - public WhereBuilder in(String column, Collection values) { - addInClause(column, values); - return this; - } - @Override public UpdateQueryExecution execute() { return QueryBuilderImpl.this.update(query.toString(), parameters.toArray(new Object[parameters.size()])); } } - private class SelectWhereClause extends WhereClause> { + private class SelectWhereClause extends WhereBuilderImpl> { public SelectWhereClause(StringBuilder query) { super(query); } - public SelectWhereClause(StringBuilder query, List parameters) { - super(query, parameters); - } - @Override public Observable all() { if (!parameters.isEmpty()) { @@ -289,185 +230,28 @@ public Observable all() { } } - @Override - public WhereBuilder> eq(String column, T value) { - addEqualityClause(column, value); - return this; - } - - @Override - public WhereBuilder> is(String column, T value) { - addIsClause(column, value); - return this; - } - - @Override - public WhereBuilder> in(String column, Collection values) { - addInClause(column, values); - return this; - } - @Override public Observable execute() { return QueryBuilderImpl.this.query(query.toString(), parameters.toArray(new Object[parameters.size()])); } } - private abstract class WhereClause implements WhereBuilder, ConditionBuilder, JoinBuilder { - protected final StringBuilder query; - protected final List parameters; - - public WhereClause(StringBuilder query) { - this(query, new ArrayList<>()); - } - - public WhereClause(StringBuilder query, List parameters) { - this.query = query; - this.parameters = parameters; - } - - protected void addEqualityClause(String column, T value) { - query.append(column); - query.append(SQL_EQ); - - if (value != null) { - query.append(SQL_PARAMETER_PLACEHOLDER); - parameters.add(value); - } else { - query.append(SQL_NULL); - } - } - - protected void addIsClause(String column, T value) { - query.append(column); - query.append(SQL_IS); - - if (value != null) { - query.append(SQL_PARAMETER_PLACEHOLDER); - parameters.add(value); - } else { - query.append(SQL_NULL); - } - } - - protected void addInClause(String column, Collection values) { - if (!values.isEmpty()) { - query.append(column); - query.append(SQL_IN); - - query.append(OPEN_PARENTHESIS); - Iterator iterator = values.iterator(); - while(iterator.hasNext()) { - query.append(SQL_PARAMETER_PLACEHOLDER); - iterator.next(); - if (iterator.hasNext()) { - query.append(COMMA); - } - } - query.append(CLOSE_PARENTHESIS); - - parameters.addAll(values); - } - } - - @Override - public ConditionBuilder and() { - query.append(SQL_AND); - return this; - } - - @Override - public WhereBuilder eq(Member member, T value) { - return eq(member.outline().pluralName(), member.name(), value); - } - - @Override - public WhereBuilder eq(String table, String column, T value) { - return eq(qualifiedName(table, column), value); - } - - @Override - public WhereBuilder is(String table, String column, T value) { - return is(qualifiedName(table, column), value); - } - - @Override - public WhereBuilder is(Member member, T value) { - return is(member.outline().pluralName(), member.name(), value); - } - - @Override - public WhereBuilder in(String table, String column, Collection values) { - return in(qualifiedName(table, column), values); - } - - @Override - public WhereBuilder in(Member member, Collection values) { - return in(member.outline().pluralName(), member.name(), values); - } - - @Override - public ConditionBuilder where() { - query.append(SQL_WHERE); - return this; - } - - @Override - public JoinBuilder leftJoin(String table) { - query.append(SQL_LEFT_JOIN); - query.append(table); - - return this; - } - - @Override - public JoinBuilder leftJoin(Outline outline) { - return leftJoin(outline.pluralName()); - } - - @Override - public WhereBuilder onEq(String column1, String column2) { - query.append(SQL_ON); - query.append(column1); - query.append(SQL_EQ); - query.append(column2); - - return this; - } - - @Override - public WhereBuilder onEq(String table1, String column1, String table2, String column2) { - return onEq(qualifiedName(table1, column1), qualifiedName(table2, column2)); - } - - @Override - public WhereBuilder onEq(Member member1, Member member2) { - return onEq(member1.outline().pluralName(), member1.name(), - member2.outline().pluralName(), member2.name()); - } - - @Override - public WhereBuilder onEq(String table1, String column1, Member member2) { - return onEq(table1, column1, member2.outline().pluralName(), member2.name()); - } - } - private class SelectQuery implements SelectBuilder { private final StringBuilder query = new StringBuilder(); public SelectQuery() { - query.append(SQL_SELECT); + query.append(SqlSyntax.SQL_SELECT); query.append('*'); } public SelectQuery(Iterable columns) { - query.append(SQL_SELECT); + query.append(SqlSyntax.SQL_SELECT); query.append(Joiner.on(", ").join(columns)); } @Override public WhereBuilder> from(String table) { - query.append(SQL_FROM); + query.append(SqlSyntax.SQL_FROM); query.append(table); return new SelectWhereClause(query); } @@ -480,7 +264,7 @@ public WhereBuilder> from(Outline outline) { @Override public WhereBuilder deleteFrom(String table) { - return new UpdateWhereClause(new StringBuilder(SQL_DELETE_FROM).append(table)); + return new UpdateWhereClause(new StringBuilder(SqlSyntax.SQL_DELETE_FROM).append(table)); } @Override @@ -490,7 +274,7 @@ public WhereBuilder deleteFrom(Outline outline) { @Override public WhereBuilder deleteFromNamed(String table) { - return new UpdateWhereClause(new StringBuilder(SQL_DELETE).append(table).append(SQL_FROM).append(table)); + return new UpdateWhereClause(new StringBuilder(SqlSyntax.SQL_DELETE).append(table).append(SqlSyntax.SQL_FROM).append(table)); } @Override @@ -531,7 +315,7 @@ public SelectBuilder select(String... columns) { public SelectBuilder select(Member... members) { ArrayList columns = new ArrayList<>(); for (Member member : members) { - columns.add(qualifiedName(member.outline().pluralName(), member.name())); + columns.add(SqlSyntax.qualifiedName(member.outline().pluralName(), member.name())); } return select(columns); @@ -542,20 +326,16 @@ public SelectBuilder select(Iterable columns) { return new SelectQuery(columns); } - private static String qualifiedName(String table, String column) { - return table + "." + column; - } - @Override public SelectBuilder selectQualified(String table, String column) { - return select(qualifiedName(table, column)); + return select(SqlSyntax.qualifiedName(table, column)); } @Override public SelectBuilder selectQualified(String table, String... columns) { return select( Stream.of(columns) - .map(column -> qualifiedName(table, column)) + .map(column -> SqlSyntax.qualifiedName(table, column)) .collect(Collectors.toList())); } @@ -563,7 +343,7 @@ public SelectBuilder selectQualified(String table, String... columns) { public SelectBuilder selectQualified(String table, Iterable columns) { return select( StreamSupport.stream(columns.spliterator(), false) - .map(column -> qualifiedName(table, column)) + .map(column -> SqlSyntax.qualifiedName(table, column)) .collect(Collectors.toList())); } @@ -587,25 +367,4 @@ public UpdateBuilder update(Outline outline) { return update(outline.pluralName()); } - private static class EmptyUpdateSuffixBuilder implements InsertSuffixBuilder { - @Override - public Observable count() { - return Observable.just(0); - } - - @Override - public Observable getIds() { - return Observable.empty(); - } - - @Override - public UpdateQueryExecution onDuplicateKeyUpdate(Function> rowConstructor) { - return this; - } - - @Override - public UpdateQueryExecution onDuplicateKeyUpdate(Map values) { - return this; - } - } } diff --git a/core/src/main/java/foundation/stack/datamill/db/impl/SqlSyntax.java b/core/src/main/java/foundation/stack/datamill/db/impl/SqlSyntax.java new file mode 100644 index 0000000..1718968 --- /dev/null +++ b/core/src/main/java/foundation/stack/datamill/db/impl/SqlSyntax.java @@ -0,0 +1,35 @@ +package foundation.stack.datamill.db.impl; + +/** + * @author Ravi Chodavarapu (rchodava@gmail.com) + */ +public interface SqlSyntax { + String SQL_ASSIGNMENT = " = "; + String SQL_DELETE_FROM = "DELETE FROM "; + String SQL_DELETE = "DELETE "; + String SQL_EQ = " = "; + String SQL_FROM = " FROM "; + String SQL_GREATER_THAN = " > "; + String SQL_INSERT_INTO = "INSERT INTO "; + String SQL_LEFT_JOIN = " LEFT JOIN "; + String SQL_LESS_THAN = " < "; + String SQL_NULL = "NULL"; + String SQL_ON = " ON "; + String SQL_ON_DUPLICATE_KEY_UPDATE = " ON DUPLICATE KEY UPDATE "; + String SQL_PARAMETER_PLACEHOLDER = "?"; + String SQL_SELECT = "SELECT "; + String SQL_SET = " SET "; + String SQL_WHERE = " WHERE "; + String SQL_UPDATE = "UPDATE "; + String SQL_IS = " IS "; + String SQL_AND = " AND "; + String SQL_IN = " IN "; + String SQL_OR = " OR "; + String OPEN_PARENTHESIS = "("; + String CLOSE_PARENTHESIS = ")"; + String COMMA = ","; + + static String qualifiedName(String table, String column) { + return table + "." + column; + } +} diff --git a/core/src/main/java/foundation/stack/datamill/db/impl/WhereBuilderImpl.java b/core/src/main/java/foundation/stack/datamill/db/impl/WhereBuilderImpl.java new file mode 100644 index 0000000..4b28948 --- /dev/null +++ b/core/src/main/java/foundation/stack/datamill/db/impl/WhereBuilderImpl.java @@ -0,0 +1,228 @@ +package foundation.stack.datamill.db.impl; + +import foundation.stack.datamill.db.*; +import foundation.stack.datamill.reflection.Member; +import foundation.stack.datamill.reflection.Outline; +import rx.functions.Func1; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +/** + * @author Ravi Chodavarapu (rchodava@gmail.com) + */ +abstract class WhereBuilderImpl implements WhereBuilder, ConditionBuilder, ConjunctionBuilder, JoinBuilder { + protected final StringBuilder query; + protected final List parameters; + + public WhereBuilderImpl(StringBuilder query) { + this(query, new ArrayList<>()); + } + + public WhereBuilderImpl(StringBuilder query, List parameters) { + this.query = query; + this.parameters = parameters; + } + + protected ConjunctionBuilder addBinaryClause(String operator, String column, T value) { + query.append(column); + query.append(operator); + + if (value != null) { + query.append(SqlSyntax.SQL_PARAMETER_PLACEHOLDER); + parameters.add(value); + } else { + query.append(SqlSyntax.SQL_NULL); + } + + return this; + } + + protected void addInClause(String column, Collection values) { + if (!values.isEmpty()) { + query.append(column); + query.append(SqlSyntax.SQL_IN); + + query.append(SqlSyntax.OPEN_PARENTHESIS); + Iterator iterator = values.iterator(); + while (iterator.hasNext()) { + query.append(SqlSyntax.SQL_PARAMETER_PLACEHOLDER); + iterator.next(); + if (iterator.hasNext()) { + query.append(SqlSyntax.COMMA); + } + } + query.append(SqlSyntax.CLOSE_PARENTHESIS); + + parameters.addAll(values); + } + } + + @Override + public TerminalCondition and( + Func1 left, + Func1 right) { + query.append(SqlSyntax.OPEN_PARENTHESIS); + left.call(this); + query.append(SqlSyntax.CLOSE_PARENTHESIS); + query.append(SqlSyntax.SQL_AND); + query.append(SqlSyntax.OPEN_PARENTHESIS); + right.call(this); + query.append(SqlSyntax.CLOSE_PARENTHESIS); + + return this; + } + + @Override + public ConditionBuilder and() { + query.append(SqlSyntax.SQL_AND); + return this; + } + + @Override + public TerminalCondition or( + Func1 left, + Func1 right) { + query.append(SqlSyntax.OPEN_PARENTHESIS); + left.call(this); + query.append(SqlSyntax.CLOSE_PARENTHESIS); + query.append(SqlSyntax.SQL_OR); + query.append(SqlSyntax.OPEN_PARENTHESIS); + right.call(this); + query.append(SqlSyntax.CLOSE_PARENTHESIS); + + return this; + } + + @Override + public ConditionBuilder or() { + query.append(SqlSyntax.SQL_OR); + return this; + } + + @Override + public ConjunctionBuilder eq(String column, T value) { + return addBinaryClause(SqlSyntax.SQL_EQ, column, value); + } + + @Override + public ConjunctionBuilder eq(Member member, T value) { + return eq(member.outline().pluralName(), member.name(), value); + } + + @Override + public ConjunctionBuilder eq(String table, String column, T value) { + return eq(SqlSyntax.qualifiedName(table, column), value); + } + + @Override + public ConjunctionBuilder gt(String column, T value) { + return addBinaryClause(SqlSyntax.SQL_GREATER_THAN, column, value); + } + + @Override + public ConjunctionBuilder gt(Member member, T value) { + return gt(member.outline().pluralName(), member.name(), value); + } + + @Override + public ConjunctionBuilder gt(String table, String column, T value) { + return gt(SqlSyntax.qualifiedName(table, column), value); + } + + @Override + public ConjunctionBuilder is(String column, T value) { + return addBinaryClause(SqlSyntax.SQL_IS, column, value); + } + + @Override + public ConjunctionBuilder is(String table, String column, T value) { + return is(SqlSyntax.qualifiedName(table, column), value); + } + + @Override + public ConjunctionBuilder is(Member member, T value) { + return is(member.outline().pluralName(), member.name(), value); + } + + @Override + public ConjunctionBuilder in(String table, String column, Collection values) { + return in(SqlSyntax.qualifiedName(table, column), values); + } + + @Override + public ConjunctionBuilder in(Member member, Collection values) { + return in(member.outline().pluralName(), member.name(), values); + } + + @Override + public ConjunctionBuilder in(String column, Collection values) { + addInClause(column, values); + return this; + } + + @Override + public ConjunctionBuilder lt(String column, T value) { + return addBinaryClause(SqlSyntax.SQL_LESS_THAN, column, value); + } + + @Override + public ConjunctionBuilder lt(Member member, T value) { + return lt(member.outline().pluralName(), member.name(), value); + } + + @Override + public ConjunctionBuilder lt(String table, String column, T value) { + return lt(SqlSyntax.qualifiedName(table, column), value); + } + + protected abstract R execute(); + + @Override + public R where(Func1 conditionBuilder) { + query.append(SqlSyntax.SQL_WHERE); + conditionBuilder.call(this); + return execute(); + } + + @Override + public JoinBuilder leftJoin(String table) { + query.append(SqlSyntax.SQL_LEFT_JOIN); + query.append(table); + + return this; + } + + @Override + public JoinBuilder leftJoin(Outline outline) { + return leftJoin(outline.pluralName()); + } + + @Override + public WhereBuilder onEq(String column1, String column2) { + query.append(SqlSyntax.SQL_ON); + query.append(column1); + query.append(SqlSyntax.SQL_EQ); + query.append(column2); + + return this; + } + + @Override + public WhereBuilder onEq(String table1, String column1, String table2, String column2) { + return onEq(SqlSyntax.qualifiedName(table1, column1), SqlSyntax.qualifiedName(table2, column2)); + } + + @Override + public WhereBuilder onEq(Member member1, Member member2) { + return onEq(member1.outline().pluralName(), member1.name(), + member2.outline().pluralName(), member2.name()); + } + + @Override + public WhereBuilder onEq(String table1, String column1, Member member2) { + return onEq(table1, column1, member2.outline().pluralName(), member2.name()); + } +} diff --git a/core/src/test/java/foundation/stack/datamill/db/impl/QueryBuilderImplTest.java b/core/src/test/java/foundation/stack/datamill/db/impl/QueryBuilderImplTest.java index e67c3bf..f7dcae5 100644 --- a/core/src/test/java/foundation/stack/datamill/db/impl/QueryBuilderImplTest.java +++ b/core/src/test/java/foundation/stack/datamill/db/impl/QueryBuilderImplTest.java @@ -113,27 +113,27 @@ public void selectQueries() { assertEquals("SELECT * FROM query_test_beans", queryBuilder.getLastQuery()); assertFalse(queryBuilder.getLastWasUpdate()); - queryBuilder.selectAll().from("table_name").where().eq("int_column", 2).execute(); + queryBuilder.selectAll().from("table_name").where(c -> c.eq("int_column", 2)); assertEquals("SELECT * FROM table_name WHERE int_column = ?", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { 2 }, queryBuilder.getLastParameters()); assertFalse(queryBuilder.getLastWasUpdate()); - queryBuilder.selectAll().from("table_name").where().eq(outline.member(m -> m.getId()), 2).execute(); + queryBuilder.selectAll().from("table_name").where(c -> c.eq(outline.member(m -> m.getId()), 2)); assertEquals("SELECT * FROM table_name WHERE query_test_beans.id = ?", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { 2 }, queryBuilder.getLastParameters()); assertFalse(queryBuilder.getLastWasUpdate()); - queryBuilder.selectAll().from("table_name").where().eq("table_name", "int_column", 2).execute(); + queryBuilder.selectAll().from("table_name").where(c -> c.eq("table_name", "int_column", 2)); assertEquals("SELECT * FROM table_name WHERE table_name.int_column = ?", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { 2 }, queryBuilder.getLastParameters()); assertFalse(queryBuilder.getLastWasUpdate()); - queryBuilder.selectAll().from("table_name").where().eq("boolean_column", true).execute(); + queryBuilder.selectAll().from("table_name").where(c -> c.eq("boolean_column", true)); assertEquals("SELECT * FROM table_name WHERE boolean_column = ?", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { true }, queryBuilder.getLastParameters()); assertFalse(queryBuilder.getLastWasUpdate()); - queryBuilder.selectAll().from("table_name").where().eq("string_column", "value").execute(); + queryBuilder.selectAll().from("table_name").where(c -> c.eq("string_column", "value")); assertEquals("SELECT * FROM table_name WHERE string_column = ?", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { "value" }, queryBuilder.getLastParameters()); assertFalse(queryBuilder.getLastWasUpdate()); @@ -170,42 +170,86 @@ public void selectQueries() { assertEquals("SELECT table_name.column_name, table_name.second_column FROM table_name", queryBuilder.getLastQuery()); assertFalse(queryBuilder.getLastWasUpdate()); - queryBuilder.select("column_name", "second_column").from("table_name").where().eq("int_column", 2).execute(); + queryBuilder.select("column_name", "second_column").from("table_name").where(c -> c.eq("int_column", 2)); assertEquals("SELECT column_name, second_column FROM table_name WHERE int_column = ?", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { 2 }, queryBuilder.getLastParameters()); assertFalse(queryBuilder.getLastWasUpdate()); - queryBuilder.select("column_name", "second_column").from("table_name").where().eq("int_column", 2).and().eq("boolean_column", true).execute(); + queryBuilder.select("column_name", "second_column").from("table_name").where(c -> c.eq("int_column", 2).and().eq("boolean_column", true)); assertEquals("SELECT column_name, second_column FROM table_name WHERE int_column = ? AND boolean_column = ?", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { 2, true }, queryBuilder.getLastParameters()); assertFalse(queryBuilder.getLastWasUpdate()); - queryBuilder.select("column_name", "second_column").from("table_name").where().eq("int_column", 2).and().eq("boolean_column", null).execute(); + queryBuilder.select("column_name", "second_column").from("table_name").where(c -> c.eq("int_column", 2).or().eq("boolean_column", true)); + assertEquals("SELECT column_name, second_column FROM table_name WHERE int_column = ? OR boolean_column = ?", queryBuilder.getLastQuery()); + assertArrayEquals(new Object[] { 2, true }, queryBuilder.getLastParameters()); + assertFalse(queryBuilder.getLastWasUpdate()); + + queryBuilder.select("column_name", "second_column").from("table_name").where(c -> c.eq("int_column", 2).and().eq("boolean_column", null)); assertEquals("SELECT column_name, second_column FROM table_name WHERE int_column = ? AND boolean_column = NULL", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { 2 }, queryBuilder.getLastParameters()); assertFalse(queryBuilder.getLastWasUpdate()); - queryBuilder.select("column_name", "second_column").from("table_name").where().eq("int_column", null).execute(); + queryBuilder.select("column_name", "second_column") + .from("table_name") + .where(c -> c.eq("int_column", 2) + .or().eq("boolean_column", true) + .and().eq("boolean_column2", null)); + assertEquals("SELECT column_name, second_column FROM table_name WHERE int_column = ? OR boolean_column = ? AND boolean_column2 = NULL", queryBuilder.getLastQuery()); + assertArrayEquals(new Object[] { 2, true }, queryBuilder.getLastParameters()); + assertFalse(queryBuilder.getLastWasUpdate()); + + queryBuilder.select("column_name", "second_column") + .from("table_name") + .where(c -> c.and(l -> l.eq("int_column", 2), r -> r.eq("boolean_column", true))); + assertEquals("SELECT column_name, second_column FROM table_name WHERE (int_column = ?) AND (boolean_column = ?)", queryBuilder.getLastQuery()); + assertArrayEquals(new Object[] { 2, true }, queryBuilder.getLastParameters()); + assertFalse(queryBuilder.getLastWasUpdate()); + + queryBuilder.select("column_name", "second_column") + .from("table_name") + .where(c -> c.or(l -> l.eq("int_column", 2), r -> r.eq("boolean_column", true))); + assertEquals("SELECT column_name, second_column FROM table_name WHERE (int_column = ?) OR (boolean_column = ?)", queryBuilder.getLastQuery()); + assertArrayEquals(new Object[] { 2, true }, queryBuilder.getLastParameters()); + assertFalse(queryBuilder.getLastWasUpdate()); + + queryBuilder.select("column_name", "second_column") + .from("table_name") + .where(c -> c.or(l -> l.gt("int_column", 2).and().lt("int_column2", 5), r -> r.eq("boolean_column", true))); + assertEquals("SELECT column_name, second_column FROM table_name WHERE (int_column > ? AND int_column2 < ?) OR (boolean_column = ?)", queryBuilder.getLastQuery()); + assertArrayEquals(new Object[] { 2, 5, true }, queryBuilder.getLastParameters()); + assertFalse(queryBuilder.getLastWasUpdate()); + + queryBuilder.select("column_name", "second_column") + .from("table_name") + .where(c -> c.or( + l -> l.gt("table_name", "int_column", 2).and().lt("table_name", "int_column2", 5), + r -> r.eq("boolean_column", true))); + assertEquals("SELECT column_name, second_column FROM table_name WHERE (table_name.int_column > ? AND table_name.int_column2 < ?) OR (boolean_column = ?)", queryBuilder.getLastQuery()); + assertArrayEquals(new Object[] { 2, 5, true }, queryBuilder.getLastParameters()); + assertFalse(queryBuilder.getLastWasUpdate()); + + queryBuilder.select("column_name", "second_column").from("table_name").where(c -> c.eq("int_column", null)); assertEquals("SELECT column_name, second_column FROM table_name WHERE int_column = NULL", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { }, queryBuilder.getLastParameters()); assertFalse(queryBuilder.getLastWasUpdate()); - queryBuilder.select("column_name", "second_column").from("table_name").where().is("int_column", null).execute(); + queryBuilder.select("column_name", "second_column").from("table_name").where(c -> c.is("int_column", null)); assertEquals("SELECT column_name, second_column FROM table_name WHERE int_column IS NULL", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { }, queryBuilder.getLastParameters()); assertFalse(queryBuilder.getLastWasUpdate()); - queryBuilder.select("column_name", "second_column").from("table_name").where().eq("table_name", "int_column", 2).execute(); + queryBuilder.select("column_name", "second_column").from("table_name").where(c -> c.eq("table_name", "int_column", 2)); assertEquals("SELECT column_name, second_column FROM table_name WHERE table_name.int_column = ?", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { 2 }, queryBuilder.getLastParameters()); assertFalse(queryBuilder.getLastWasUpdate()); - queryBuilder.select("column_name", "second_column").from("table_name").where().in("table_name", "int_column", Arrays.asList(1, 2)).execute(); + queryBuilder.select("column_name", "second_column").from("table_name").where(c -> c.in("table_name", "int_column", Arrays.asList(1, 2))); assertEquals("SELECT column_name, second_column FROM table_name WHERE table_name.int_column IN (?,?)", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { 1, 2 }, queryBuilder.getLastParameters()); assertFalse(queryBuilder.getLastWasUpdate()); - queryBuilder.select("column_name", "second_column").from("table_name").where().in("table_name", "int_column", Collections.singletonList(1)).execute(); + queryBuilder.select("column_name", "second_column").from("table_name").where(c -> c.in("table_name", "int_column", Collections.singletonList(1))); assertEquals("SELECT column_name, second_column FROM table_name WHERE table_name.int_column IN (?)", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { 1 }, queryBuilder.getLastParameters()); assertFalse(queryBuilder.getLastWasUpdate()); @@ -221,7 +265,7 @@ public void selectQueries() { assertFalse(queryBuilder.getLastWasUpdate()); queryBuilder.select(Arrays.asList("column_name", "second_column")).from("table_name") - .leftJoin("second_table").onEq("second_column", "third_column").where().eq("column_name", 2).execute(); + .leftJoin("second_table").onEq("second_column", "third_column").where(c -> c.eq("column_name", 2)); assertEquals("SELECT column_name, second_column FROM table_name LEFT JOIN second_table ON second_column = third_column WHERE column_name = ?", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { 2 }, queryBuilder.getLastParameters()); assertFalse(queryBuilder.getLastWasUpdate()); @@ -245,33 +289,33 @@ public void deleteQueries() { assertEquals("DELETE FROM table_name", queryBuilder.getLastQuery()); assertTrue(queryBuilder.getLastWasUpdate()); - queryBuilder.deleteFrom("table_name").where().eq("int_column", 2).execute(); + queryBuilder.deleteFrom("table_name").where(c ->c.eq("int_column", 2)); assertEquals("DELETE FROM table_name WHERE int_column = ?", queryBuilder.getLastQuery()); assertArrayEquals(new Object[]{2}, queryBuilder.getLastParameters()); assertTrue(queryBuilder.getLastWasUpdate()); - queryBuilder.deleteFrom("table_name").where().eq("boolean_column", true).execute(); + queryBuilder.deleteFrom("table_name").where(c -> c.eq("boolean_column", true)); assertEquals("DELETE FROM table_name WHERE boolean_column = ?", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { true }, queryBuilder.getLastParameters()); assertTrue(queryBuilder.getLastWasUpdate()); - queryBuilder.deleteFrom("table_name").where().eq("string_column", "value").execute(); + queryBuilder.deleteFrom("table_name").where(c -> c.eq("string_column", "value")); assertEquals("DELETE FROM table_name WHERE string_column = ?", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { "value" }, queryBuilder.getLastParameters()); assertTrue(queryBuilder.getLastWasUpdate()); queryBuilder.deleteFromNamed("table_name") - .where().eq("string_column", "value").execute(); + .where(c -> c.eq("string_column", "value")); assertEquals("DELETE table_name FROM table_name WHERE string_column = ?", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { "value" }, queryBuilder.getLastParameters()); assertTrue(queryBuilder.getLastWasUpdate()); - queryBuilder.deleteFrom("table_name").where().in("string_column", Arrays.asList(1, 2)).execute(); + queryBuilder.deleteFrom("table_name").where(c -> c.in("string_column", Arrays.asList(1, 2))); assertEquals("DELETE FROM table_name WHERE string_column IN (?,?)", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { 1, 2 }, queryBuilder.getLastParameters()); assertTrue(queryBuilder.getLastWasUpdate()); - queryBuilder.deleteFrom("table_name").where().in("string_column", Collections.singletonList(1)).execute(); + queryBuilder.deleteFrom("table_name").where(c -> c.in("string_column", Collections.singletonList(1))); assertEquals("DELETE FROM table_name WHERE string_column IN (?)", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { 1 }, queryBuilder.getLastParameters()); assertTrue(queryBuilder.getLastWasUpdate()); @@ -381,27 +425,27 @@ public void updateQueries() { assertArrayEquals(new Object[] { 2 }, queryBuilder.getLastParameters()); assertTrue(queryBuilder.getLastWasUpdate()); - queryBuilder.update("table_name").set(ImmutableMap.of("int_column", 1)).where().eq("int_column", 2).execute(); + queryBuilder.update("table_name").set(ImmutableMap.of("int_column", 1)).where(c -> c.eq("int_column", 2)); assertEquals("UPDATE table_name SET int_column = ? WHERE int_column = ?", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { 1, 2 }, queryBuilder.getLastParameters()); assertTrue(queryBuilder.getLastWasUpdate()); - queryBuilder.update("table_name").set(ImmutableMap.of("int_column", 1)).where().in("int_column", Arrays.asList(2, 3)).execute(); + queryBuilder.update("table_name").set(ImmutableMap.of("int_column", 1)).where(c -> c.in("int_column", Arrays.asList(2, 3))); assertEquals("UPDATE table_name SET int_column = ? WHERE int_column IN (?,?)", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { 1, 2, 3 }, queryBuilder.getLastParameters()); assertTrue(queryBuilder.getLastWasUpdate()); - queryBuilder.update("table_name").set(ImmutableMap.of("int_column", 1)).where().in("int_column", Collections.singletonList(2)).execute(); + queryBuilder.update("table_name").set(ImmutableMap.of("int_column", 1)).where(c -> c.in("int_column", Collections.singletonList(2))); assertEquals("UPDATE table_name SET int_column = ? WHERE int_column IN (?)", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { 1, 2 }, queryBuilder.getLastParameters()); assertTrue(queryBuilder.getLastWasUpdate()); - queryBuilder.update("table_name").set(ImmutableMap.of("int_column", 1)).where().eq("boolean_column", true).execute(); + queryBuilder.update("table_name").set(ImmutableMap.of("int_column", 1)).where(c -> c.eq("boolean_column", true)); assertEquals("UPDATE table_name SET int_column = ? WHERE boolean_column = ?", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { 1, true }, queryBuilder.getLastParameters()); assertTrue(queryBuilder.getLastWasUpdate()); - queryBuilder.update("table_name").set(ImmutableMap.of("int_column", 1)).where().eq("string_column", "value").execute(); + queryBuilder.update("table_name").set(ImmutableMap.of("int_column", 1)).where(c -> c.eq("string_column", "value")); assertEquals("UPDATE table_name SET int_column = ? WHERE string_column = ?", queryBuilder.getLastQuery()); assertArrayEquals(new Object[] { 1, "value" }, queryBuilder.getLastParameters()); assertTrue(queryBuilder.getLastWasUpdate());