#104 - Provide extension mechanism for R2dbcDialect resolution.

We now use Spring's spring.factories mechanism to register and lookup R2dbcDialectProvider implementations.
This allows a pluggable model for third party implementations to ship R2dbcDialect support that can be auto-discovered.

Original pull request: #127.
This commit is contained in:
Mark Paluch
2019-05-22 16:55:32 +02:00
parent 85dcf6b71d
commit 043e8a6855
11 changed files with 269 additions and 180 deletions

View File

@@ -1,56 +0,0 @@
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

@@ -0,0 +1,110 @@
package org.springframework.data.r2dbc.dialect;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
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 lombok.RequiredArgsConstructor;
import java.util.Optional;
import org.junit.Test;
import org.reactivestreams.Publisher;
import org.springframework.data.relational.core.dialect.LimitClause;
import org.springframework.data.relational.core.sql.render.SelectRenderContext;
import com.github.jasync.r2dbc.mysql.JasyncConnectionFactory;
import com.github.jasync.sql.db.mysql.pool.MySQLConnectionFactory;
/**
* Unit tests for {@link DialectResolver}.
*
* @author Mark Paluch
*/
public class DialectResolverUnitTests {
@Test // gh-20, gh-104
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());
JasyncConnectionFactory mysql = new JasyncConnectionFactory(mock(MySQLConnectionFactory.class));
assertThat(DialectResolver.getDialect(postgres)).isEqualTo(PostgresDialect.INSTANCE);
assertThat(DialectResolver.getDialect(mssql)).isEqualTo(SqlServerDialect.INSTANCE);
assertThat(DialectResolver.getDialect(h2)).isEqualTo(H2Dialect.INSTANCE);
assertThat(DialectResolver.getDialect(mysql)).isEqualTo(MySqlDialect.INSTANCE);
}
@Test // gh-20, gh-104
public void shouldNotResolveUnknownDatabase() {
assertThatThrownBy(() -> DialectResolver.getDialect(new ExternalConnectionFactory("unknown")))
.isInstanceOf(DialectResolver.NoDialectException.class);
}
@Test // gh-104
public void shouldResolveExternalDialect() {
assertThat(DialectResolver.getDialect(new ExternalConnectionFactory("external")))
.isEqualTo(ExternalDialect.INSTANCE);
}
@RequiredArgsConstructor
static class ExternalConnectionFactory implements ConnectionFactory {
private final String name;
@Override
public Publisher<? extends Connection> create() {
throw new UnsupportedOperationException();
}
@Override
public ConnectionFactoryMetadata getMetadata() {
return () -> this.name;
}
}
static class ExternalDialectProvider implements DialectResolver.R2dbcDialectProvider {
@Override
public Optional<R2dbcDialect> getDialect(ConnectionFactory connectionFactory) {
if (connectionFactory.getMetadata().getName().equals("external")) {
return Optional.of(ExternalDialect.INSTANCE);
}
return Optional.empty();
}
}
enum ExternalDialect implements R2dbcDialect {
INSTANCE;
@Override
public BindMarkersFactory getBindMarkersFactory() {
return null;
}
@Override
public LimitClause limit() {
return null;
}
@Override
public SelectRenderContext getSelectContext() {
return null;
}
}
}

View File

@@ -42,7 +42,8 @@ import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.r2dbc.convert.MappingR2dbcConverter;
import org.springframework.data.r2dbc.core.DefaultReactiveDataAccessStrategy;
import org.springframework.data.r2dbc.core.TransactionalDatabaseClient;
import org.springframework.data.r2dbc.dialect.Database;
import org.springframework.data.r2dbc.dialect.DialectResolver;
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
import org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactory;
import org.springframework.data.r2dbc.testing.R2dbcIntegrationTestSupport;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
@@ -172,9 +173,9 @@ public abstract class AbstractR2dbcRepositoryIntegrationTests extends R2dbcInteg
@Test
public void shouldInsertItemsTransactional() {
Database database = Database.findDatabase(createConnectionFactory()).get();
DefaultReactiveDataAccessStrategy dataAccessStrategy = new DefaultReactiveDataAccessStrategy(
database.defaultDialect(), new MappingR2dbcConverter(mappingContext));
R2dbcDialect dialect = DialectResolver.getDialect(createConnectionFactory());
DefaultReactiveDataAccessStrategy dataAccessStrategy = new DefaultReactiveDataAccessStrategy(dialect,
new MappingR2dbcConverter(mappingContext));
TransactionalDatabaseClient client = TransactionalDatabaseClient.builder()
.connectionFactory(createConnectionFactory()).dataAccessStrategy(dataAccessStrategy).build();

View File

@@ -0,0 +1 @@
org.springframework.data.r2dbc.dialect.DialectResolver$R2dbcDialectProvider=org.springframework.data.r2dbc.dialect.DialectResolverUnitTests.ExternalDialectProvider