#20 - Add Dialect initial support for H2, PostgreSQL, and Microsoft SQL Server.
We now provide dialect support for H2, PostgreSQL, and Microsoft SQL Server databases, configurable through AbstractR2dbcConfiguration. By default, we obtain the Dialect by inspecting ConnectionFactoryMetadata to identify the database and the most likely dialect to use. BindableOperation encapsulates statements/queries that can accept parameters. Use BindableOperation for statements through DatabaseClient. Extract SQL creation. Split integration test into abstract base class that can be implemented with a database-specific test class. Original pull request: #24.
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
package org.springframework.data.r2dbc.dialect;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import io.r2dbc.h2.H2ConnectionConfiguration;
|
||||
import io.r2dbc.h2.H2ConnectionFactory;
|
||||
import io.r2dbc.mssql.MssqlConnectionConfiguration;
|
||||
import io.r2dbc.mssql.MssqlConnectionFactory;
|
||||
import io.r2dbc.postgresql.PostgresqlConnectionConfiguration;
|
||||
import io.r2dbc.postgresql.PostgresqlConnectionFactory;
|
||||
import io.r2dbc.spi.Connection;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import io.r2dbc.spi.ConnectionFactoryMetadata;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link Database}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class DatabaseUnitTests {
|
||||
|
||||
@Test // gh-20
|
||||
public void shouldResolveDatabaseType() {
|
||||
|
||||
PostgresqlConnectionFactory postgres = new PostgresqlConnectionFactory(PostgresqlConnectionConfiguration.builder()
|
||||
.host("localhost").database("foo").username("bar").password("password").build());
|
||||
MssqlConnectionFactory mssql = new MssqlConnectionFactory(MssqlConnectionConfiguration.builder().host("localhost")
|
||||
.database("foo").username("bar").password("password").build());
|
||||
H2ConnectionFactory h2 = new H2ConnectionFactory(H2ConnectionConfiguration.builder().inMemory("mem").build());
|
||||
|
||||
assertThat(Database.findDatabase(postgres)).contains(Database.POSTGRES);
|
||||
assertThat(Database.findDatabase(mssql)).contains(Database.SQL_SERVER);
|
||||
assertThat(Database.findDatabase(h2)).contains(Database.H2);
|
||||
}
|
||||
|
||||
@Test // gh-20
|
||||
public void shouldNotResolveUnknownDatabase() {
|
||||
assertThat(Database.findDatabase(new UnknownConnectionFactory())).isEmpty();
|
||||
}
|
||||
|
||||
static class UnknownConnectionFactory implements ConnectionFactory {
|
||||
|
||||
@Override
|
||||
public Publisher<? extends Connection> create() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectionFactoryMetadata getMetadata() {
|
||||
return () -> "foo";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,25 @@ public class IndexedBindMarkersUnitTests {
|
||||
assertThat(bindMarkers2.next().getPlaceholder()).isEqualTo("$0");
|
||||
}
|
||||
|
||||
@Test // gh-15
|
||||
public void shouldCreateNewBindMarkersWithOffset() {
|
||||
|
||||
Statement<?> statement = mock(Statement.class);
|
||||
|
||||
BindMarkers bindMarkers = BindMarkersFactory.indexed("$", 1).create();
|
||||
|
||||
BindMarker first = bindMarkers.next();
|
||||
first.bind(statement, "foo");
|
||||
|
||||
BindMarker second = bindMarkers.next();
|
||||
second.bind(statement, "bar");
|
||||
|
||||
assertThat(first.getPlaceholder()).isEqualTo("$1");
|
||||
assertThat(second.getPlaceholder()).isEqualTo("$2");
|
||||
verify(statement).bind(0, "foo");
|
||||
verify(statement).bind(1, "bar");
|
||||
}
|
||||
|
||||
@Test // gh-15
|
||||
public void nextShouldIncrementBindMarker() {
|
||||
|
||||
@@ -50,8 +69,8 @@ public class IndexedBindMarkersUnitTests {
|
||||
|
||||
BindMarkers bindMarkers = BindMarkersFactory.indexed("$", 0).create();
|
||||
|
||||
bindMarkers.next().bindValue(statement, "foo");
|
||||
bindMarkers.next().bindValue(statement, "bar");
|
||||
bindMarkers.next().bind(statement, "foo");
|
||||
bindMarkers.next().bind(statement, "bar");
|
||||
|
||||
verify(statement).bind(0, "foo");
|
||||
verify(statement).bind(1, "bar");
|
||||
|
||||
@@ -89,8 +89,8 @@ public class NamedBindMarkersUnitTests {
|
||||
|
||||
BindMarkers bindMarkers = BindMarkersFactory.named("@", "p", 32).create();
|
||||
|
||||
bindMarkers.next().bindValue(statement, "foo");
|
||||
bindMarkers.next().bindValue(statement, "bar");
|
||||
bindMarkers.next().bind(statement, "foo");
|
||||
bindMarkers.next().bind(statement, "bar");
|
||||
|
||||
verify(statement).bind("p0", "foo");
|
||||
verify(statement).bind("p1", "bar");
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.springframework.data.r2dbc.dialect;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link PostgresDialect}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class PostgresDialectUnitTests {
|
||||
|
||||
@Test // gh-20
|
||||
public void shouldUsePostgresPlaceholders() {
|
||||
|
||||
BindMarkers bindMarkers = PostgresDialect.INSTANCE.getBindMarkersFactory().create();
|
||||
|
||||
BindMarker first = bindMarkers.next();
|
||||
BindMarker second = bindMarkers.next("foo");
|
||||
|
||||
assertThat(first.getPlaceholder()).isEqualTo("$1");
|
||||
assertThat(second.getPlaceholder()).isEqualTo("$2");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.springframework.data.r2dbc.dialect;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link SqlServerDialect}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class SqlServerDialectUnitTests {
|
||||
|
||||
@Test // gh-20
|
||||
public void shouldUseNamedPlaceholders() {
|
||||
|
||||
BindMarkers bindMarkers = SqlServerDialect.INSTANCE.getBindMarkersFactory().create();
|
||||
|
||||
BindMarker first = bindMarkers.next();
|
||||
BindMarker second = bindMarkers.next("'foo!bar");
|
||||
|
||||
assertThat(first.getPlaceholder()).isEqualTo("@P0");
|
||||
assertThat(second.getPlaceholder()).isEqualTo("@P1_foobar");
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,11 @@ import lombok.Data;
|
||||
import reactor.core.publisher.Hooks;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
@@ -33,11 +36,11 @@ import org.springframework.data.relational.core.mapping.Table;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link DatabaseClient} against PostgreSQL.
|
||||
* Integration tests for {@link DatabaseClient}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class DatabaseClientIntegrationTests extends R2dbcIntegrationTestSupport {
|
||||
public abstract class AbstractDatabaseClientIntegrationTests extends R2dbcIntegrationTestSupport {
|
||||
|
||||
private ConnectionFactory connectionFactory;
|
||||
|
||||
@@ -50,24 +53,56 @@ public class DatabaseClientIntegrationTests extends R2dbcIntegrationTestSupport
|
||||
|
||||
connectionFactory = createConnectionFactory();
|
||||
|
||||
String tableToCreate = "CREATE TABLE IF NOT EXISTS legoset (\n"
|
||||
+ " id integer CONSTRAINT id PRIMARY KEY,\n" + " name varchar(255) NOT NULL,\n"
|
||||
+ " manual integer NULL\n" + ");";
|
||||
|
||||
jdbc = createJdbcTemplate(createDataSource());
|
||||
jdbc.execute(tableToCreate);
|
||||
jdbc.execute("DELETE FROM legoset");
|
||||
|
||||
try {
|
||||
jdbc.execute("DROP TABLE legoset");
|
||||
} catch (DataAccessException e) {}
|
||||
jdbc.execute(getCreateTableStatement());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link DataSource} to be used in this test.
|
||||
*
|
||||
* @return the {@link DataSource} to be used in this test.
|
||||
*/
|
||||
protected abstract DataSource createDataSource();
|
||||
|
||||
/**
|
||||
* Creates a {@link ConnectionFactory} to be used in this test.
|
||||
*
|
||||
* @return the {@link ConnectionFactory} to be used in this test.
|
||||
*/
|
||||
protected abstract ConnectionFactory createConnectionFactory();
|
||||
|
||||
/**
|
||||
* Returns the the CREATE TABLE statement for table {@code legoset} with the following three columns:
|
||||
* <ul>
|
||||
* <li>id integer (primary key), not null</li>
|
||||
* <li>name varchar(255), nullable</li>
|
||||
* <li>manual integer, nullable</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return the CREATE TABLE statement for table {@code legoset} with three columns.
|
||||
*/
|
||||
protected abstract String getCreateTableStatement();
|
||||
|
||||
/**
|
||||
* Get a parameterized {@code INSERT INTO legoset} statement setting id, name, and manual values.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract String getInsertIntoLegosetStatement();
|
||||
|
||||
@Test
|
||||
public void executeInsert() {
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
databaseClient.execute().sql("INSERT INTO legoset (id, name, manual) VALUES($1, $2, $3)") //
|
||||
databaseClient.execute().sql(getInsertIntoLegosetStatement()) //
|
||||
.bind(0, 42055) //
|
||||
.bind(1, "SCHAUFELRADBAGGER") //
|
||||
.bindNull("$3", Integer.class) //
|
||||
.bindNull(2, Integer.class) //
|
||||
.fetch().rowsUpdated() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(1) //
|
||||
@@ -83,10 +118,10 @@ public class DatabaseClientIntegrationTests extends R2dbcIntegrationTestSupport
|
||||
|
||||
executeInsert();
|
||||
|
||||
databaseClient.execute().sql("INSERT INTO legoset (id, name, manual) VALUES($1, $2, $3)") //
|
||||
databaseClient.execute().sql(getInsertIntoLegosetStatement()) //
|
||||
.bind(0, 42055) //
|
||||
.bind(1, "SCHAUFELRADBAGGER") //
|
||||
.bindNull("$3", Integer.class) //
|
||||
.bindNull(2, Integer.class) //
|
||||
.fetch().rowsUpdated() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectErrorSatisfies(exception -> {
|
||||
@@ -104,7 +139,6 @@ public class DatabaseClientIntegrationTests extends R2dbcIntegrationTestSupport
|
||||
|
||||
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
|
||||
|
||||
// TODO: Driver/Decode does not support decoding null values?
|
||||
databaseClient.execute().sql("SELECT id, name, manual FROM legoset") //
|
||||
.as(LegoSet.class) //
|
||||
.fetch().all() //
|
||||
@@ -127,9 +161,9 @@ public class DatabaseClientIntegrationTests extends R2dbcIntegrationTestSupport
|
||||
.value("name", "SCHAUFELRADBAGGER") //
|
||||
.nullValue("manual", Integer.class) //
|
||||
.exchange() //
|
||||
.flatMapMany(it -> it.extract((r, m) -> r.get("id", Integer.class)).all()) //
|
||||
.flatMapMany(FetchSpec::rowsUpdated) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(42055).verifyComplete();
|
||||
.expectNext(1).verifyComplete();
|
||||
|
||||
assertThat(jdbc.queryForMap("SELECT id, name, manual FROM legoset")).containsEntry("id", 42055);
|
||||
}
|
||||
@@ -162,8 +196,9 @@ public class DatabaseClientIntegrationTests extends R2dbcIntegrationTestSupport
|
||||
|
||||
databaseClient.insert().into(LegoSet.class)//
|
||||
.using(legoSet).exchange() //
|
||||
.flatMapMany(it -> it.extract((r, m) -> r.get("id", Integer.class)).all()).as(StepVerifier::create) //
|
||||
.expectNext(42055).verifyComplete();
|
||||
.flatMapMany(FetchSpec::rowsUpdated) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(1).verifyComplete();
|
||||
|
||||
assertThat(jdbc.queryForMap("SELECT id, name, manual FROM legoset")).containsEntry("id", 42055);
|
||||
}
|
||||
@@ -28,18 +28,21 @@ import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.r2dbc.testing.R2dbcIntegrationTestSupport;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.transaction.NoTransactionException;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link TransactionalDatabaseClient}.
|
||||
* Abstract base class for integration tests for {@link TransactionalDatabaseClient}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class TransactionalDatabaseClientIntegrationTests extends R2dbcIntegrationTestSupport {
|
||||
public abstract class AbstractTransactionalDatabaseClientIntegrationTests extends R2dbcIntegrationTestSupport {
|
||||
|
||||
private ConnectionFactory connectionFactory;
|
||||
|
||||
@@ -52,15 +55,54 @@ public class TransactionalDatabaseClientIntegrationTests extends R2dbcIntegratio
|
||||
|
||||
connectionFactory = createConnectionFactory();
|
||||
|
||||
String tableToCreate = "CREATE TABLE IF NOT EXISTS legoset (\n"
|
||||
+ " id integer CONSTRAINT id PRIMARY KEY,\n" + " name varchar(255) NOT NULL,\n"
|
||||
+ " manual integer NULL\n" + ");";
|
||||
|
||||
jdbc = createJdbcTemplate(createDataSource());
|
||||
jdbc.execute(tableToCreate);
|
||||
try {
|
||||
jdbc.execute("DROP TABLE legoset");
|
||||
} catch (DataAccessException e) {}
|
||||
jdbc.execute(getCreateTableStatement());
|
||||
jdbc.execute("DELETE FROM legoset");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link DataSource} to be used in this test.
|
||||
*
|
||||
* @return the {@link DataSource} to be used in this test.
|
||||
*/
|
||||
protected abstract DataSource createDataSource();
|
||||
|
||||
/**
|
||||
* Creates a {@link ConnectionFactory} to be used in this test.
|
||||
*
|
||||
* @return the {@link ConnectionFactory} to be used in this test.
|
||||
*/
|
||||
protected abstract ConnectionFactory createConnectionFactory();
|
||||
|
||||
/**
|
||||
* Returns the the CREATE TABLE statement for table {@code legoset} with the following three columns:
|
||||
* <ul>
|
||||
* <li>id integer (primary key), not null</li>
|
||||
* <li>name varchar(255), nullable</li>
|
||||
* <li>manual integer, nullable</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return the CREATE TABLE statement for table {@code legoset} with three columns.
|
||||
*/
|
||||
protected abstract String getCreateTableStatement();
|
||||
|
||||
/**
|
||||
* Get a parameterized {@code INSERT INTO legoset} statement setting id, name, and manual values.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract String getInsertIntoLegosetStatement();
|
||||
|
||||
/**
|
||||
* Get a statement that returns the current transactionId.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract String getCurrentTransactionIdStatement();
|
||||
|
||||
@Test
|
||||
public void executeInsertInManagedTransaction() {
|
||||
|
||||
@@ -68,10 +110,10 @@ public class TransactionalDatabaseClientIntegrationTests extends R2dbcIntegratio
|
||||
|
||||
Flux<Integer> integerFlux = databaseClient.inTransaction(db -> {
|
||||
|
||||
return db.execute().sql("INSERT INTO legoset (id, name, manual) VALUES($1, $2, $3)") //
|
||||
return db.execute().sql(getInsertIntoLegosetStatement()) //
|
||||
.bind(0, 42055) //
|
||||
.bind(1, "SCHAUFELRADBAGGER") //
|
||||
.bindNull("$3", Integer.class) //
|
||||
.bindNull(2, Integer.class) //
|
||||
.fetch().rowsUpdated();
|
||||
});
|
||||
|
||||
@@ -87,11 +129,10 @@ public class TransactionalDatabaseClientIntegrationTests extends R2dbcIntegratio
|
||||
|
||||
TransactionalDatabaseClient databaseClient = TransactionalDatabaseClient.create(connectionFactory);
|
||||
|
||||
Mono<Integer> integerFlux = databaseClient.execute()
|
||||
.sql("INSERT INTO legoset (id, name, manual) VALUES($1, $2, $3)") //
|
||||
Mono<Integer> integerFlux = databaseClient.execute().sql(getInsertIntoLegosetStatement()) //
|
||||
.bind(0, 42055) //
|
||||
.bind(1, "SCHAUFELRADBAGGER") //
|
||||
.bindNull("$3", Integer.class) //
|
||||
.bindNull(2, Integer.class) //
|
||||
.fetch().rowsUpdated();
|
||||
|
||||
integerFlux.as(StepVerifier::create) //
|
||||
@@ -107,7 +148,7 @@ public class TransactionalDatabaseClientIntegrationTests extends R2dbcIntegratio
|
||||
Queue<Long> transactionIds = new ArrayBlockingQueue<>(5);
|
||||
TransactionalDatabaseClient databaseClient = TransactionalDatabaseClient.create(connectionFactory);
|
||||
|
||||
Flux<Long> txId = databaseClient.execute().sql("SELECT txid_current();").exchange()
|
||||
Flux<Long> txId = databaseClient.execute().sql(getCurrentTransactionIdStatement()).exchange()
|
||||
.flatMapMany(it -> it.extract((r, md) -> r.get(0, Long.class)).all());
|
||||
|
||||
Mono<Void> then = databaseClient.enableTransactionSynchronization(databaseClient.beginTransaction() //
|
||||
@@ -144,10 +185,10 @@ public class TransactionalDatabaseClientIntegrationTests extends R2dbcIntegratio
|
||||
|
||||
Flux<Integer> integerFlux = databaseClient.inTransaction(db -> {
|
||||
|
||||
return db.execute().sql("INSERT INTO legoset (id, name, manual) VALUES($1, $2, $3)") //
|
||||
return db.execute().sql(getInsertIntoLegosetStatement()) //
|
||||
.bind(0, 42055) //
|
||||
.bind(1, "SCHAUFELRADBAGGER") //
|
||||
.bindNull("$3", Integer.class) //
|
||||
.bindNull(2, Integer.class) //
|
||||
.fetch().rowsUpdated().then(Mono.error(new IllegalStateException("failed")));
|
||||
});
|
||||
|
||||
@@ -155,7 +196,8 @@ public class TransactionalDatabaseClientIntegrationTests extends R2dbcIntegratio
|
||||
.expectError(IllegalStateException.class) //
|
||||
.verify();
|
||||
|
||||
assertThat(jdbc.queryForMap("SELECT count(*) FROM legoset")).containsEntry("count", 0L);
|
||||
Integer count = jdbc.queryForObject("SELECT COUNT(*) FROM legoset", Integer.class);
|
||||
assertThat(count).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -163,10 +205,10 @@ public class TransactionalDatabaseClientIntegrationTests extends R2dbcIntegratio
|
||||
|
||||
TransactionalDatabaseClient databaseClient = TransactionalDatabaseClient.create(connectionFactory);
|
||||
|
||||
Flux<Long> transactionIds = databaseClient.inTransaction(db -> {
|
||||
Flux<Object> transactionIds = databaseClient.inTransaction(db -> {
|
||||
|
||||
Flux<Long> txId = db.execute().sql("SELECT txid_current();").exchange()
|
||||
.flatMapMany(it -> it.extract((r, md) -> r.get(0, Long.class)).all());
|
||||
Flux<Object> txId = db.execute().sql(getCurrentTransactionIdStatement()).exchange()
|
||||
.flatMapMany(it -> it.extract((r, md) -> r.get(0)).all());
|
||||
return txId.concatWith(txId);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
package org.springframework.data.r2dbc.function;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import io.r2dbc.spi.Statement;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.r2dbc.dialect.PostgresDialect;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DefaultReactiveDataAccessStrategy}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class DefaultReactiveDataAccessStrategyUnitTests {
|
||||
|
||||
DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE);
|
||||
|
||||
@Test // gh-20
|
||||
public void shouldRenderInsertAndReturnGeneratedKeysQuery() {
|
||||
|
||||
BindableOperation operation = strategy.insertAndReturnGeneratedKeys("table",
|
||||
new HashSet<>(Arrays.asList("firstname", "lastname")));
|
||||
|
||||
assertThat(operation.toQuery()).isEqualTo("INSERT INTO table (firstname, lastname) VALUES($1, $2) RETURNING *");
|
||||
}
|
||||
|
||||
@Test // gh-20
|
||||
public void shouldRenderUpdateByIdQuery() {
|
||||
|
||||
BindableOperation operation = strategy.updateById("table", new HashSet<>(Arrays.asList("firstname", "lastname")),
|
||||
"id");
|
||||
|
||||
assertThat(operation.toQuery()).isEqualTo("UPDATE table SET firstname = $2, lastname = $3 WHERE id = $1");
|
||||
}
|
||||
|
||||
@Test // gh-20
|
||||
public void shouldRenderSelectByIdQuery() {
|
||||
|
||||
BindableOperation operation = strategy.selectById("table", new HashSet<>(Arrays.asList("firstname", "lastname")),
|
||||
"id");
|
||||
|
||||
assertThat(operation.toQuery()).isEqualTo("SELECT firstname, lastname FROM table WHERE id = $1");
|
||||
}
|
||||
|
||||
@Test // gh-20
|
||||
public void shouldRenderSelectByIdQueryWithLimit() {
|
||||
|
||||
BindableOperation operation = strategy.selectById("table", new HashSet<>(Arrays.asList("firstname", "lastname")),
|
||||
"id", 10);
|
||||
|
||||
assertThat(operation.toQuery())
|
||||
.isEqualTo("SELECT firstname, lastname FROM table WHERE id = $1 ORDER BY id LIMIT 10");
|
||||
}
|
||||
|
||||
@Test // gh-20
|
||||
public void shouldFailRenderingSelectByIdInQueryWithoutBindings() {
|
||||
|
||||
BindableOperation operation = strategy.selectByIdIn("table", new HashSet<>(Arrays.asList("firstname", "lastname")),
|
||||
"id");
|
||||
|
||||
assertThatThrownBy(operation::toQuery).isInstanceOf(UnsupportedOperationException.class);
|
||||
}
|
||||
|
||||
@Test // gh-20
|
||||
public void shouldRenderSelectByIdInQuery() {
|
||||
|
||||
Statement<?> statement = mock(Statement.class);
|
||||
BindIdOperation operation = strategy.selectByIdIn("table", new HashSet<>(Arrays.asList("firstname", "lastname")),
|
||||
"id");
|
||||
|
||||
operation.bindId(statement, Collections.singleton("foo"));
|
||||
assertThat(operation.toQuery()).isEqualTo("SELECT firstname, lastname FROM table WHERE id IN ($1)");
|
||||
|
||||
operation.bindId(statement, "bar");
|
||||
assertThat(operation.toQuery()).isEqualTo("SELECT firstname, lastname FROM table WHERE id IN ($1, $2)");
|
||||
}
|
||||
|
||||
@Test // gh-20
|
||||
public void shouldRenderDeleteByIdQuery() {
|
||||
|
||||
BindableOperation operation = strategy.deleteById("table", "id");
|
||||
|
||||
assertThat(operation.toQuery()).isEqualTo("DELETE FROM table WHERE id = $1");
|
||||
}
|
||||
|
||||
@Test // gh-20
|
||||
public void shouldRenderDeleteByIdInQuery() {
|
||||
|
||||
Statement<?> statement = mock(Statement.class);
|
||||
BindIdOperation operation = strategy.deleteByIdIn("table", "id");
|
||||
|
||||
operation.bindId(statement, Collections.singleton("foo"));
|
||||
assertThat(operation.toQuery()).isEqualTo("DELETE FROM table WHERE id IN ($1)");
|
||||
|
||||
operation.bindId(statement, "bar");
|
||||
assertThat(operation.toQuery()).isEqualTo("DELETE FROM table WHERE id IN ($1, $2)");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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 io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Ignore;
|
||||
import org.springframework.data.r2dbc.testing.ExternalDatabase;
|
||||
import org.springframework.data.r2dbc.testing.PostgresTestSupport;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link DatabaseClient} against PostgreSQL.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class PostgresDatabaseClientIntegrationTests extends AbstractDatabaseClientIntegrationTests {
|
||||
|
||||
@ClassRule public static final ExternalDatabase database = PostgresTestSupport.database();
|
||||
|
||||
@Override
|
||||
protected DataSource createDataSource() {
|
||||
return PostgresTestSupport.createDataSource(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConnectionFactory createConnectionFactory() {
|
||||
return PostgresTestSupport.createConnectionFactory(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCreateTableStatement() {
|
||||
return PostgresTestSupport.CREATE_TABLE_LEGOSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInsertIntoLegosetStatement() {
|
||||
return PostgresTestSupport.INSERT_INTO_LEGOSET;
|
||||
}
|
||||
|
||||
@Ignore("Adding RETURNING * lets Postgres report 0 affected rows.")
|
||||
@Override
|
||||
public void insert() {}
|
||||
|
||||
@Ignore("Adding RETURNING * lets Postgres report 0 affected rows.")
|
||||
@Override
|
||||
public void insertTypedObject() {}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.springframework.data.r2dbc.function;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.springframework.data.r2dbc.testing.ExternalDatabase;
|
||||
import org.springframework.data.r2dbc.testing.PostgresTestSupport;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link TransactionalDatabaseClient} against PostgreSQL.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class PostgresTransactionalDatabaseClientIntegrationTests
|
||||
extends AbstractTransactionalDatabaseClientIntegrationTests {
|
||||
|
||||
@ClassRule public static final ExternalDatabase database = PostgresTestSupport.database();
|
||||
|
||||
@Override
|
||||
protected DataSource createDataSource() {
|
||||
return PostgresTestSupport.createDataSource(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConnectionFactory createConnectionFactory() {
|
||||
return PostgresTestSupport.createConnectionFactory(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCreateTableStatement() {
|
||||
return PostgresTestSupport.CREATE_TABLE_LEGOSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInsertIntoLegosetStatement() {
|
||||
return PostgresTestSupport.INSERT_INTO_LEGOSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCurrentTransactionIdStatement() {
|
||||
return "SELECT txid_current();";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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 io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.springframework.data.r2dbc.testing.ExternalDatabase;
|
||||
import org.springframework.data.r2dbc.testing.SqlServerTestSupport;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link DatabaseClient} against Microsoft SQL Server.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class SqlServerDatabaseClientIntegrationTests extends AbstractDatabaseClientIntegrationTests {
|
||||
|
||||
@ClassRule public static final ExternalDatabase database = SqlServerTestSupport.database();
|
||||
|
||||
@Override
|
||||
protected DataSource createDataSource() {
|
||||
return SqlServerTestSupport.createDataSource(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConnectionFactory createConnectionFactory() {
|
||||
return SqlServerTestSupport.createConnectionFactory(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCreateTableStatement() {
|
||||
return SqlServerTestSupport.CREATE_TABLE_LEGOSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInsertIntoLegosetStatement() {
|
||||
return SqlServerTestSupport.INSERT_INTO_LEGOSET;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.springframework.data.r2dbc.function;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.springframework.data.r2dbc.testing.ExternalDatabase;
|
||||
import org.springframework.data.r2dbc.testing.SqlServerTestSupport;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link TransactionalDatabaseClient} against Microsoft SQL Server.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class SqlServerTransactionalDatabaseClientIntegrationTests
|
||||
extends AbstractTransactionalDatabaseClientIntegrationTests {
|
||||
|
||||
@ClassRule public static final ExternalDatabase database = SqlServerTestSupport.database();
|
||||
|
||||
@Override
|
||||
protected DataSource createDataSource() {
|
||||
return SqlServerTestSupport.createDataSource(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConnectionFactory createConnectionFactory() {
|
||||
return SqlServerTestSupport.createConnectionFactory(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCreateTableStatement() {
|
||||
return SqlServerTestSupport.CREATE_TABLE_LEGOSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInsertIntoLegosetStatement() {
|
||||
return SqlServerTestSupport.INSERT_INTO_LEGOSET;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCurrentTransactionIdStatement() {
|
||||
return "SELECT CURRENT_TRANSACTION_ID();";
|
||||
}
|
||||
}
|
||||
@@ -30,54 +30,37 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.r2dbc.dialect.Database;
|
||||
import org.springframework.data.r2dbc.function.DefaultReactiveDataAccessStrategy;
|
||||
import org.springframework.data.r2dbc.function.TransactionalDatabaseClient;
|
||||
import org.springframework.data.r2dbc.repository.config.AbstractR2dbcConfiguration;
|
||||
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;
|
||||
import org.springframework.data.r2dbc.repository.query.Query;
|
||||
import org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactory;
|
||||
import org.springframework.data.r2dbc.testing.R2dbcIntegrationTestSupport;
|
||||
import org.springframework.data.relational.core.conversion.BasicRelationalConverter;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
import org.springframework.data.relational.core.mapping.Table;
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link LegoSetRepository} using {@link R2dbcRepositoryFactory}.
|
||||
* Abstract base class for integration tests for {@link LegoSetRepository} using {@link R2dbcRepositoryFactory}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
public class R2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestSupport {
|
||||
public abstract class AbstractR2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestSupport {
|
||||
|
||||
private static RelationalMappingContext mappingContext = new RelationalMappingContext();
|
||||
|
||||
@Autowired private LegoSetRepository repository;
|
||||
private JdbcTemplate jdbc;
|
||||
|
||||
@Configuration
|
||||
@EnableR2dbcRepositories(considerNestedRepositories = true,
|
||||
includeFilters = @Filter(classes = LegoSetRepository.class, type = FilterType.ASSIGNABLE_TYPE))
|
||||
static class IntegrationTestConfiguration extends AbstractR2dbcConfiguration {
|
||||
|
||||
@Override
|
||||
public ConnectionFactory connectionFactory() {
|
||||
return createConnectionFactory();
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
|
||||
@@ -85,13 +68,41 @@ public class R2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestSupport
|
||||
|
||||
this.jdbc = createJdbcTemplate(createDataSource());
|
||||
|
||||
String tableToCreate = "CREATE TABLE IF NOT EXISTS repo_legoset (\n" + " id SERIAL PRIMARY KEY,\n"
|
||||
+ " name varchar(255) NOT NULL,\n" + " manual integer NULL\n" + ");";
|
||||
try {
|
||||
this.jdbc.execute("DROP TABLE legoset");
|
||||
} catch (DataAccessException e) {}
|
||||
|
||||
this.jdbc.execute("DROP TABLE IF EXISTS repo_legoset");
|
||||
this.jdbc.execute(tableToCreate);
|
||||
this.jdbc.execute(getCreateTableStatement());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link DataSource} to be used in this test.
|
||||
*
|
||||
* @return the {@link DataSource} to be used in this test.
|
||||
*/
|
||||
protected abstract DataSource createDataSource();
|
||||
|
||||
/**
|
||||
* Creates a {@link ConnectionFactory} to be used in this test.
|
||||
*
|
||||
* @return the {@link ConnectionFactory} to be used in this test.
|
||||
*/
|
||||
protected abstract ConnectionFactory createConnectionFactory();
|
||||
|
||||
/**
|
||||
* Returns the the CREATE TABLE statement for table {@code legoset} with the following three columns:
|
||||
* <ul>
|
||||
* <li>id integer (primary key), not null, auto-increment</li>
|
||||
* <li>name varchar(255), nullable</li>
|
||||
* <li>manual integer, nullable</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return the CREATE TABLE statement for table {@code legoset} with three columns.
|
||||
*/
|
||||
protected abstract String getCreateTableStatement();
|
||||
|
||||
protected abstract Class<? extends LegoSetRepository> getRepositoryInterfaceType();
|
||||
|
||||
@Test
|
||||
public void shouldInsertNewItems() {
|
||||
|
||||
@@ -148,13 +159,14 @@ public class R2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestSupport
|
||||
@Test
|
||||
public void shouldInsertItemsTransactional() {
|
||||
|
||||
Database database = Database.findDatabase(createConnectionFactory()).get();
|
||||
DefaultReactiveDataAccessStrategy dataAccessStrategy = new DefaultReactiveDataAccessStrategy(
|
||||
database.latestDialect(), new BasicRelationalConverter(mappingContext));
|
||||
TransactionalDatabaseClient client = TransactionalDatabaseClient.builder()
|
||||
.connectionFactory(createConnectionFactory())
|
||||
.dataAccessStrategy(new DefaultReactiveDataAccessStrategy(new BasicRelationalConverter(mappingContext)))
|
||||
.build();
|
||||
.connectionFactory(createConnectionFactory()).dataAccessStrategy(dataAccessStrategy).build();
|
||||
|
||||
LegoSetRepository transactionalRepository = new R2dbcRepositoryFactory(client, mappingContext)
|
||||
.getRepository(LegoSetRepository.class);
|
||||
LegoSetRepository transactionalRepository = new R2dbcRepositoryFactory(client, mappingContext, dataAccessStrategy)
|
||||
.getRepository(getRepositoryInterfaceType());
|
||||
|
||||
LegoSet legoSet1 = new LegoSet(null, "SCHAUFELRADBAGGER", 12);
|
||||
LegoSet legoSet2 = new LegoSet(null, "FORSCHUNGSSCHIFF", 13);
|
||||
@@ -162,33 +174,31 @@ public class R2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestSupport
|
||||
Flux<Map<String, Object>> transactional = client.inTransaction(db -> {
|
||||
|
||||
return transactionalRepository.save(legoSet1) //
|
||||
.map(it -> jdbc.queryForMap("SELECT count(*) FROM repo_legoset"));
|
||||
.map(it -> jdbc.queryForMap("SELECT count(*) FROM legoset"));
|
||||
});
|
||||
|
||||
Mono<Map<String, Object>> nonTransactional = transactionalRepository.save(legoSet2) //
|
||||
.map(it -> jdbc.queryForMap("SELECT count(*) FROM repo_legoset"));
|
||||
.map(it -> jdbc.queryForMap("SELECT count(*) FROM legoset"));
|
||||
|
||||
transactional.as(StepVerifier::create).expectNext(Collections.singletonMap("count", 0L)).verifyComplete();
|
||||
nonTransactional.as(StepVerifier::create).expectNext(Collections.singletonMap("count", 2L)).verifyComplete();
|
||||
|
||||
Map<String, Object> count = jdbc.queryForMap("SELECT count(*) FROM repo_legoset");
|
||||
Map<String, Object> count = jdbc.queryForMap("SELECT count(*) FROM legoset");
|
||||
assertThat(count).containsEntry("count", 2L);
|
||||
}
|
||||
|
||||
@NoRepositoryBean
|
||||
interface LegoSetRepository extends ReactiveCrudRepository<LegoSet, Integer> {
|
||||
|
||||
@Query("SELECT * FROM repo_legoset WHERE name like $1")
|
||||
Flux<LegoSet> findByNameContains(String name);
|
||||
|
||||
@Query("SELECT * FROM repo_legoset")
|
||||
Flux<Named> findAsProjection();
|
||||
|
||||
@Query("SELECT * FROM repo_legoset WHERE manual = $1")
|
||||
Mono<LegoSet> findByManual(int manual);
|
||||
}
|
||||
|
||||
@Data
|
||||
@Table("repo_legoset")
|
||||
@Table("legoset")
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
static class LegoSet {
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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.repository;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration;
|
||||
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;
|
||||
import org.springframework.data.r2dbc.repository.query.Query;
|
||||
import org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactory;
|
||||
import org.springframework.data.r2dbc.testing.ExternalDatabase;
|
||||
import org.springframework.data.r2dbc.testing.PostgresTestSupport;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link LegoSetRepository} using {@link R2dbcRepositoryFactory} against Postgres.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
public class PostgresR2dbcRepositoryIntegrationTests extends AbstractR2dbcRepositoryIntegrationTests {
|
||||
|
||||
@ClassRule public static final ExternalDatabase database = PostgresTestSupport.database();
|
||||
|
||||
@Configuration
|
||||
@EnableR2dbcRepositories(considerNestedRepositories = true,
|
||||
includeFilters = @Filter(classes = PostgresLegoSetRepository.class, type = FilterType.ASSIGNABLE_TYPE))
|
||||
static class IntegrationTestConfiguration extends AbstractR2dbcConfiguration {
|
||||
|
||||
@Override
|
||||
public ConnectionFactory connectionFactory() {
|
||||
return PostgresTestSupport.createConnectionFactory(database);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataSource createDataSource() {
|
||||
return PostgresTestSupport.createDataSource(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConnectionFactory createConnectionFactory() {
|
||||
return PostgresTestSupport.createConnectionFactory(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCreateTableStatement() {
|
||||
return PostgresTestSupport.CREATE_TABLE_LEGOSET_WITH_ID_GENERATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<? extends LegoSetRepository> getRepositoryInterfaceType() {
|
||||
return PostgresLegoSetRepository.class;
|
||||
}
|
||||
|
||||
interface PostgresLegoSetRepository extends LegoSetRepository {
|
||||
|
||||
@Override
|
||||
@Query("SELECT * FROM legoset WHERE name like $1")
|
||||
Flux<LegoSet> findByNameContains(String name);
|
||||
|
||||
@Override
|
||||
@Query("SELECT * FROM legoset")
|
||||
Flux<Named> findAsProjection();
|
||||
|
||||
@Override
|
||||
@Query("SELECT * FROM legoset WHERE manual = $1")
|
||||
Mono<LegoSet> findByManual(int manual);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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.repository;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration;
|
||||
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;
|
||||
import org.springframework.data.r2dbc.repository.query.Query;
|
||||
import org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactory;
|
||||
import org.springframework.data.r2dbc.testing.ExternalDatabase;
|
||||
import org.springframework.data.r2dbc.testing.SqlServerTestSupport;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link LegoSetRepository} using {@link R2dbcRepositoryFactory} against Microsoft SQL Server.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
public class SqlServerR2dbcRepositoryIntegrationTests extends AbstractR2dbcRepositoryIntegrationTests {
|
||||
|
||||
@ClassRule public static final ExternalDatabase database = SqlServerTestSupport.database();
|
||||
|
||||
@Configuration
|
||||
@EnableR2dbcRepositories(considerNestedRepositories = true,
|
||||
includeFilters = @Filter(classes = SqlServerLegoSetRepository.class, type = FilterType.ASSIGNABLE_TYPE))
|
||||
static class IntegrationTestConfiguration extends AbstractR2dbcConfiguration {
|
||||
|
||||
@Override
|
||||
public ConnectionFactory connectionFactory() {
|
||||
return SqlServerTestSupport.createConnectionFactory(database);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataSource createDataSource() {
|
||||
return SqlServerTestSupport.createDataSource(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConnectionFactory createConnectionFactory() {
|
||||
return SqlServerTestSupport.createConnectionFactory(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCreateTableStatement() {
|
||||
return SqlServerTestSupport.CREATE_TABLE_LEGOSET_WITH_ID_GENERATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<? extends LegoSetRepository> getRepositoryInterfaceType() {
|
||||
return SqlServerLegoSetRepository.class;
|
||||
}
|
||||
|
||||
@Ignore("SQL server locks a SELECT COUNT so we cannot proceed.")
|
||||
@Override
|
||||
public void shouldInsertItemsTransactional() {}
|
||||
|
||||
interface SqlServerLegoSetRepository extends LegoSetRepository {
|
||||
|
||||
@Override
|
||||
@Query("SELECT * FROM legoset WHERE name like @name")
|
||||
Flux<LegoSet> findByNameContains(String name);
|
||||
|
||||
@Override
|
||||
@Query("SELECT * FROM legoset")
|
||||
Flux<Named> findAsProjection();
|
||||
|
||||
@Override
|
||||
@Query("SELECT * FROM legoset WHERE manual = @P0")
|
||||
Mono<LegoSet> findByManual(int manual);
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.r2dbc.function.DatabaseClient;
|
||||
import org.springframework.data.r2dbc.function.ReactiveDataAccessStrategy;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@@ -44,6 +45,11 @@ public class R2dbcRepositoriesRegistrarTests {
|
||||
public DatabaseClient databaseClient() {
|
||||
return mock(DatabaseClient.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ReactiveDataAccessStrategy reactiveDataAccessStrategy() {
|
||||
return mock(ReactiveDataAccessStrategy.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired PersonRepository personRepository;
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
package org.springframework.data.r2dbc.repository.query;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
@@ -60,7 +59,6 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
private RepositoryMetadata metadata;
|
||||
|
||||
@Before
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setUp() {
|
||||
|
||||
this.mappingContext = new RelationalMappingContext();
|
||||
@@ -68,7 +66,7 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
this.metadata = AbstractRepositoryMetadata.getMetadata(SampleRepository.class);
|
||||
this.factory = new SpelAwareProxyProjectionFactory();
|
||||
|
||||
when(bindSpec.bind(anyString(), any())).thenReturn(bindSpec);
|
||||
when(bindSpec.bind(anyInt(), any())).thenReturn(bindSpec);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -82,7 +80,7 @@ public class StringBasedR2dbcQueryUnitTests {
|
||||
assertThat(stringQuery.get()).isEqualTo("SELECT * FROM person WHERE lastname = $1");
|
||||
assertThat(stringQuery.bind(bindSpec)).isNotNull();
|
||||
|
||||
verify(bindSpec).bind("$1", "White");
|
||||
verify(bindSpec).bind(0, "White");
|
||||
}
|
||||
|
||||
private StringBasedR2dbcQuery getQueryMethod(String name, Class<?>... args) {
|
||||
|
||||
@@ -17,7 +17,6 @@ package org.springframework.data.r2dbc.repository.support;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -28,17 +27,19 @@ import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.r2dbc.function.DatabaseClient;
|
||||
import org.springframework.data.r2dbc.function.ReactiveDataAccessStrategy;
|
||||
import org.springframework.data.r2dbc.function.convert.MappingR2dbcConverter;
|
||||
import org.springframework.data.r2dbc.repository.config.AbstractR2dbcConfiguration;
|
||||
import org.springframework.data.r2dbc.testing.R2dbcIntegrationTestSupport;
|
||||
import org.springframework.data.relational.core.conversion.BasicRelationalConverter;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
@@ -47,34 +48,23 @@ import org.springframework.data.relational.core.mapping.Table;
|
||||
import org.springframework.data.relational.repository.query.RelationalEntityInformation;
|
||||
import org.springframework.data.relational.repository.support.MappingRelationalEntityInformation;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link SimpleR2dbcRepository}.
|
||||
* Abstract integration tests for {@link SimpleR2dbcRepository} to be ran against various databases.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
public class SimpleR2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestSupport {
|
||||
public abstract class AbstractSimpleR2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestSupport {
|
||||
|
||||
@Autowired private DatabaseClient databaseClient;
|
||||
|
||||
@Autowired private RelationalMappingContext mappingContext;
|
||||
|
||||
@Autowired private ReactiveDataAccessStrategy strategy;
|
||||
|
||||
private SimpleR2dbcRepository<LegoSet, Integer> repository;
|
||||
private JdbcTemplate jdbc;
|
||||
|
||||
@Configuration
|
||||
static class IntegrationTestConfiguration extends AbstractR2dbcConfiguration {
|
||||
|
||||
@Override
|
||||
public ConnectionFactory connectionFactory() {
|
||||
return createConnectionFactory();
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
|
||||
@@ -84,17 +74,35 @@ public class SimpleR2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestS
|
||||
(RelationalPersistentEntity<LegoSet>) mappingContext.getRequiredPersistentEntity(LegoSet.class));
|
||||
|
||||
this.repository = new SimpleR2dbcRepository<>(entityInformation, databaseClient,
|
||||
new MappingR2dbcConverter(new BasicRelationalConverter(mappingContext)));
|
||||
new MappingR2dbcConverter(new BasicRelationalConverter(mappingContext)), strategy);
|
||||
|
||||
this.jdbc = createJdbcTemplate(createDataSource());
|
||||
try {
|
||||
this.jdbc.execute("DROP TABLE legoset");
|
||||
} catch (DataAccessException e) {}
|
||||
|
||||
String tableToCreate = "CREATE TABLE IF NOT EXISTS repo_legoset (\n" + " id SERIAL PRIMARY KEY,\n"
|
||||
+ " name varchar(255) NOT NULL,\n" + " manual integer NULL\n" + ");";
|
||||
|
||||
this.jdbc.execute("DROP TABLE IF EXISTS repo_legoset");
|
||||
this.jdbc.execute(tableToCreate);
|
||||
this.jdbc.execute(getCreateTableStatement());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link DataSource} to be used in this test.
|
||||
*
|
||||
* @return the {@link DataSource} to be used in this test.
|
||||
*/
|
||||
protected abstract DataSource createDataSource();
|
||||
|
||||
/**
|
||||
* Returns the the CREATE TABLE statement for table {@code legoset} with the following three columns:
|
||||
* <ul>
|
||||
* <li>id integer (primary key), not null, auto-increment</li>
|
||||
* <li>name varchar(255), nullable</li>
|
||||
* <li>manual integer, nullable</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return the CREATE TABLE statement for table {@code legoset} with three columns.
|
||||
*/
|
||||
protected abstract String getCreateTableStatement();
|
||||
|
||||
@Test
|
||||
public void shouldSaveNewObject() {
|
||||
|
||||
@@ -107,16 +115,17 @@ public class SimpleR2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestS
|
||||
assertThat(actual.getId()).isNotNull();
|
||||
}).verifyComplete();
|
||||
|
||||
Map<String, Object> map = jdbc.queryForMap("SELECT * FROM repo_legoset");
|
||||
Map<String, Object> map = jdbc.queryForMap("SELECT * FROM legoset");
|
||||
assertThat(map).containsEntry("name", "SCHAUFELRADBAGGER").containsEntry("manual", 12).containsKey("id");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUpdateObject() {
|
||||
|
||||
jdbc.execute("INSERT INTO repo_legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 12)");
|
||||
Integer id = jdbc.queryForObject("SELECT id FROM legoset", Integer.class);
|
||||
|
||||
LegoSet legoSet = new LegoSet(42055, "SCHAUFELRADBAGGER", 12);
|
||||
LegoSet legoSet = new LegoSet(id, "SCHAUFELRADBAGGER", 12);
|
||||
legoSet.setManual(14);
|
||||
|
||||
repository.save(legoSet) //
|
||||
@@ -124,7 +133,7 @@ public class SimpleR2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestS
|
||||
.expectNextCount(1) //
|
||||
.verifyComplete();
|
||||
|
||||
Map<String, Object> map = jdbc.queryForMap("SELECT * FROM repo_legoset");
|
||||
Map<String, Object> map = jdbc.queryForMap("SELECT * FROM legoset");
|
||||
assertThat(map).containsEntry("name", "SCHAUFELRADBAGGER").containsEntry("manual", 14).containsKey("id");
|
||||
}
|
||||
|
||||
@@ -145,8 +154,8 @@ public class SimpleR2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestS
|
||||
.expectNext(15) //
|
||||
.verifyComplete();
|
||||
|
||||
Map<String, Object> map = jdbc.queryForMap("SELECT COUNT(*) FROM repo_legoset");
|
||||
assertThat(map).containsEntry("count", 4L);
|
||||
Integer count = jdbc.queryForObject("SELECT COUNT(*) FROM legoset", Integer.class);
|
||||
assertThat(count).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -160,20 +169,21 @@ public class SimpleR2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestS
|
||||
.expectNextCount(2) //
|
||||
.verifyComplete();
|
||||
|
||||
Map<String, Object> map = jdbc.queryForMap("SELECT COUNT(*) FROM repo_legoset");
|
||||
assertThat(map).containsEntry("count", 2L);
|
||||
Integer count = jdbc.queryForObject("SELECT COUNT(*) FROM legoset", Integer.class);
|
||||
assertThat(count).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindById() {
|
||||
|
||||
jdbc.execute("INSERT INTO repo_legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 12)");
|
||||
Integer id = jdbc.queryForObject("SELECT id FROM legoset", Integer.class);
|
||||
|
||||
repository.findById(42055) //
|
||||
repository.findById(id) //
|
||||
.as(StepVerifier::create) //
|
||||
.assertNext(actual -> {
|
||||
|
||||
assertThat(actual.getId()).isEqualTo(42055);
|
||||
assertThat(actual.getId()).isEqualTo(id);
|
||||
assertThat(actual.getName()).isEqualTo("SCHAUFELRADBAGGER");
|
||||
assertThat(actual.getManual()).isEqualTo(12);
|
||||
}).verifyComplete();
|
||||
@@ -182,9 +192,10 @@ public class SimpleR2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestS
|
||||
@Test
|
||||
public void shouldExistsById() {
|
||||
|
||||
jdbc.execute("INSERT INTO repo_legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 12)");
|
||||
Integer id = jdbc.queryForObject("SELECT id FROM legoset", Integer.class);
|
||||
|
||||
repository.existsById(42055) //
|
||||
repository.existsById(id) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(true)//
|
||||
.verifyComplete();
|
||||
@@ -198,9 +209,10 @@ public class SimpleR2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestS
|
||||
@Test
|
||||
public void shouldExistsByIdPublisher() {
|
||||
|
||||
jdbc.execute("INSERT INTO repo_legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 12)");
|
||||
Integer id = jdbc.queryForObject("SELECT id FROM legoset", Integer.class);
|
||||
|
||||
repository.existsById(Mono.just(42055)) //
|
||||
repository.existsById(Mono.just(id)) //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(true)//
|
||||
.verifyComplete();
|
||||
@@ -214,8 +226,8 @@ public class SimpleR2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestS
|
||||
@Test
|
||||
public void shouldFindByAll() {
|
||||
|
||||
jdbc.execute("INSERT INTO repo_legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO repo_legoset (id, name, manual) VALUES(42064, 'FORSCHUNGSSCHIFF', 13)");
|
||||
jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (name, manual) VALUES('FORSCHUNGSSCHIFF', 13)");
|
||||
|
||||
repository.findAll() //
|
||||
.map(LegoSet::getName) //
|
||||
@@ -230,10 +242,12 @@ public class SimpleR2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestS
|
||||
@Test
|
||||
public void shouldFindAllByIdUsingIterable() {
|
||||
|
||||
jdbc.execute("INSERT INTO repo_legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO repo_legoset (id, name, manual) VALUES(42064, 'FORSCHUNGSSCHIFF', 13)");
|
||||
jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (name, manual) VALUES('FORSCHUNGSSCHIFF', 13)");
|
||||
|
||||
repository.findAllById(Arrays.asList(42055, 42064)) //
|
||||
List<Integer> ids = jdbc.queryForList("SELECT id FROM legoset", Integer.class);
|
||||
|
||||
repository.findAllById(ids) //
|
||||
.map(LegoSet::getName) //
|
||||
.collectList() //
|
||||
.as(StepVerifier::create) //
|
||||
@@ -246,10 +260,12 @@ public class SimpleR2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestS
|
||||
@Test
|
||||
public void shouldFindAllByIdUsingPublisher() {
|
||||
|
||||
jdbc.execute("INSERT INTO repo_legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO repo_legoset (id, name, manual) VALUES(42064, 'FORSCHUNGSSCHIFF', 13)");
|
||||
jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (name, manual) VALUES('FORSCHUNGSSCHIFF', 13)");
|
||||
|
||||
repository.findAllById(Flux.just(42055, 42064)) //
|
||||
List<Integer> ids = jdbc.queryForList("SELECT id FROM legoset", Integer.class);
|
||||
|
||||
repository.findAllById(Flux.fromIterable(ids)) //
|
||||
.map(LegoSet::getName) //
|
||||
.collectList() //
|
||||
.as(StepVerifier::create) //
|
||||
@@ -267,8 +283,8 @@ public class SimpleR2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestS
|
||||
.expectNext(0L) //
|
||||
.verifyComplete();
|
||||
|
||||
jdbc.execute("INSERT INTO repo_legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO repo_legoset (id, name, manual) VALUES(42064, 'FORSCHUNGSSCHIFF', 13)");
|
||||
jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (name, manual) VALUES('FORSCHUNGSSCHIFF', 13)");
|
||||
|
||||
repository.count() //
|
||||
.as(StepVerifier::create) //
|
||||
@@ -279,76 +295,81 @@ public class SimpleR2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestS
|
||||
@Test
|
||||
public void shouldDeleteById() {
|
||||
|
||||
jdbc.execute("INSERT INTO repo_legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 12)");
|
||||
Integer id = jdbc.queryForObject("SELECT id FROM legoset", Integer.class);
|
||||
|
||||
repository.deleteById(42055) //
|
||||
repository.deleteById(id) //
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
|
||||
Map<String, Object> map = jdbc.queryForMap("SELECT COUNT(*) FROM repo_legoset");
|
||||
assertThat(map).containsEntry("count", 0L);
|
||||
Integer count = jdbc.queryForObject("SELECT COUNT(*) FROM legoset", Integer.class);
|
||||
assertThat(count).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDeleteByIdPublisher() {
|
||||
|
||||
jdbc.execute("INSERT INTO repo_legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 12)");
|
||||
Integer id = jdbc.queryForObject("SELECT id FROM legoset", Integer.class);
|
||||
|
||||
repository.deleteById(Mono.just(42055)) //
|
||||
repository.deleteById(Mono.just(id)) //
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
|
||||
Map<String, Object> map = jdbc.queryForMap("SELECT COUNT(*) FROM repo_legoset");
|
||||
assertThat(map).containsEntry("count", 0L);
|
||||
Integer count = jdbc.queryForObject("SELECT COUNT(*) FROM legoset", Integer.class);
|
||||
assertThat(count).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDelete() {
|
||||
|
||||
jdbc.execute("INSERT INTO repo_legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 12)");
|
||||
Integer id = jdbc.queryForObject("SELECT id FROM legoset", Integer.class);
|
||||
|
||||
LegoSet legoSet = new LegoSet(42055, "SCHAUFELRADBAGGER", 12);
|
||||
LegoSet legoSet = new LegoSet(id, "SCHAUFELRADBAGGER", 12);
|
||||
|
||||
repository.delete(legoSet) //
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
|
||||
Map<String, Object> map = jdbc.queryForMap("SELECT COUNT(*) FROM repo_legoset");
|
||||
assertThat(map).containsEntry("count", 0L);
|
||||
Integer count = jdbc.queryForObject("SELECT COUNT(*) FROM legoset", Integer.class);
|
||||
assertThat(count).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDeleteAllUsingIterable() {
|
||||
|
||||
jdbc.execute("INSERT INTO repo_legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 12)");
|
||||
Integer id = jdbc.queryForObject("SELECT id FROM legoset", Integer.class);
|
||||
|
||||
LegoSet legoSet = new LegoSet(42055, "SCHAUFELRADBAGGER", 12);
|
||||
LegoSet legoSet = new LegoSet(id, "SCHAUFELRADBAGGER", 12);
|
||||
|
||||
repository.deleteAll(Collections.singletonList(legoSet)) //
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
|
||||
Map<String, Object> map = jdbc.queryForMap("SELECT COUNT(*) FROM repo_legoset");
|
||||
assertThat(map).containsEntry("count", 0L);
|
||||
Integer count = jdbc.queryForObject("SELECT COUNT(*) FROM legoset", Integer.class);
|
||||
assertThat(count).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDeleteAllUsingPublisher() {
|
||||
|
||||
jdbc.execute("INSERT INTO repo_legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
|
||||
jdbc.execute("INSERT INTO legoset (name, manual) VALUES('SCHAUFELRADBAGGER', 12)");
|
||||
Integer id = jdbc.queryForObject("SELECT id FROM legoset", Integer.class);
|
||||
|
||||
LegoSet legoSet = new LegoSet(42055, "SCHAUFELRADBAGGER", 12);
|
||||
LegoSet legoSet = new LegoSet(id, "SCHAUFELRADBAGGER", 12);
|
||||
|
||||
repository.deleteAll(Mono.just(legoSet)) //
|
||||
.as(StepVerifier::create) //
|
||||
.verifyComplete();
|
||||
|
||||
Map<String, Object> map = jdbc.queryForMap("SELECT COUNT(*) FROM repo_legoset");
|
||||
assertThat(map).containsEntry("count", 0L);
|
||||
Integer count = jdbc.queryForObject("SELECT COUNT(*) FROM legoset", Integer.class);
|
||||
assertThat(count).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Data
|
||||
@Table("repo_legoset")
|
||||
@Table("legoset")
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
static class LegoSet {
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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.repository.support;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration;
|
||||
import org.springframework.data.r2dbc.testing.ExternalDatabase;
|
||||
import org.springframework.data.r2dbc.testing.PostgresTestSupport;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link SimpleR2dbcRepository} against Postgres.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
public class PostgresSimpleR2dbcRepositoryIntegrationTests extends AbstractSimpleR2dbcRepositoryIntegrationTests {
|
||||
|
||||
@ClassRule public static final ExternalDatabase database = PostgresTestSupport.database();
|
||||
|
||||
@Configuration
|
||||
static class IntegrationTestConfiguration extends AbstractR2dbcConfiguration {
|
||||
|
||||
@Override
|
||||
public ConnectionFactory connectionFactory() {
|
||||
return PostgresTestSupport.createConnectionFactory(database);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataSource createDataSource() {
|
||||
return PostgresTestSupport.createDataSource(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCreateTableStatement() {
|
||||
return PostgresTestSupport.CREATE_TABLE_LEGOSET_WITH_ID_GENERATION;
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.r2dbc.function.DatabaseClient;
|
||||
import org.springframework.data.r2dbc.function.ReactiveDataAccessStrategy;
|
||||
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
|
||||
import org.springframework.data.relational.repository.query.RelationalEntityInformation;
|
||||
import org.springframework.data.relational.repository.support.MappingRelationalEntityInformation;
|
||||
@@ -41,6 +42,7 @@ public class R2dbcRepositoryFactoryUnitTests {
|
||||
@Mock DatabaseClient databaseClient;
|
||||
@Mock @SuppressWarnings("rawtypes") MappingContext mappingContext;
|
||||
@Mock @SuppressWarnings("rawtypes") RelationalPersistentEntity entity;
|
||||
@Mock ReactiveDataAccessStrategy dataAccessStrategy;
|
||||
|
||||
@Before
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -52,7 +54,7 @@ public class R2dbcRepositoryFactoryUnitTests {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void usesMappingRelationalEntityInformationIfMappingContextSet() {
|
||||
|
||||
R2dbcRepositoryFactory factory = new R2dbcRepositoryFactory(databaseClient, mappingContext);
|
||||
R2dbcRepositoryFactory factory = new R2dbcRepositoryFactory(databaseClient, mappingContext, dataAccessStrategy);
|
||||
RelationalEntityInformation<Person, Long> entityInformation = factory.getEntityInformation(Person.class);
|
||||
|
||||
assertThat(entityInformation).isInstanceOf(MappingRelationalEntityInformation.class);
|
||||
@@ -62,7 +64,7 @@ public class R2dbcRepositoryFactoryUnitTests {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void createsRepositoryWithIdTypeLong() {
|
||||
|
||||
R2dbcRepositoryFactory factory = new R2dbcRepositoryFactory(databaseClient, mappingContext);
|
||||
R2dbcRepositoryFactory factory = new R2dbcRepositoryFactory(databaseClient, mappingContext, dataAccessStrategy);
|
||||
MyPersonRepository repository = factory.getRepository(MyPersonRepository.class);
|
||||
|
||||
assertThat(repository).isNotNull();
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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.repository.support;
|
||||
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration;
|
||||
import org.springframework.data.r2dbc.testing.ExternalDatabase;
|
||||
import org.springframework.data.r2dbc.testing.SqlServerTestSupport;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link SimpleR2dbcRepository} against Microsoft SQL Server.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration
|
||||
public class SqlServerSimpleR2dbcRepositoryIntegrationTests extends AbstractSimpleR2dbcRepositoryIntegrationTests {
|
||||
|
||||
@ClassRule public static final ExternalDatabase database = SqlServerTestSupport.database();
|
||||
|
||||
@Configuration
|
||||
static class IntegrationTestConfiguration extends AbstractR2dbcConfiguration {
|
||||
|
||||
@Override
|
||||
public ConnectionFactory connectionFactory() {
|
||||
return SqlServerTestSupport.createConnectionFactory(database);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataSource createDataSource() {
|
||||
return SqlServerTestSupport.createDataSource(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCreateTableStatement() {
|
||||
return SqlServerTestSupport.CREATE_TABLE_LEGOSET_WITH_ID_GENERATION;
|
||||
}
|
||||
}
|
||||
@@ -57,12 +57,11 @@ public abstract class ExternalDatabase extends ExternalResource {
|
||||
protected void before() {
|
||||
|
||||
try (Socket socket = new Socket()) {
|
||||
;
|
||||
socket.connect(new InetSocketAddress(getHostname(), getPort()), Math.toIntExact(TimeUnit.SECONDS.toMillis(5)));
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new AssumptionViolatedException(
|
||||
String.format("Cannot connect to %s:%d. Skiping tests.", getHostname(), getPort()));
|
||||
String.format("Cannot connect to %s:%d. Skipping tests.", getHostname(), getPort()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package org.springframework.data.r2dbc.testing;
|
||||
|
||||
import io.r2dbc.postgresql.PostgresqlConnectionConfiguration;
|
||||
import io.r2dbc.postgresql.PostgresqlConnectionFactory;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.postgresql.ds.PGSimpleDataSource;
|
||||
import org.springframework.data.r2dbc.testing.ExternalDatabase.ProvidedDatabase;
|
||||
|
||||
/**
|
||||
* Utility class for testing against Postgres.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class PostgresTestSupport {
|
||||
|
||||
public static String CREATE_TABLE_LEGOSET = "CREATE TABLE legoset (\n" //
|
||||
+ " id integer CONSTRAINT id PRIMARY KEY,\n" //
|
||||
+ " name varchar(255) NOT NULL,\n" //
|
||||
+ " manual integer NULL\n" //
|
||||
+ ");";
|
||||
|
||||
public static String CREATE_TABLE_LEGOSET_WITH_ID_GENERATION = "CREATE TABLE legoset (\n" //
|
||||
+ " id serial CONSTRAINT id PRIMARY KEY,\n" //
|
||||
+ " name varchar(255) NOT NULL,\n" //
|
||||
+ " manual integer NULL\n" //
|
||||
+ ");";
|
||||
|
||||
public static String INSERT_INTO_LEGOSET = "INSERT INTO legoset (id, name, manual) VALUES($1, $2, $3)";
|
||||
|
||||
/**
|
||||
* Returns a locally provided database at {@code postgres:@localhost:5432/postgres}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static ExternalDatabase database() {
|
||||
return local();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a locally provided database at {@code postgres:@localhost:5432/postgres}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private static ExternalDatabase local() {
|
||||
return ProvidedDatabase.builder().hostname("localhost").port(5432).database("postgres").username("postgres")
|
||||
.password("").build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConnectionFactory} configured from the {@link ExternalDatabase}..
|
||||
*/
|
||||
public static ConnectionFactory createConnectionFactory(ExternalDatabase database) {
|
||||
return new PostgresqlConnectionFactory(PostgresqlConnectionConfiguration.builder().host(database.getHostname())
|
||||
.database(database.getDatabase()).username(database.getUsername()).password(database.getPassword()).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link DataSource} configured from the {@link ExternalDatabase}.
|
||||
*/
|
||||
public static DataSource createDataSource(ExternalDatabase database) {
|
||||
|
||||
PGSimpleDataSource dataSource = new PGSimpleDataSource();
|
||||
|
||||
dataSource.setUser(database.getUsername());
|
||||
dataSource.setPassword(database.getPassword());
|
||||
dataSource.setDatabaseName(database.getDatabase());
|
||||
dataSource.setServerName(database.getHostname());
|
||||
dataSource.setPortNumber(database.getPort());
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
}
|
||||
@@ -15,15 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.r2dbc.testing;
|
||||
|
||||
import io.r2dbc.postgresql.PostgresqlConnectionConfiguration;
|
||||
import io.r2dbc.postgresql.PostgresqlConnectionFactory;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.postgresql.ds.PGSimpleDataSource;
|
||||
import org.springframework.data.r2dbc.testing.ExternalDatabase.ProvidedDatabase;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
/**
|
||||
@@ -33,34 +26,6 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
||||
*/
|
||||
public abstract class R2dbcIntegrationTestSupport {
|
||||
|
||||
/**
|
||||
* Local test database at {@code postgres:@localhost:5432/postgres}.
|
||||
*/
|
||||
@ClassRule public static final ExternalDatabase database = ProvidedDatabase.builder().hostname("localhost").port(5432)
|
||||
.database("postgres").username("postgres").password("").build();
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConnectionFactory} configured from the {@link ExternalDatabase}..
|
||||
*/
|
||||
protected static ConnectionFactory createConnectionFactory() {
|
||||
return new PostgresqlConnectionFactory(PostgresqlConnectionConfiguration.builder().host(database.getHostname())
|
||||
.database(database.getDatabase()).username(database.getUsername()).password(database.getPassword()).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link DataSource} configured from the {@link ExternalDatabase}.
|
||||
*/
|
||||
protected static DataSource createDataSource() {
|
||||
|
||||
PGSimpleDataSource dataSource = new PGSimpleDataSource();
|
||||
dataSource.setUser(database.getUsername());
|
||||
dataSource.setPassword(database.getPassword());
|
||||
dataSource.setDatabaseName(database.getDatabase());
|
||||
dataSource.setServerName(database.getHostname());
|
||||
dataSource.setPortNumber(database.getPort());
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link JdbcTemplate} for a {@link DataSource}.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package org.springframework.data.r2dbc.testing;
|
||||
|
||||
import io.r2dbc.mssql.MssqlConnectionConfiguration;
|
||||
import io.r2dbc.mssql.MssqlConnectionFactory;
|
||||
import io.r2dbc.spi.ConnectionFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.data.r2dbc.testing.ExternalDatabase.ProvidedDatabase;
|
||||
|
||||
import com.microsoft.sqlserver.jdbc.SQLServerDataSource;
|
||||
|
||||
/**
|
||||
* Utility class for testing against Microsoft SQL Server.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class SqlServerTestSupport {
|
||||
|
||||
public static String CREATE_TABLE_LEGOSET = "CREATE TABLE legoset (\n" //
|
||||
+ " id integer PRIMARY KEY,\n" //
|
||||
+ " name varchar(255) NOT NULL,\n" //
|
||||
+ " manual integer NULL\n" //
|
||||
+ ");";
|
||||
|
||||
public static String CREATE_TABLE_LEGOSET_WITH_ID_GENERATION = "CREATE TABLE legoset (\n" //
|
||||
+ " id integer IDENTITY(1,1) PRIMARY KEY,\n" //
|
||||
+ " name varchar(255) NOT NULL,\n" //
|
||||
+ " manual integer NULL\n" //
|
||||
+ ");";
|
||||
|
||||
public static String INSERT_INTO_LEGOSET = "INSERT INTO legoset (id, name, manual) VALUES(@P0, @P1, @P3)";
|
||||
|
||||
/**
|
||||
* Returns a locally provided database at {@code sqlserver:@localhost:1433/master}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static ExternalDatabase database() {
|
||||
return local();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a locally provided database at {@code postgres:@localhost:5432/postgres}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private static ExternalDatabase local() {
|
||||
return ProvidedDatabase.builder().hostname("localhost").port(1433).database("master").username("sa")
|
||||
.password("my1.password").build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConnectionFactory} configured from the {@link ExternalDatabase}..
|
||||
*/
|
||||
public static ConnectionFactory createConnectionFactory(ExternalDatabase database) {
|
||||
return new MssqlConnectionFactory(MssqlConnectionConfiguration.builder().host(database.getHostname()) //
|
||||
.database(database.getDatabase()) //
|
||||
.username(database.getUsername()) //
|
||||
.password(database.getPassword()) //
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link DataSource} configured from the {@link ExternalDatabase}.
|
||||
*/
|
||||
public static DataSource createDataSource(ExternalDatabase database) {
|
||||
|
||||
SQLServerDataSource dataSource = new SQLServerDataSource();
|
||||
|
||||
dataSource.setUser(database.getUsername());
|
||||
dataSource.setPassword(database.getPassword());
|
||||
dataSource.setDatabaseName(database.getDatabase());
|
||||
dataSource.setServerName(database.getHostname());
|
||||
dataSource.setPortNumber(database.getPort());
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user