diff --git a/pom.xml b/pom.xml index 2220303342..8a72b2eade 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-relational-parent - 1.1.0.BUILD-SNAPSHOT + 1.1.0.DATAJDBC-410-SNAPSHOT pom Spring Data Relational Parent diff --git a/spring-data-jdbc-distribution/pom.xml b/spring-data-jdbc-distribution/pom.xml index 71b9a3c782..83b19c7d2a 100644 --- a/spring-data-jdbc-distribution/pom.xml +++ b/spring-data-jdbc-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-relational-parent - 1.1.0.BUILD-SNAPSHOT + 1.1.0.DATAJDBC-410-SNAPSHOT ../pom.xml diff --git a/spring-data-jdbc/pom.xml b/spring-data-jdbc/pom.xml index d33566bc88..5d6588cadf 100644 --- a/spring-data-jdbc/pom.xml +++ b/spring-data-jdbc/pom.xml @@ -5,7 +5,7 @@ 4.0.0 spring-data-jdbc - 1.1.0.BUILD-SNAPSHOT + 1.1.0.DATAJDBC-410-SNAPSHOT Spring Data JDBC Spring Data module for JDBC repositories. @@ -14,7 +14,7 @@ org.springframework.data spring-data-relational-parent - 1.1.0.BUILD-SNAPSHOT + 1.1.0.DATAJDBC-410-SNAPSHOT diff --git a/spring-data-relational/pom.xml b/spring-data-relational/pom.xml index fa7f2f4873..3f6279909c 100644 --- a/spring-data-relational/pom.xml +++ b/spring-data-relational/pom.xml @@ -5,7 +5,7 @@ 4.0.0 spring-data-relational - 1.1.0.BUILD-SNAPSHOT + 1.1.0.DATAJDBC-410-SNAPSHOT Spring Data Relational Spring Data Relational support @@ -13,7 +13,7 @@ org.springframework.data spring-data-relational-parent - 1.1.0.BUILD-SNAPSHOT + 1.1.0.DATAJDBC-410-SNAPSHOT diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java index 97e71b2a6d..aba998fa2f 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java @@ -193,6 +193,26 @@ public In in(Select subselect) { return Conditions.in(this, subselect); } + /** + * Creates a new {@code not} {@link In} {@link Condition} given right {@link Expression}s. + * + * @param expression right side of the comparison. + * @return the {@link In} condition. + */ + public In notIn(Expression... expression) { + return Conditions.notIn(this, expression); + } + + /** + * Creates a new {@code not} {@link In} {@link Condition} given a subselects. + * + * @param subselect right side of the comparison. + * @return the {@link In} condition. + */ + public In notIn(Select subselect) { + return Conditions.notIn(this, subselect); + } + /** * Creates a {@code IS NULL} condition. * diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Conditions.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Conditions.java index ea71b0c63c..d20055b386 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Conditions.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Conditions.java @@ -192,6 +192,66 @@ public static In in(Column column, Select subselect) { return in(column, new SubselectExpression(subselect)); } + /** + * Creates a {@code NOT IN} {@link Condition clause}. + * + * @param columnOrExpression left side of the comparison. + * @param arg IN argument. + * @return the {@link In} condition. + */ + public static In notIn(Expression columnOrExpression, Expression arg) { + + Assert.notNull(columnOrExpression, "Comparison column or expression must not be null"); + Assert.notNull(arg, "Expression argument must not be null"); + + return In.create(columnOrExpression, arg); + } + + /** + * Creates a new {@code NOT IN} {@link Condition} given left and right {@link Expression}s. + * + * @param columnOrExpression left hand side of the {@link Condition} must not be {@literal null}. + * @param expressions right hand side (collection {@link Expression}) must not be {@literal null}. + * @return the {@link In} {@link Condition}. + */ + public static Condition notIn(Expression columnOrExpression, Collection expressions) { + + Assert.notNull(columnOrExpression, "Comparison column or expression must not be null"); + Assert.notNull(expressions, "Expression argument must not be null"); + + return In.createNotIn(columnOrExpression, new ArrayList<>(expressions)); + } + + /** + * Creates a new {@code NOT IN} {@link Condition} given left and right {@link Expression}s. + * + * @param columnOrExpression left hand side of the {@link Condition} must not be {@literal null}. + * @param expressions right hand side (collection {@link Expression}) must not be {@literal null}. + * @return the {@link In} {@link Condition}. + */ + public static In notIn(Expression columnOrExpression, Expression... expressions) { + + Assert.notNull(columnOrExpression, "Comparison column or expression must not be null"); + Assert.notNull(expressions, "Expression argument must not be null"); + + return In.createNotIn(columnOrExpression, Arrays.asList(expressions)); + } + + /** + * Creates a {@code NOT IN} {@link Condition clause} for a {@link Select subselect}. + * + * @param column the column to compare. + * @param subselect the subselect. + * @return the {@link In} condition. + */ + public static In notIn(Column column, Select subselect) { + + Assert.notNull(column, "Column must not be null"); + Assert.notNull(subselect, "Subselect must not be null"); + + return notIn(column, new SubselectExpression(subselect)); + } + static class ConstantCondition extends AbstractSegment implements Condition { private final String condition; diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/In.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/In.java index 16ff59be10..a590a9acb9 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/In.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/In.java @@ -34,13 +34,15 @@ public class In extends AbstractSegment implements Condition { private final Expression left; private final Collection expressions; + private final boolean notIn; - private In(Expression left, Collection expressions) { + private In(Expression left, Collection expressions, boolean notIn) { super(toArray(left, expressions)); this.left = left; this.expressions = expressions; + this.notIn = notIn; } private static Segment[] toArray(Expression expression, Collection expressions) { @@ -69,7 +71,7 @@ public static In create(Expression columnOrExpression, Expression arg) { Assert.notNull(columnOrExpression, "Comparison column or expression must not be null"); Assert.notNull(arg, "Expression argument must not be null"); - return new In(columnOrExpression, Collections.singletonList(arg)); + return new In(columnOrExpression, Collections.singletonList(arg), false); } /** @@ -84,7 +86,7 @@ public static In create(Expression columnOrExpression, Collection(expressions)); + return new In(columnOrExpression, new ArrayList<>(expressions), false); } /** @@ -99,7 +101,58 @@ public static In create(Expression columnOrExpression, Expression... expressions Assert.notNull(columnOrExpression, "Comparison column or expression must not be null"); Assert.notNull(expressions, "Expression argument must not be null"); - return new In(columnOrExpression, Arrays.asList(expressions)); + return new In(columnOrExpression, Arrays.asList(expressions), false); + } + + /** + * Creates a new {@link In} {@link Condition} given left and right {@link Expression}s. + * + * @param columnOrExpression left hand side of the {@link Condition} must not be {@literal null}. + * @param arg right hand side (collection {@link Expression}) must not be {@literal null}. + * @return the {@link In} {@link Condition}. + */ + public static In createNotIn(Expression columnOrExpression, Expression arg) { + + Assert.notNull(columnOrExpression, "Comparison column or expression must not be null"); + Assert.notNull(arg, "Expression argument must not be null"); + + return new In(columnOrExpression, Collections.singletonList(arg), true); + } + + /** + * Creates a new {@link In} {@link Condition} given left and right {@link Expression}s. + * + * @param columnOrExpression left hand side of the {@link Condition} must not be {@literal null}. + * @param expressions right hand side (collection {@link Expression}) must not be {@literal null}. + * @return the {@link In} {@link Condition}. + */ + public static In createNotIn(Expression columnOrExpression, Collection expressions) { + + Assert.notNull(columnOrExpression, "Comparison column or expression must not be null"); + Assert.notNull(expressions, "Expression argument must not be null"); + + return new In(columnOrExpression, new ArrayList<>(expressions), true); + } + + /** + * Creates a new {@link In} {@link Condition} given left and right {@link Expression}s. + * + * @param columnOrExpression left hand side of the {@link Condition} must not be {@literal null}. + * @param expressions right hand side (collection {@link Expression}) must not be {@literal null}. + * @return the {@link In} {@link Condition}. + */ + public static In createNotIn(Expression columnOrExpression, Expression... expressions) { + + Assert.notNull(columnOrExpression, "Comparison column or expression must not be null"); + Assert.notNull(expressions, "Expression argument must not be null"); + + return new In(columnOrExpression, Arrays.asList(expressions), true); + } + + @Override + public Condition not() { + + return new In(left, expressions, !notIn); } /* @@ -108,6 +161,10 @@ public static In create(Expression columnOrExpression, Expression... expressions */ @Override public String toString() { - return left + " IN (" + StringUtils.collectionToDelimitedString(expressions, ", ") + ")"; + return left + (notIn ? " NOT" : "") + " IN (" + StringUtils.collectionToDelimitedString(expressions, ", ") + ")"; + } + + public boolean isNotIn() { + return notIn; } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/InVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/InVisitor.java index eee1af8f81..2d47b1913b 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/InVisitor.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/InVisitor.java @@ -30,6 +30,7 @@ class InVisitor extends TypedSingleConditionRenderSupport { private final RenderTarget target; private final StringBuilder part = new StringBuilder(); private boolean needsComma = false; + private boolean notIn = false; InVisitor(RenderContext context, RenderTarget target) { super(context); @@ -52,6 +53,9 @@ Delegation leaveNested(Visitable segment) { if (part.length() == 0) { part.append(renderedPart); + if (notIn) { + part.append(" NOT"); + } part.append(" IN ("); } else { part.append(renderedPart); @@ -62,6 +66,14 @@ Delegation leaveNested(Visitable segment) { return super.leaveNested(segment); } + @Override + Delegation enterMatched(In segment) { + + notIn = segment.isNotIn(); + + return super.enterMatched(segment); + } + /* * (non-Javadoc) * @see org.springframework.data.relational.core.sql.render.TypedSubtreeVisitor#leaveMatched(org.springframework.data.relational.core.sql.Visitable) diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/ConditionRendererUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/ConditionRendererUnitTests.java index b8c9b6e6a1..6f9c65101e 100644 --- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/ConditionRendererUnitTests.java +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/ConditionRendererUnitTests.java @@ -126,4 +126,16 @@ public void shouldRenderIsNotNull() { assertThat(sql).endsWith("WHERE my_table.left IS NOT NULL"); } + + @Test // DATAJDBC-410 + public void shouldRenderNotIn() { + + String sql = SqlRenderer.toString(StatementBuilder.select(left).from(table).where(left.in(right).not()).build()); + + assertThat(sql).endsWith("WHERE my_table.left NOT IN (my_table.right)"); + + sql = SqlRenderer.toString(StatementBuilder.select(left).from(table).where(left.notIn(right)).build()); + + assertThat(sql).endsWith("WHERE my_table.left NOT IN (my_table.right)"); + } }