DATAJDBC-604 - Support empty IN lists.
We now support empty IN lists by rendering a condition that evaluates to FALSE using 1 = 0. For NOT IN, we render a condition that evaluates to TRUE using 1 = 1.
This commit is contained in:
@@ -18,7 +18,6 @@ package org.springframework.data.jdbc.repository;
|
||||
import static java.util.Arrays.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.assertj.core.api.SoftAssertions.*;
|
||||
import static org.springframework.data.jdbc.testing.TestDatabaseFeatures.Feature.*;
|
||||
import static org.springframework.test.context.TestExecutionListeners.MergeMode.*;
|
||||
|
||||
import lombok.Data;
|
||||
@@ -44,7 +43,6 @@ import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.jdbc.repository.query.Query;
|
||||
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
|
||||
import org.springframework.data.jdbc.testing.AssumeFeatureRule;
|
||||
import org.springframework.data.jdbc.testing.EnabledOnFeature;
|
||||
import org.springframework.data.jdbc.testing.TestConfiguration;
|
||||
import org.springframework.data.relational.core.mapping.event.AbstractRelationalEvent;
|
||||
import org.springframework.data.relational.core.mapping.event.AfterLoadEvent;
|
||||
@@ -339,6 +337,38 @@ public class JdbcRepositoryIntegrationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-604
|
||||
public void existsInWorksAsExpected() {
|
||||
|
||||
DummyEntity dummy = repository.save(createDummyEntity());
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
softly.assertThat(repository.existsByNameIn(dummy.getName())) //
|
||||
.describedAs("Positive") //
|
||||
.isTrue();
|
||||
softly.assertThat(repository.existsByNameIn()) //
|
||||
.describedAs("Negative") //
|
||||
.isFalse();
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-604
|
||||
public void existsNotInWorksAsExpected() {
|
||||
|
||||
DummyEntity dummy = repository.save(createDummyEntity());
|
||||
|
||||
assertSoftly(softly -> {
|
||||
|
||||
softly.assertThat(repository.existsByNameNotIn(dummy.getName())) //
|
||||
.describedAs("Positive") //
|
||||
.isFalse();
|
||||
softly.assertThat(repository.existsByNameNotIn()) //
|
||||
.describedAs("Negative") //
|
||||
.isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-534
|
||||
public void countByQueryDerivation() {
|
||||
|
||||
@@ -370,6 +400,10 @@ public class JdbcRepositoryIntegrationTests {
|
||||
@Query("SELECT id_Prop from dummy_entity where id_Prop = :id")
|
||||
DummyEntity withMissingColumn(@Param("id") Long id);
|
||||
|
||||
boolean existsByNameIn(String... names);
|
||||
|
||||
boolean existsByNameNotIn(String... names);
|
||||
|
||||
boolean existsByName(String name);
|
||||
|
||||
int countByName(String name);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.relational.core.sql;
|
||||
|
||||
/**
|
||||
* Simple condition that evaluates to SQL {@code FALSE}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
public class FalseCondition implements Condition {
|
||||
|
||||
public static final FalseCondition INSTANCE = new FalseCondition();
|
||||
|
||||
private FalseCondition() {}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "1 = 0";
|
||||
}
|
||||
}
|
||||
@@ -149,7 +149,7 @@ public class In extends AbstractSegment implements Condition {
|
||||
return new In(columnOrExpression, Arrays.asList(expressions), true);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.relational.core.sql.Condition#not()
|
||||
*/
|
||||
@@ -158,13 +158,26 @@ public class In extends AbstractSegment implements Condition {
|
||||
return new In(left, expressions, !notIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if this condition has at least one expression.
|
||||
* @since 2.1
|
||||
*/
|
||||
public boolean hasExpressions() {
|
||||
return !expressions.isEmpty();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return left + (notIn ? " NOT" : "") + " IN (" + StringUtils.collectionToDelimitedString(expressions, ", ") + ")";
|
||||
|
||||
if (hasExpressions()) {
|
||||
return left + (notIn ? " NOT" : "") + " IN (" + StringUtils.collectionToDelimitedString(expressions, ", ") + ")";
|
||||
}
|
||||
|
||||
return notIn ? TrueCondition.INSTANCE.toString() : FalseCondition.INSTANCE.toString();
|
||||
}
|
||||
|
||||
public boolean isNotIn() {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.relational.core.sql;
|
||||
|
||||
/**
|
||||
* Simple condition that evaluates to SQL {@code TRUE}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
public class TrueCondition implements Condition {
|
||||
|
||||
public static final TrueCondition INSTANCE = new TrueCondition();
|
||||
|
||||
private TrueCondition() {}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "1 = 1";
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,12 @@ class ConditionVisitor extends TypedSubtreeVisitor<Condition> implements PartRen
|
||||
}
|
||||
|
||||
if (segment instanceof In) {
|
||||
return new InVisitor(context, builder::append);
|
||||
|
||||
if (((In) segment).hasExpressions()) {
|
||||
return new InVisitor(context, builder::append);
|
||||
} else {
|
||||
return new EmptyInVisitor(context, builder::append);
|
||||
}
|
||||
}
|
||||
|
||||
if (segment instanceof NestedCondition) {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2019-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.relational.core.sql.render;
|
||||
|
||||
import org.springframework.data.relational.core.sql.FalseCondition;
|
||||
import org.springframework.data.relational.core.sql.In;
|
||||
import org.springframework.data.relational.core.sql.TrueCondition;
|
||||
|
||||
/**
|
||||
* Renderer for empty {@link In}. Uses a {@link RenderTarget} to call back for render results.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
class EmptyInVisitor extends TypedSingleConditionRenderSupport<In> {
|
||||
|
||||
private final RenderTarget target;
|
||||
|
||||
EmptyInVisitor(RenderContext context, RenderTarget target) {
|
||||
super(context);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.relational.core.sql.render.TypedSubtreeVisitor#leaveMatched(org.springframework.data.relational.core.sql.Visitable)
|
||||
*/
|
||||
@Override
|
||||
Delegation leaveMatched(In segment) {
|
||||
|
||||
target.onRendered(segment.isNotIn() ? TrueCondition.INSTANCE.toString() : FalseCondition.INSTANCE.toString());
|
||||
|
||||
return super.leaveMatched(segment);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.relational.core.sql;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link In}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class InTests {
|
||||
|
||||
@Test // DATAJDBC-604
|
||||
void shouldRenderToString() {
|
||||
|
||||
Table table = Table.create("table");
|
||||
|
||||
assertThat(In.create(table.column("col"), SQL.bindMarker())).hasToString("table.col IN (?)");
|
||||
assertThat(In.create(table.column("col"), SQL.bindMarker()).not()).hasToString("table.col NOT IN (?)");
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-604
|
||||
void shouldRenderEmptyExpressionToString() {
|
||||
|
||||
Table table = Table.create("table");
|
||||
|
||||
assertThat(In.create(table.column("col"))).hasToString("1 = 0");
|
||||
assertThat(In.create(table.column("col")).not()).hasToString("1 = 1");
|
||||
}
|
||||
}
|
||||
@@ -167,6 +167,22 @@ public class ConditionRendererUnitTests {
|
||||
assertThat(sql).endsWith("WHERE my_table.left IN (my_table.right)");
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-604
|
||||
public void shouldRenderEmptyIn() {
|
||||
|
||||
String sql = SqlRenderer.toString(StatementBuilder.select(left).from(table).where(left.in()).build());
|
||||
|
||||
assertThat(sql).endsWith("WHERE 1 = 0");
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-604
|
||||
public void shouldRenderEmptyNotIn() {
|
||||
|
||||
String sql = SqlRenderer.toString(StatementBuilder.select(left).from(table).where(left.notIn()).build());
|
||||
|
||||
assertThat(sql).endsWith("WHERE 1 = 1");
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-309
|
||||
public void shouldRenderLike() {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user