#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:
Mark Paluch
2018-11-21 13:48:07 +01:00
parent dc3a0d5fc4
commit 68f1e0b0b3
51 changed files with 2594 additions and 445 deletions

View File

@@ -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";
}
}
}

View File

@@ -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");

View File

@@ -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");

View File

@@ -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");
}
}

View File

@@ -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");
}
}

View File

@@ -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);
}

View File

@@ -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);
});

View File

@@ -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)");
}
}

View File

@@ -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() {}
}

View File

@@ -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();";
}
}

View File

@@ -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;
}
}

View File

@@ -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();";
}
}

View File

@@ -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 {

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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;
}
}

View File

@@ -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()));
}
}

View File

@@ -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;
}
}

View File

@@ -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}.
*/

View File

@@ -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;
}
}