#64 - API polishing.

Document fluent API. Add fluent API for update. Introduce StatementMapper. Migrate Insert to StatementMapper. Refactoring and cleanup. Migrate Select to StatementMapper.

Original pull request: #106.
This commit is contained in:
Mark Paluch
2019-05-06 14:13:21 +02:00
committed by Oliver Drotbohm
parent ff69a41162
commit 774d2e8b09
28 changed files with 2369 additions and 1734 deletions

View File

@@ -13,22 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.r2dbc.domain;
package org.springframework.data.r2dbc.dialect;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import io.r2dbc.spi.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;
import org.springframework.data.r2dbc.dialect.BindMarker;
import org.springframework.data.r2dbc.dialect.BindMarkers;
import org.springframework.data.r2dbc.dialect.BindMarkersFactory;
import org.springframework.data.r2dbc.domain.BindTarget;
/**
* Unit tests for {@link Bindings}.
@@ -38,7 +34,7 @@ import org.springframework.data.r2dbc.dialect.BindMarkersFactory;
public class BindingsUnitTests {
BindMarkersFactory markersFactory = BindMarkersFactory.indexed("$", 1);
Statement statementMock = mock(Statement.class);
BindTarget bindTarget = mock(BindTarget.class);
@Test // gh-64
public void shouldCreateBindings() {
@@ -57,9 +53,9 @@ public class BindingsUnitTests {
MutableBindings bindings = new MutableBindings(markersFactory.create());
bindings.bind(bindings.nextMarker(), "foo");
bindings.apply(statementMock);
bindings.apply(bindTarget);
verify(statementMock).bind(0, "foo");
verify(bindTarget).bind(0, "foo");
}
@Test // gh-64
@@ -68,10 +64,10 @@ public class BindingsUnitTests {
MutableBindings bindings = new MutableBindings(markersFactory.create());
BindMarker marker = bindings.bind("foo");
bindings.apply(statementMock);
bindings.apply(bindTarget);
assertThat(marker.getPlaceholder()).isEqualTo("$1");
verify(statementMock).bind(0, "foo");
verify(bindTarget).bind(0, "foo");
}
@Test // gh-64
@@ -81,9 +77,9 @@ public class BindingsUnitTests {
bindings.bindNull(bindings.nextMarker(), String.class);
bindings.apply(statementMock);
bindings.apply(bindTarget);
verify(statementMock).bindNull(0, String.class);
verify(bindTarget).bindNull(0, String.class);
}
@Test // gh-64
@@ -92,10 +88,10 @@ public class BindingsUnitTests {
MutableBindings bindings = new MutableBindings(markersFactory.create());
BindMarker marker = bindings.bindNull(String.class);
bindings.apply(statementMock);
bindings.apply(bindTarget);
assertThat(marker.getPlaceholder()).isEqualTo("$1");
verify(statementMock).bindNull(0, String.class);
verify(bindTarget).bindNull(0, String.class);
}
@Test // gh-64
@@ -147,10 +143,10 @@ public class BindingsUnitTests {
assertThat(merged).hasSize(3);
merged.apply(statementMock);
verify(statementMock).bind(0, "override");
verify(statementMock).bind(1, "left");
verify(statementMock).bind(2, "right");
merged.apply(bindTarget);
verify(bindTarget).bind(0, "override");
verify(bindTarget).bind(1, "left");
verify(bindTarget).bind(2, "right");
}
}

View File

@@ -17,6 +17,7 @@ package org.springframework.data.r2dbc.function;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.domain.Sort.Order.*;
import static org.springframework.data.r2dbc.function.query.Criteria.*;
import io.r2dbc.spi.ConnectionFactory;
import lombok.Data;
@@ -30,9 +31,11 @@ import org.junit.Test;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.r2dbc.function.query.Criteria;
import org.springframework.data.r2dbc.function.query.Update;
import org.springframework.data.r2dbc.testing.R2dbcIntegrationTestSupport;
import org.springframework.data.relational.core.mapping.Table;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -159,11 +162,12 @@ public abstract class AbstractDatabaseClientIntegrationTests extends R2dbcIntegr
databaseClient.insert().into("legoset")//
.value("id", 42055) //
.value("name", "SCHAUFELRADBAGGER") //
.nullValue("manual", Integer.class) //
.nullValue("manual") //
.fetch() //
.rowsUpdated() //
.as(StepVerifier::create) //
.expectNext(1).verifyComplete();
.expectNext(1) //
.verifyComplete();
assertThat(jdbc.queryForMap("SELECT id, name, manual FROM legoset")).containsEntry("id", 42055);
}
@@ -176,7 +180,7 @@ public abstract class AbstractDatabaseClientIntegrationTests extends R2dbcIntegr
databaseClient.insert().into("legoset")//
.value("id", 42055) //
.value("name", "SCHAUFELRADBAGGER") //
.nullValue("manual", Integer.class) //
.nullValue("manual") //
.then() //
.as(StepVerifier::create) //
.verifyComplete();
@@ -205,6 +209,65 @@ public abstract class AbstractDatabaseClientIntegrationTests extends R2dbcIntegr
assertThat(jdbc.queryForMap("SELECT id, name, manual FROM legoset")).containsEntry("id", 42055);
}
@Test // gh-64
public void update() {
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
databaseClient.update().table("legoset")//
.using(Update.update("name", "Lego")) //
.matching(Criteria.where("id").is(42055)) //
.fetch() //
.rowsUpdated() //
.as(StepVerifier::create) //
.expectNext(1) //
.verifyComplete();
assertThat(jdbc.queryForMap("SELECT name, manual FROM legoset")).containsEntry("name", "Lego");
}
@Test // gh-64
public void updateWithoutResult() {
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
databaseClient.update().table("legoset")//
.using(Update.update("name", "Lego")) //
.then() //
.as(StepVerifier::create) //
.verifyComplete();
assertThat(jdbc.queryForMap("SELECT name, manual FROM legoset")).containsEntry("name", "Lego");
}
@Test // gh-64
public void updateTypedObject() {
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
LegoSet legoSet = new LegoSet();
legoSet.setId(42055);
legoSet.setName("Lego");
legoSet.setManual(null);
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
databaseClient.update() //
.table(LegoSet.class) //
.using(legoSet) //
.fetch() //
.rowsUpdated() //
.as(StepVerifier::create) //
.expectNext(1) //
.verifyComplete();
assertThat(jdbc.queryForMap("SELECT name, manual FROM legoset")).containsEntry("name", "Lego");
}
@Test // gh-64
public void deleteUntyped() {
@@ -215,7 +278,7 @@ public abstract class AbstractDatabaseClientIntegrationTests extends R2dbcIntegr
databaseClient.delete() //
.from("legoset") //
.where(Criteria.of("id").is(42055)) //
.matching(where("id").is(42055)) //
.fetch() //
.rowsUpdated() //
.as(StepVerifier::create) //
@@ -234,7 +297,7 @@ public abstract class AbstractDatabaseClientIntegrationTests extends R2dbcIntegr
databaseClient.delete() //
.from(LegoSet.class) //
.where(Criteria.of("id").is(42055)) //
.matching(where("id").is(42055)) //
.then() //
.as(StepVerifier::create) //
.verifyComplete();
@@ -289,7 +352,7 @@ public abstract class AbstractDatabaseClientIntegrationTests extends R2dbcIntegr
databaseClient.select().from("legoset") //
.project("id", "name", "manual") //
.orderBy(Sort.by("id")) //
.where(Criteria.of("id").greaterThanOrEquals(42055).and("id").lessThanOrEquals(42055))
.matching(where("id").greaterThanOrEquals(42055).and("id").lessThanOrEquals(42055))
.map((r, md) -> r.get("id", Integer.class)) //
.all() //
.as(StepVerifier::create) //
@@ -308,7 +371,7 @@ public abstract class AbstractDatabaseClientIntegrationTests extends R2dbcIntegr
databaseClient.select().from(LegoSet.class) //
.orderBy(Sort.by("id")) //
.where(Criteria.of("id").in(42055, 42064)) //
.matching(where("id").in(42055, 42064)) //
.map((r, md) -> r.get("id", Integer.class)) //
.all() //
.as(StepVerifier::create) //
@@ -376,7 +439,7 @@ public abstract class AbstractDatabaseClientIntegrationTests extends R2dbcIntegr
@Table("legoset")
static class LegoSet {
int id;
@Id int id;
String name;
Integer manual;
}

View File

@@ -1,264 +0,0 @@
/*
* Copyright 2019 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.r2dbc.function;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import io.r2dbc.spi.Connection;
import io.r2dbc.spi.Statement;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.r2dbc.dialect.PostgresDialect;
import org.springframework.data.r2dbc.domain.PreparedOperation;
import org.springframework.data.r2dbc.domain.SettableValue;
import org.springframework.data.relational.core.dialect.RenderContextFactory;
import org.springframework.data.relational.core.sql.Delete;
import org.springframework.data.relational.core.sql.Insert;
import org.springframework.data.relational.core.sql.Select;
import org.springframework.data.relational.core.sql.Update;
/**
* Unit tests for {@link StatementFactory}.
*
* @author Mark Paluch
*/
public class StatementFactoryUnitTests {
// See https://github.com/spring-projects/spring-data-r2dbc/issues/55
DefaultStatementFactory statements = new DefaultStatementFactory(PostgresDialect.INSTANCE,
new RenderContextFactory(org.springframework.data.relational.core.dialect.PostgresDialect.INSTANCE)
.createRenderContext());
Statement statementMock = mock(Statement.class);
Connection connectionMock = mock(Connection.class);
@Before
public void before() {
when(connectionMock.createStatement(anyString())).thenReturn(statementMock);
}
@Test
public void shouldToQuerySimpleSelectWithoutBindings() {
PreparedOperation<Select> select = statements.select("foo", Arrays.asList("bar", "baz"), it -> {});
assertThat(select.getSource()).isInstanceOf(Select.class);
assertThat(select.toQuery()).isEqualTo("SELECT foo.bar, foo.baz FROM foo");
createBoundStatement(select, connectionMock);
verifyZeroInteractions(statementMock);
}
@Test
public void shouldToQuerySimpleSelectWithSimpleFilter() {
PreparedOperation<Select> select = statements.select("foo", Arrays.asList("bar", "baz"), it -> {
it.filterBy("doe", SettableValue.from("John"));
});
assertThat(select.getSource()).isInstanceOf(Select.class);
assertThat(select.toQuery()).isEqualTo("SELECT foo.bar, foo.baz FROM foo WHERE foo.doe = $1");
createBoundStatement(select, connectionMock);
verify(statementMock).bind(0, "John");
verifyNoMoreInteractions(statementMock);
}
@Test
public void shouldToQuerySimpleSelectWithMultipleFilters() {
PreparedOperation<Select> select = statements.select("foo", Arrays.asList("bar", "baz"), it -> {
it.filterBy("doe", SettableValue.from("John"));
it.filterBy("baz", SettableValue.from("Jake"));
});
assertThat(select.getSource()).isInstanceOf(Select.class);
assertThat(select.toQuery()).isEqualTo("SELECT foo.bar, foo.baz FROM foo WHERE foo.doe = $1 AND foo.baz = $2");
createBoundStatement(select, connectionMock);
verify(statementMock).bind(0, "John");
verify(statementMock).bind(1, "Jake");
verifyNoMoreInteractions(statementMock);
}
@Test
public void shouldToQuerySimpleSelectWithNullFilter() {
PreparedOperation<Select> select = statements.select("foo", Arrays.asList("bar", "baz"), it -> {
it.filterBy("doe", SettableValue.empty(String.class));
});
assertThat(select.getSource()).isInstanceOf(Select.class);
assertThat(select.toQuery()).isEqualTo("SELECT foo.bar, foo.baz FROM foo WHERE foo.doe IS NULL");
createBoundStatement(select, connectionMock);
verifyZeroInteractions(statementMock);
}
@Test
public void shouldToQuerySimpleSelectWithIterableFilter() {
PreparedOperation<Select> select = statements.select("foo", Arrays.asList("bar", "baz"), it -> {
it.filterBy("doe", SettableValue.from(Arrays.asList("John", "Jake")));
});
assertThat(select.getSource()).isInstanceOf(Select.class);
assertThat(select.toQuery()).isEqualTo("SELECT foo.bar, foo.baz FROM foo WHERE foo.doe IN ($1, $2)");
createBoundStatement(select, connectionMock);
verify(statementMock).bind(0, "John");
verify(statementMock).bind(1, "Jake");
verifyNoMoreInteractions(statementMock);
}
@Test
public void shouldFailInsertToQueryingWithoutValueBindings() {
assertThatThrownBy(() -> statements.insert("foo", Collections.emptyList(), it -> {}))
.isInstanceOf(IllegalStateException.class);
}
@Test
public void shouldToQuerySimpleInsert() {
PreparedOperation<Insert> insert = statements.insert("foo", Collections.emptyList(), it -> {
it.bind("bar", SettableValue.from("Foo"));
});
assertThat(insert.getSource()).isInstanceOf(Insert.class);
assertThat(insert.toQuery()).isEqualTo("INSERT INTO foo (bar) VALUES ($1)");
createBoundStatement(insert, connectionMock);
verify(statementMock).bind(0, "Foo");
verifyNoMoreInteractions(statementMock);
}
@Test
public void shouldFailUpdateToQueryingWithoutValueBindings() {
assertThatThrownBy(() -> statements.update("foo", it -> it.filterBy("foo", SettableValue.empty(Object.class))))
.isInstanceOf(IllegalStateException.class);
}
@Test
public void shouldToQuerySimpleUpdate() {
PreparedOperation<Update> update = statements.update("foo", it -> {
it.bind("bar", SettableValue.from("Foo"));
});
assertThat(update.getSource()).isInstanceOf(Update.class);
assertThat(update.toQuery()).isEqualTo("UPDATE foo SET bar = $1");
createBoundStatement(update, connectionMock);
verify(statementMock).bind(0, "Foo");
verifyNoMoreInteractions(statementMock);
}
@Test
public void shouldToQueryNullUpdate() {
PreparedOperation<Update> update = statements.update("foo", it -> {
it.bind("bar", SettableValue.empty(String.class));
});
assertThat(update.getSource()).isInstanceOf(Update.class);
assertThat(update.toQuery()).isEqualTo("UPDATE foo SET bar = $1");
createBoundStatement(update, connectionMock);
verify(statementMock).bindNull(0, String.class);
verifyNoMoreInteractions(statementMock);
}
@Test
public void shouldToQueryUpdateWithFilter() {
PreparedOperation<Update> update = statements.update("foo", it -> {
it.bind("bar", SettableValue.from("Foo"));
it.filterBy("baz", SettableValue.from("Baz"));
});
assertThat(update.getSource()).isInstanceOf(Update.class);
assertThat(update.toQuery()).isEqualTo("UPDATE foo SET bar = $1 WHERE foo.baz = $2");
createBoundStatement(update, connectionMock);
verify(statementMock).bind(0, "Foo");
verify(statementMock).bind(1, "Baz");
verifyNoMoreInteractions(statementMock);
}
@Test
public void shouldToQuerySimpleDeleteWithSimpleFilter() {
PreparedOperation<Delete> delete = statements.delete("foo", it -> {
it.filterBy("doe", SettableValue.from("John"));
});
assertThat(delete.getSource()).isInstanceOf(Delete.class);
assertThat(delete.toQuery()).isEqualTo("DELETE FROM foo WHERE foo.doe = $1");
createBoundStatement(delete, connectionMock);
verify(statementMock).bind(0, "John");
verifyNoMoreInteractions(statementMock);
}
@Test
public void shouldToQuerySimpleDeleteWithMultipleFilters() {
PreparedOperation<Delete> delete = statements.delete("foo", it -> {
it.filterBy("doe", SettableValue.from("John"));
it.filterBy("baz", SettableValue.from("Jake"));
});
assertThat(delete.getSource()).isInstanceOf(Delete.class);
assertThat(delete.toQuery()).isEqualTo("DELETE FROM foo WHERE foo.doe = $1 AND foo.baz = $2");
createBoundStatement(delete, connectionMock);
verify(statementMock).bind(0, "John");
verify(statementMock).bind(1, "Jake");
verifyNoMoreInteractions(statementMock);
}
@Test
public void shouldToQuerySimpleDeleteWithNullFilter() {
PreparedOperation<Delete> delete = statements.delete("foo", it -> {
it.filterBy("doe", SettableValue.empty(String.class));
});
assertThat(delete.getSource()).isInstanceOf(Delete.class);
assertThat(delete.toQuery()).isEqualTo("DELETE FROM foo WHERE foo.doe IS NULL");
createBoundStatement(delete, connectionMock);
verifyZeroInteractions(statementMock);
}
void createBoundStatement(PreparedOperation<?> operation, Connection connection) {
Statement statement = connection.createStatement(operation.toQuery());
operation.bindTo(new DefaultDatabaseClient.StatementWrapper(statement));
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2019 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.r2dbc.function;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import org.junit.Test;
import org.springframework.data.r2dbc.dialect.PostgresDialect;
import org.springframework.data.r2dbc.domain.BindTarget;
import org.springframework.data.r2dbc.domain.PreparedOperation;
import org.springframework.data.r2dbc.function.StatementMapper.UpdateSpec;
import org.springframework.data.r2dbc.function.query.Criteria;
import org.springframework.data.r2dbc.function.query.Update;
/**
* Unit tests for {@link DefaultStatementMapper}.
*
* @author Mark Paluch
*/
public class StatementMapperUnitTests {
ReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE);
StatementMapper mapper = strategy.getStatementMapper();
BindTarget bindTarget = mock(BindTarget.class);
@Test // gh-64
public void shouldMapUpdate() {
UpdateSpec updateSpec = mapper.createUpdate("foo", Update.update("column", "value"));
PreparedOperation<?> preparedOperation = mapper.getMappedObject(updateSpec);
assertThat(preparedOperation.toQuery()).isEqualTo("UPDATE foo SET column = $1");
preparedOperation.bindTo(bindTarget);
verify(bindTarget).bind(0, "value");
}
@Test // gh-64
public void shouldMapUpdateWithCriteria() {
UpdateSpec updateSpec = mapper.createUpdate("foo", Update.update("column", "value"))
.withCriteria(Criteria.where("foo").is("bar"));
PreparedOperation<?> preparedOperation = mapper.getMappedObject(updateSpec);
assertThat(preparedOperation.toQuery()).isEqualTo("UPDATE foo SET column = $1 WHERE foo.foo = $2");
preparedOperation.bindTo(bindTarget);
verify(bindTarget).bind(0, "value");
verify(bindTarget).bind(1, "bar");
}
}

View File

@@ -34,9 +34,9 @@ public class CriteriaUnitTests {
@Test // gh-64
public void andChainedCriteria() {
Criteria criteria = of("foo").is("bar").and("baz").isNotNull();
Criteria criteria = where("foo").is("bar").and("baz").isNotNull();
assertThat(criteria.getProperty()).isEqualTo("baz");
assertThat(criteria.getColumn()).isEqualTo("baz");
assertThat(criteria.getComparator()).isEqualTo(Comparator.IS_NOT_NULL);
assertThat(criteria.getValue()).isNull();
assertThat(criteria.getPrevious()).isNotNull();
@@ -44,7 +44,7 @@ public class CriteriaUnitTests {
criteria = criteria.getPrevious();
assertThat(criteria.getProperty()).isEqualTo("foo");
assertThat(criteria.getColumn()).isEqualTo("foo");
assertThat(criteria.getComparator()).isEqualTo(Comparator.EQ);
assertThat(criteria.getValue()).isEqualTo("bar");
}
@@ -52,9 +52,9 @@ public class CriteriaUnitTests {
@Test // gh-64
public void orChainedCriteria() {
Criteria criteria = of("foo").is("bar").or("baz").isNotNull();
Criteria criteria = where("foo").is("bar").or("baz").isNotNull();
assertThat(criteria.getProperty()).isEqualTo("baz");
assertThat(criteria.getColumn()).isEqualTo("baz");
assertThat(criteria.getCombinator()).isEqualTo(Combinator.OR);
criteria = criteria.getPrevious();
@@ -66,9 +66,9 @@ public class CriteriaUnitTests {
@Test // gh-64
public void shouldBuildEqualsCriteria() {
Criteria criteria = of("foo").is("bar");
Criteria criteria = where("foo").is("bar");
assertThat(criteria.getProperty()).isEqualTo("foo");
assertThat(criteria.getColumn()).isEqualTo("foo");
assertThat(criteria.getComparator()).isEqualTo(Comparator.EQ);
assertThat(criteria.getValue()).isEqualTo("bar");
}
@@ -76,9 +76,9 @@ public class CriteriaUnitTests {
@Test // gh-64
public void shouldBuildNotEqualsCriteria() {
Criteria criteria = of("foo").not("bar");
Criteria criteria = where("foo").not("bar");
assertThat(criteria.getProperty()).isEqualTo("foo");
assertThat(criteria.getColumn()).isEqualTo("foo");
assertThat(criteria.getComparator()).isEqualTo(Comparator.NEQ);
assertThat(criteria.getValue()).isEqualTo("bar");
}
@@ -86,9 +86,9 @@ public class CriteriaUnitTests {
@Test // gh-64
public void shouldBuildInCriteria() {
Criteria criteria = of("foo").in("bar", "baz");
Criteria criteria = where("foo").in("bar", "baz");
assertThat(criteria.getProperty()).isEqualTo("foo");
assertThat(criteria.getColumn()).isEqualTo("foo");
assertThat(criteria.getComparator()).isEqualTo(Comparator.IN);
assertThat(criteria.getValue()).isEqualTo(Arrays.asList("bar", "baz"));
}
@@ -96,9 +96,9 @@ public class CriteriaUnitTests {
@Test // gh-64
public void shouldBuildNotInCriteria() {
Criteria criteria = of("foo").notIn("bar", "baz");
Criteria criteria = where("foo").notIn("bar", "baz");
assertThat(criteria.getProperty()).isEqualTo("foo");
assertThat(criteria.getColumn()).isEqualTo("foo");
assertThat(criteria.getComparator()).isEqualTo(Comparator.NOT_IN);
assertThat(criteria.getValue()).isEqualTo(Arrays.asList("bar", "baz"));
}
@@ -106,9 +106,9 @@ public class CriteriaUnitTests {
@Test // gh-64
public void shouldBuildGtCriteria() {
Criteria criteria = of("foo").greaterThan(1);
Criteria criteria = where("foo").greaterThan(1);
assertThat(criteria.getProperty()).isEqualTo("foo");
assertThat(criteria.getColumn()).isEqualTo("foo");
assertThat(criteria.getComparator()).isEqualTo(Comparator.GT);
assertThat(criteria.getValue()).isEqualTo(1);
}
@@ -116,9 +116,9 @@ public class CriteriaUnitTests {
@Test // gh-64
public void shouldBuildGteCriteria() {
Criteria criteria = of("foo").greaterThanOrEquals(1);
Criteria criteria = where("foo").greaterThanOrEquals(1);
assertThat(criteria.getProperty()).isEqualTo("foo");
assertThat(criteria.getColumn()).isEqualTo("foo");
assertThat(criteria.getComparator()).isEqualTo(Comparator.GTE);
assertThat(criteria.getValue()).isEqualTo(1);
}
@@ -126,9 +126,9 @@ public class CriteriaUnitTests {
@Test // gh-64
public void shouldBuildLtCriteria() {
Criteria criteria = of("foo").lessThan(1);
Criteria criteria = where("foo").lessThan(1);
assertThat(criteria.getProperty()).isEqualTo("foo");
assertThat(criteria.getColumn()).isEqualTo("foo");
assertThat(criteria.getComparator()).isEqualTo(Comparator.LT);
assertThat(criteria.getValue()).isEqualTo(1);
}
@@ -136,9 +136,9 @@ public class CriteriaUnitTests {
@Test // gh-64
public void shouldBuildLteCriteria() {
Criteria criteria = of("foo").lessThanOrEquals(1);
Criteria criteria = where("foo").lessThanOrEquals(1);
assertThat(criteria.getProperty()).isEqualTo("foo");
assertThat(criteria.getColumn()).isEqualTo("foo");
assertThat(criteria.getComparator()).isEqualTo(Comparator.LTE);
assertThat(criteria.getValue()).isEqualTo(1);
}
@@ -146,9 +146,9 @@ public class CriteriaUnitTests {
@Test // gh-64
public void shouldBuildLikeCriteria() {
Criteria criteria = of("foo").like("hello%");
Criteria criteria = where("foo").like("hello%");
assertThat(criteria.getProperty()).isEqualTo("foo");
assertThat(criteria.getColumn()).isEqualTo("foo");
assertThat(criteria.getComparator()).isEqualTo(Comparator.LIKE);
assertThat(criteria.getValue()).isEqualTo("hello%");
}
@@ -156,18 +156,18 @@ public class CriteriaUnitTests {
@Test // gh-64
public void shouldBuildIsNullCriteria() {
Criteria criteria = of("foo").isNull();
Criteria criteria = where("foo").isNull();
assertThat(criteria.getProperty()).isEqualTo("foo");
assertThat(criteria.getColumn()).isEqualTo("foo");
assertThat(criteria.getComparator()).isEqualTo(Comparator.IS_NULL);
}
@Test // gh-64
public void shouldBuildIsNotNullCriteria() {
Criteria criteria = of("foo").isNotNull();
Criteria criteria = where("foo").isNotNull();
assertThat(criteria.getProperty()).isEqualTo("foo");
assertThat(criteria.getColumn()).isEqualTo("foo");
assertThat(criteria.getComparator()).isEqualTo(Comparator.IS_NOT_NULL);
}
}

View File

@@ -17,12 +17,14 @@ package org.springframework.data.r2dbc.function.query;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import io.r2dbc.spi.Statement;
import static org.springframework.data.domain.Sort.Order.*;
import org.junit.Test;
import org.springframework.data.domain.Sort;
import org.springframework.data.r2dbc.dialect.BindMarkersFactory;
import org.springframework.data.r2dbc.domain.BindTarget;
import org.springframework.data.r2dbc.domain.SettableValue;
import org.springframework.data.r2dbc.function.convert.MappingR2dbcConverter;
import org.springframework.data.r2dbc.function.convert.R2dbcConverter;
import org.springframework.data.relational.core.mapping.Column;
@@ -30,33 +32,46 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext
import org.springframework.data.relational.core.sql.Table;
/**
* Unit tests for {@link CriteriaMapper}.
* Unit tests for {@link QueryMapper}.
*
* @author Mark Paluch
*/
public class CriteriaMapperUnitTests {
public class QueryMapperUnitTests {
R2dbcConverter converter = new MappingR2dbcConverter(new RelationalMappingContext());
CriteriaMapper mapper = new CriteriaMapper(converter);
Statement statementMock = mock(Statement.class);
QueryMapper mapper = new QueryMapper(converter);
BindTarget bindTarget = mock(BindTarget.class);
@Test // gh-64
public void shouldMapSimpleCriteria() {
Criteria criteria = Criteria.of("name").is("foo");
Criteria criteria = Criteria.where("name").is("foo");
BoundCondition bindings = map(criteria);
assertThat(bindings.getCondition().toString()).isEqualTo("person.name = ?[$1]");
bindings.getBindings().apply(statementMock);
verify(statementMock).bind(0, "foo");
bindings.getBindings().apply(bindTarget);
verify(bindTarget).bind(0, "foo");
}
@Test // gh-64
public void shouldMapSimpleNullableCriteria() {
Criteria criteria = Criteria.where("name").is(SettableValue.empty(Integer.class));
BoundCondition bindings = map(criteria);
assertThat(bindings.getCondition().toString()).isEqualTo("person.name = ?[$1]");
bindings.getBindings().apply(bindTarget);
verify(bindTarget).bindNull(0, Integer.class);
}
@Test // gh-64
public void shouldConsiderColumnName() {
Criteria criteria = Criteria.of("alternative").is("foo");
Criteria criteria = Criteria.where("alternative").is("foo");
BoundCondition bindings = map(criteria);
@@ -66,21 +81,21 @@ public class CriteriaMapperUnitTests {
@Test // gh-64
public void shouldMapAndCriteria() {
Criteria criteria = Criteria.of("name").is("foo").and("bar").is("baz");
Criteria criteria = Criteria.where("name").is("foo").and("bar").is("baz");
BoundCondition bindings = map(criteria);
assertThat(bindings.getCondition().toString()).isEqualTo("person.name = ?[$1] AND person.bar = ?[$2]");
bindings.getBindings().apply(statementMock);
verify(statementMock).bind(0, "foo");
verify(statementMock).bind(1, "baz");
bindings.getBindings().apply(bindTarget);
verify(bindTarget).bind(0, "foo");
verify(bindTarget).bind(1, "baz");
}
@Test // gh-64
public void shouldMapOrCriteria() {
Criteria criteria = Criteria.of("name").is("foo").or("bar").is("baz");
Criteria criteria = Criteria.where("name").is("foo").or("bar").is("baz");
BoundCondition bindings = map(criteria);
@@ -90,7 +105,7 @@ public class CriteriaMapperUnitTests {
@Test // gh-64
public void shouldMapAndOrCriteria() {
Criteria criteria = Criteria.of("name").is("foo") //
Criteria criteria = Criteria.where("name").is("foo") //
.and("name").isNotNull() //
.or("bar").is("baz") //
.and("anotherOne").is("alternative");
@@ -104,7 +119,7 @@ public class CriteriaMapperUnitTests {
@Test // gh-64
public void shouldMapNeq() {
Criteria criteria = Criteria.of("name").not("foo");
Criteria criteria = Criteria.where("name").not("foo");
BoundCondition bindings = map(criteria);
@@ -114,7 +129,7 @@ public class CriteriaMapperUnitTests {
@Test // gh-64
public void shouldMapIsNull() {
Criteria criteria = Criteria.of("name").isNull();
Criteria criteria = Criteria.where("name").isNull();
BoundCondition bindings = map(criteria);
@@ -124,7 +139,7 @@ public class CriteriaMapperUnitTests {
@Test // gh-64
public void shouldMapIsNotNull() {
Criteria criteria = Criteria.of("name").isNotNull();
Criteria criteria = Criteria.where("name").isNotNull();
BoundCondition bindings = map(criteria);
@@ -134,7 +149,7 @@ public class CriteriaMapperUnitTests {
@Test // gh-64
public void shouldMapIsIn() {
Criteria criteria = Criteria.of("name").in("a", "b", "c");
Criteria criteria = Criteria.where("name").in("a", "b", "c");
BoundCondition bindings = map(criteria);
@@ -144,7 +159,7 @@ public class CriteriaMapperUnitTests {
@Test // gh-64
public void shouldMapIsNotIn() {
Criteria criteria = Criteria.of("name").notIn("a", "b", "c");
Criteria criteria = Criteria.where("name").notIn("a", "b", "c");
BoundCondition bindings = map(criteria);
@@ -154,7 +169,7 @@ public class CriteriaMapperUnitTests {
@Test // gh-64
public void shouldMapIsGt() {
Criteria criteria = Criteria.of("name").greaterThan("a");
Criteria criteria = Criteria.where("name").greaterThan("a");
BoundCondition bindings = map(criteria);
@@ -164,7 +179,7 @@ public class CriteriaMapperUnitTests {
@Test // gh-64
public void shouldMapIsGte() {
Criteria criteria = Criteria.of("name").greaterThanOrEquals("a");
Criteria criteria = Criteria.where("name").greaterThanOrEquals("a");
BoundCondition bindings = map(criteria);
@@ -174,7 +189,7 @@ public class CriteriaMapperUnitTests {
@Test // gh-64
public void shouldMapIsLt() {
Criteria criteria = Criteria.of("name").lessThan("a");
Criteria criteria = Criteria.where("name").lessThan("a");
BoundCondition bindings = map(criteria);
@@ -184,7 +199,7 @@ public class CriteriaMapperUnitTests {
@Test // gh-64
public void shouldMapIsLte() {
Criteria criteria = Criteria.of("name").lessThanOrEquals("a");
Criteria criteria = Criteria.where("name").lessThanOrEquals("a");
BoundCondition bindings = map(criteria);
@@ -194,13 +209,24 @@ public class CriteriaMapperUnitTests {
@Test // gh-64
public void shouldMapIsLike() {
Criteria criteria = Criteria.of("name").like("a");
Criteria criteria = Criteria.where("name").like("a");
BoundCondition bindings = map(criteria);
assertThat(bindings.getCondition().toString()).isEqualTo("person.name LIKE ?[$1]");
}
@Test // gh-64
public void shouldMapSort() {
Sort sort = Sort.by(desc("alternative"));
Sort mapped = mapper.getMappedObject(sort, converter.getMappingContext().getRequiredPersistentEntity(Person.class));
assertThat(mapped.getOrderFor("another_name")).isEqualTo(desc("another_name"));
assertThat(mapped.getOrderFor("alternative")).isNull();
}
@SuppressWarnings("unchecked")
private BoundCondition map(Criteria criteria) {

View File

@@ -0,0 +1,106 @@
/*
* Copyright 2019 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.r2dbc.function.query;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.util.Map;
import java.util.stream.Collectors;
import org.junit.Test;
import org.springframework.data.r2dbc.dialect.BindMarkersFactory;
import org.springframework.data.r2dbc.domain.BindTarget;
import org.springframework.data.r2dbc.domain.SettableValue;
import org.springframework.data.r2dbc.function.convert.MappingR2dbcConverter;
import org.springframework.data.r2dbc.function.convert.R2dbcConverter;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.sql.AssignValue;
import org.springframework.data.relational.core.sql.Expression;
import org.springframework.data.relational.core.sql.SQL;
import org.springframework.data.relational.core.sql.Table;
/**
* Unit tests for {@link UpdateMapper}.
*
* @author Mark Paluch
*/
public class UpdateMapperUnitTests {
R2dbcConverter converter = new MappingR2dbcConverter(new RelationalMappingContext());
UpdateMapper mapper = new UpdateMapper(converter);
BindTarget bindTarget = mock(BindTarget.class);
@Test // gh-64
public void shouldMapFieldNamesInUpdate() {
Update update = Update.update("alternative", "foo");
BoundAssignments mapped = map(update);
Map<String, Expression> assignments = mapped.getAssignments().stream().map(it -> (AssignValue) it)
.collect(Collectors.toMap(k -> k.getColumn().getName(), AssignValue::getValue));
assertThat(assignments).containsEntry("another_name", SQL.bindMarker("$1"));
}
@Test // gh-64
public void shouldUpdateToSettableValue() {
Update update = Update.update("alternative", SettableValue.empty(String.class));
BoundAssignments mapped = map(update);
Map<String, Expression> assignments = mapped.getAssignments().stream().map(it -> (AssignValue) it)
.collect(Collectors.toMap(k -> k.getColumn().getName(), AssignValue::getValue));
assertThat(assignments).containsEntry("another_name", SQL.bindMarker("$1"));
mapped.getBindings().apply(bindTarget);
verify(bindTarget).bindNull(0, String.class);
}
@Test // gh-64
public void shouldUpdateToNull() {
Update update = Update.update("alternative", null);
BoundAssignments mapped = map(update);
assertThat(mapped.getAssignments()).hasSize(1);
assertThat(mapped.getAssignments().get(0).toString()).isEqualTo("person.another_name = NULL");
mapped.getBindings().apply(bindTarget);
verifyZeroInteractions(bindTarget);
}
@SuppressWarnings("unchecked")
private BoundAssignments map(Update update) {
BindMarkersFactory markers = BindMarkersFactory.indexed("$", 1);
return mapper.getMappedObject(markers.create(), update, Table.create("person"),
converter.getMappingContext().getRequiredPersistentEntity(Person.class));
}
static class Person {
String name;
@Column("another_name") String alternative;
}
}