#2 - Add DatabaseClient.

Provide DatabaseClient and add ExternalDatabase class to provide database connection details/encapsulate a dockerized testcontainer.
This commit is contained in:
Mark Paluch
2018-06-04 16:03:34 +02:00
parent 8cc1535d4d
commit c59e9a58eb
13 changed files with 2548 additions and 0 deletions

View File

@@ -0,0 +1,155 @@
/*
* 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.jdbc.core.function;
import static org.assertj.core.api.Assertions.*;
import io.r2dbc.postgresql.PostgresqlConnectionConfiguration;
import io.r2dbc.postgresql.PostgresqlConnectionFactory;
import io.r2dbc.spi.ConnectionFactory;
import lombok.Data;
import reactor.core.publisher.Hooks;
import reactor.test.StepVerifier;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.postgresql.ds.PGSimpleDataSource;
import org.springframework.data.jdbc.core.function.ExternalDatabase.ProvidedDatabase;
import org.springframework.data.jdbc.core.mapping.Table;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Integration tests for {@link DatabaseClient} against PostgreSQL.
*
* @author Mark Paluch
*/
public class DatabaseClientIntegrationTests {
/**
* 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();
private ConnectionFactory connectionFactory;
private JdbcTemplate jdbc;
@Before
public void before() {
Hooks.onOperatorDebug();
connectionFactory = new PostgresqlConnectionFactory(
PostgresqlConnectionConfiguration.builder().host(database.getHostname()).database(database.getDatabase())
.username(database.getUsername()).password(database.getPassword()).build());
PGSimpleDataSource dataSource = new PGSimpleDataSource();
dataSource.setUser(database.getUsername());
dataSource.setPassword(database.getPassword());
dataSource.setDatabaseName(database.getDatabase());
dataSource.setServerName(database.getHostname());
dataSource.setPortNumber(database.getPort());
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 = new JdbcTemplate(dataSource);
jdbc.execute(tableToCreate);
jdbc.execute("DELETE FROM legoset");
}
@Test
public void executeInsert() {
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
databaseClient.execute().sql("INSERT INTO legoset (id, name, manual) VALUES($1, $2, $3)") //
.bind(0, 42055) //
.bind(1, "SCHAUFELRADBAGGER") //
.bindNull("$3") //
.fetch().rowsUpdated() //
.as(StepVerifier::create) //
.expectNext(1) //
.verifyComplete();
assertThat(jdbc.queryForMap("SELECT id, name, manual FROM legoset")).containsEntry("id", 42055);
}
@Test
public void executeSelect() {
jdbc.execute("INSERT INTO legoset (id, name, manual) VALUES(42055, 'SCHAUFELRADBAGGER', 12)");
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() //
.as(StepVerifier::create) //
.consumeNextWith(actual -> {
assertThat(actual.getId()).isEqualTo(42055);
assertThat(actual.getName()).isEqualTo("SCHAUFELRADBAGGER");
assertThat(actual.getManual()).isEqualTo(12);
}).verifyComplete();
}
@Test
public void insert() {
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
databaseClient.insert().into("legoset")//
.value("id", 42055) //
.value("name", "SCHAUFELRADBAGGER") //
.nullValue("manual") //
.exchange() //
.flatMapMany(it -> it.extract((r, m) -> r.get("id", Integer.class)).all()).as(StepVerifier::create) //
.expectNext(42055).verifyComplete();
assertThat(jdbc.queryForMap("SELECT id, name, manual FROM legoset")).containsEntry("id", 42055);
}
@Test
public void insertTypedObject() {
LegoSet legoSet = new LegoSet();
legoSet.setId(42055);
legoSet.setName("SCHAUFELRADBAGGER");
legoSet.setManual(12);
DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
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();
assertThat(jdbc.queryForMap("SELECT id, name, manual FROM legoset")).containsEntry("id", 42055);
}
@Data
@Table("legoset")
static class LegoSet {
int id;
String name;
Integer manual;
}
}

View File

@@ -0,0 +1,126 @@
/*
* 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.jdbc.core.function;
import lombok.Builder;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.concurrent.TimeUnit;
import org.junit.AssumptionViolatedException;
import org.junit.rules.ExternalResource;
/**
* {@link ExternalResource} wrapper to encapsulate {@link ProvidedDatabase} and
* {@link org.testcontainers.containers.PostgreSQLContainer}.
*
* @author Mark Paluch
*/
public abstract class ExternalDatabase extends ExternalResource {
/**
* @return the post of the database service.
*/
public abstract int getPort();
/**
* @return hostname on which the database service runs.
*/
public abstract String getHostname();
/**
* @return name of the database.
*/
public abstract String getDatabase();
/**
* @return database user name.
*/
public abstract String getUsername();
@Override
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()));
}
}
/**
* @return password for the database user.
*/
public abstract String getPassword();
/**
* Provided (unmanaged resource) database connection coordinates.
*/
@Builder
static class ProvidedDatabase extends ExternalDatabase {
private final int port;
private final String hostname;
private final String database;
private final String username;
private final String password;
/* (non-Javadoc)
* @see org.springframework.data.jdbc.core.function.ExternalDatabase#getPort()
*/
@Override
public int getPort() {
return port;
}
/* (non-Javadoc)
* @see org.springframework.data.jdbc.core.function.ExternalDatabase#getHostname()
*/
@Override
public String getHostname() {
return hostname;
}
/* (non-Javadoc)
* @see org.springframework.data.jdbc.core.function.ExternalDatabase#getDatabase()
*/
@Override
public String getDatabase() {
return database;
}
/* (non-Javadoc)
* @see org.springframework.data.jdbc.core.function.ExternalDatabase#getUsername()
*/
@Override
public String getUsername() {
return username;
}
/* (non-Javadoc)
* @see org.springframework.data.jdbc.core.function.ExternalDatabase#getPassword()
*/
@Override
public String getPassword() {
return password;
}
}
}