Browse Source

Reinstate support for mariadb-r2dbc.

Closes #1364
pull/1453/head
Mark Paluch 3 years ago
parent
commit
7341a02345
No known key found for this signature in database
GPG Key ID: 4406B84C1661DCD1
  1. 15
      spring-data-r2dbc/pom.xml
  2. 4
      spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverter.java
  3. 14
      spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/RowMetadataUtils.java
  4. 6
      spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/dialect/DialectResolverUnitTests.java
  5. 95
      spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/MariaDbR2dbcRepositoryIntegrationTests.java
  6. 167
      spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/testing/MariaDbTestSupport.java

15
spring-data-r2dbc/pom.xml

@ -28,6 +28,7 @@
<degraph-check.version>0.1.4</degraph-check.version> <degraph-check.version>0.1.4</degraph-check.version>
<r2dbc-postgresql.version>1.0.1.RELEASE</r2dbc-postgresql.version> <r2dbc-postgresql.version>1.0.1.RELEASE</r2dbc-postgresql.version>
<r2dbc-h2.version>1.0.0.RELEASE</r2dbc-h2.version> <r2dbc-h2.version>1.0.0.RELEASE</r2dbc-h2.version>
<r2dbc-mariadb.version>1.1.3</r2dbc-mariadb.version>
<r2dbc-mssql.version>1.0.0.RELEASE</r2dbc-mssql.version> <r2dbc-mssql.version>1.0.0.RELEASE</r2dbc-mssql.version>
<oracle-r2dbc.version>1.0.0</oracle-r2dbc.version> <oracle-r2dbc.version>1.0.0</oracle-r2dbc.version>
<r2dbc-spi.version>1.0.0.RELEASE</r2dbc-spi.version> <r2dbc-spi.version>1.0.0.RELEASE</r2dbc-spi.version>
@ -190,6 +191,13 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>${mariadb-java-client.version}</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>com.oracle.database.jdbc</groupId> <groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc11</artifactId> <artifactId>ojdbc11</artifactId>
@ -213,6 +221,13 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.mariadb</groupId>
<artifactId>r2dbc-mariadb</artifactId>
<version>${r2dbc-mariadb.version}</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>io.r2dbc</groupId> <groupId>io.r2dbc</groupId>
<artifactId>r2dbc-mssql</artifactId> <artifactId>r2dbc-mssql</artifactId>

4
spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverter.java

@ -598,6 +598,10 @@ public class MappingR2dbcConverter extends BasicRelationalConverter implements R
return (row, metadata) -> { return (row, metadata) -> {
if (metadata == null) {
metadata = row.getMetadata();
}
PersistentPropertyAccessor<?> propertyAccessor = entity.getPropertyAccessor(object); PersistentPropertyAccessor<?> propertyAccessor = entity.getPropertyAccessor(object);
RelationalPersistentProperty idProperty = entity.getRequiredIdProperty(); RelationalPersistentProperty idProperty = entity.getRequiredIdProperty();

14
spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/RowMetadataUtils.java

@ -18,11 +18,6 @@ package org.springframework.data.r2dbc.convert;
import io.r2dbc.spi.ColumnMetadata; import io.r2dbc.spi.ColumnMetadata;
import io.r2dbc.spi.RowMetadata; import io.r2dbc.spi.RowMetadata;
import java.lang.reflect.Method;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
/** /**
* Utility methods for {@link io.r2dbc.spi.RowMetadata} * Utility methods for {@link io.r2dbc.spi.RowMetadata}
* *
@ -31,9 +26,6 @@ import org.springframework.util.ReflectionUtils;
*/ */
class RowMetadataUtils { class RowMetadataUtils {
private static final @Nullable Method getColumnMetadatas = ReflectionUtils.findMethod(RowMetadata.class,
"getColumnMetadatas");
/** /**
* Check whether the column {@code name} is contained in {@link RowMetadata}. The check happens case-insensitive. * Check whether the column {@code name} is contained in {@link RowMetadata}. The check happens case-insensitive.
* *
@ -63,12 +55,6 @@ class RowMetadataUtils {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static Iterable<? extends ColumnMetadata> getColumnMetadata(RowMetadata metadata) { public static Iterable<? extends ColumnMetadata> getColumnMetadata(RowMetadata metadata) {
if (getColumnMetadatas != null) {
// Return type of RowMetadata.getColumnMetadatas was updated with R2DBC 0.9.
return (Iterable<? extends ColumnMetadata>) ReflectionUtils.invokeMethod(getColumnMetadatas, metadata);
}
return metadata.getColumnMetadatas(); return metadata.getColumnMetadatas();
} }
} }

6
spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/dialect/DialectResolverUnitTests.java

@ -14,8 +14,9 @@ import lombok.RequiredArgsConstructor;
import java.util.Optional; import java.util.Optional;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mariadb.r2dbc.MariadbConnectionConfiguration;
import org.mariadb.r2dbc.MariadbConnectionFactory;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import org.springframework.data.relational.core.dialect.LimitClause; import org.springframework.data.relational.core.dialect.LimitClause;
import org.springframework.data.relational.core.dialect.LockClause; import org.springframework.data.relational.core.dialect.LockClause;
import org.springframework.data.relational.core.sql.LockOptions; import org.springframework.data.relational.core.sql.LockOptions;
@ -35,9 +36,12 @@ public class DialectResolverUnitTests {
PostgresqlConnectionFactory postgres = new PostgresqlConnectionFactory(PostgresqlConnectionConfiguration.builder() PostgresqlConnectionFactory postgres = new PostgresqlConnectionFactory(PostgresqlConnectionConfiguration.builder()
.host("localhost").database("foo").username("bar").password("password").build()); .host("localhost").database("foo").username("bar").password("password").build());
H2ConnectionFactory h2 = new H2ConnectionFactory(H2ConnectionConfiguration.builder().inMemory("mem").build()); H2ConnectionFactory h2 = new H2ConnectionFactory(H2ConnectionConfiguration.builder().inMemory("mem").build());
MariadbConnectionFactory mariadb = new MariadbConnectionFactory(
MariadbConnectionConfiguration.builder().socket("/foo").username("bar").build());
assertThat(DialectResolver.getDialect(postgres)).isEqualTo(PostgresDialect.INSTANCE); assertThat(DialectResolver.getDialect(postgres)).isEqualTo(PostgresDialect.INSTANCE);
assertThat(DialectResolver.getDialect(h2)).isEqualTo(H2Dialect.INSTANCE); assertThat(DialectResolver.getDialect(h2)).isEqualTo(H2Dialect.INSTANCE);
assertThat(DialectResolver.getDialect(mariadb)).isEqualTo(MySqlDialect.INSTANCE);
} }
@Test // gh-20, gh-104 @Test // gh-20, gh-104

95
spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/MariaDbR2dbcRepositoryIntegrationTests.java

@ -0,0 +1,95 @@
/*
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.r2dbc.repository;
import io.r2dbc.spi.ConnectionFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import javax.sql.DataSource;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.context.annotation.Bean;
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.support.R2dbcRepositoryFactory;
import org.springframework.data.r2dbc.testing.ExternalDatabase;
import org.springframework.data.r2dbc.testing.MariaDbTestSupport;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* Integration tests for {@link LegoSetRepository} using {@link R2dbcRepositoryFactory} against MariaDB.
*
* @author Mark Paluch
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration
public class MariaDbR2dbcRepositoryIntegrationTests extends AbstractR2dbcRepositoryIntegrationTests {
@RegisterExtension public static final ExternalDatabase database = MariaDbTestSupport.database();
@Configuration
@EnableR2dbcRepositories(considerNestedRepositories = true,
includeFilters = @Filter(classes = MySqlLegoSetRepository.class, type = FilterType.ASSIGNABLE_TYPE))
static class IntegrationTestConfiguration extends AbstractR2dbcConfiguration {
@Bean
@Override
public ConnectionFactory connectionFactory() {
return MariaDbTestSupport.createConnectionFactory(database);
}
}
@Override
protected DataSource createDataSource() {
return MariaDbTestSupport.createDataSource(database);
}
@Override
protected ConnectionFactory createConnectionFactory() {
return MariaDbTestSupport.createConnectionFactory(database);
}
@Override
protected String getCreateTableStatement() {
return MariaDbTestSupport.CREATE_TABLE_LEGOSET_WITH_ID_GENERATION;
}
@Override
protected Class<? extends LegoSetRepository> getRepositoryInterfaceType() {
return MySqlLegoSetRepository.class;
}
interface MySqlLegoSetRepository extends LegoSetRepository {
@Override
@Query("SELECT name FROM legoset")
Flux<Named> findAsProjection();
@Override
@Query("SELECT * FROM legoset WHERE manual = :manual")
Mono<LegoSet> findByManual(int manual);
@Override
@Query("SELECT id FROM legoset")
Flux<Integer> findAllIds();
}
}

167
spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/testing/MariaDbTestSupport.java

@ -0,0 +1,167 @@
/*
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.r2dbc.testing;
import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.ConnectionFactoryOptions;
import lombok.SneakyThrows;
import java.util.function.Supplier;
import java.util.stream.Stream;
import javax.sql.DataSource;
import org.mariadb.jdbc.MariaDbDataSource;
import org.mariadb.r2dbc.MariadbConnectionFactoryProvider;
import org.springframework.data.r2dbc.testing.ExternalDatabase.ProvidedDatabase;
import org.testcontainers.containers.MariaDBContainer;
import org.testcontainers.utility.DockerImageName;
/**
* Utility class for testing against MariaDB.
*
* @author Mark Paluch
* @author Jens Schauder
*/
public class MariaDbTestSupport {
private static ExternalDatabase testContainerDatabase;
public static final String CREATE_TABLE_LEGOSET = "CREATE TABLE legoset (\n" //
+ " id integer PRIMARY KEY,\n" //
+ " name varchar(255) NOT NULL,\n" //
+ " manual integer NULL\n," //
+ " cert varbinary(255) NULL\n" //
+ ") ENGINE=InnoDB;";
public static final String CREATE_TABLE_LEGOSET_WITH_ID_GENERATION = "CREATE TABLE legoset (\n" //
+ " id integer AUTO_INCREMENT PRIMARY KEY,\n" //
+ " name varchar(255) NOT NULL,\n" //
+ " flag boolean NOT NULL,\n" //
+ " manual integer NULL\n" //
+ ") ENGINE=InnoDB;";
public static final String CREATE_TABLE_LEGOSET_WITH_MIXED_CASE_NAMES = "CREATE TABLE `LegoSet` (\n" //
+ " `Id` integer AUTO_INCREMENT PRIMARY KEY,\n" //
+ " `Name` varchar(255) NOT NULL,\n" //
+ " `Manual` integer NULL\n" //
+ ") ENGINE=InnoDB;";
public static final String DROP_TABLE_LEGOSET_WITH_MIXED_CASE_NAMES = "DROP TABLE `LegoSet`";
/**
* Returns a database either hosted locally at {@code localhost:3306/mysql} or running inside Docker.
*
* @return information about the database. Guaranteed to be not {@literal null}.
*/
public static ExternalDatabase database() {
if (Boolean.getBoolean("spring.data.r2dbc.test.preferLocalDatabase")) {
return getFirstWorkingDatabase( //
MariaDbTestSupport::local, //
MariaDbTestSupport::testContainer //
);
} else {
return getFirstWorkingDatabase( //
MariaDbTestSupport::testContainer, //
MariaDbTestSupport::local //
);
}
}
@SafeVarargs
private static ExternalDatabase getFirstWorkingDatabase(Supplier<ExternalDatabase>... suppliers) {
return Stream.of(suppliers).map(Supplier::get) //
.filter(ExternalDatabase::checkValidity) //
.findFirst() //
.orElse(ExternalDatabase.unavailable());
}
/**
* Returns a locally provided database .
*/
private static ExternalDatabase local() {
return ProvidedDatabase.builder() //
.hostname("localhost") //
.port(3306) //
.database("mysql") //
.username("root") //
.password("my-secret-pw") //
.jdbcUrl("jdbc:mariadb://localhost:3306/mysql") //
.build();
}
/**
* Returns a database provided via Testcontainers.
*/
private static ExternalDatabase testContainer() {
if (testContainerDatabase == null) {
try {
String osArch = System.getProperty("os.arch");
DockerImageName armImageName = DockerImageName.parse("arm64v8/mariadb:10.3")
.asCompatibleSubstituteFor("mariadb");
DockerImageName mariadb = DockerImageName.parse("mariadb").withTag("10.3.6");
var container = new MariaDBContainer<>("aarch64".equals(osArch) ? armImageName : mariadb);
container.start();
testContainerDatabase = ProvidedDatabase.builder(container) //
.username("root") //
.database(container.getDatabaseName()) //
.build();
} catch (IllegalStateException ise) {
// docker not available.
testContainerDatabase = ExternalDatabase.unavailable();
}
}
return testContainerDatabase;
}
/**
* Creates a new R2DBC MariaDB {@link ConnectionFactory} configured from the {@link ExternalDatabase}.
*/
public static ConnectionFactory createConnectionFactory(ExternalDatabase database) {
ConnectionFactoryOptions options = ConnectionUtils.createOptions("mariadb", database);
return new MariadbConnectionFactoryProvider().create(options);
}
/**
* Creates a new {@link DataSource} configured from the {@link ExternalDatabase}.
*/
@SneakyThrows
public static DataSource createDataSource(ExternalDatabase database) {
MariaDbDataSource dataSource = new MariaDbDataSource();
dataSource.setUser(database.getUsername());
dataSource.setPassword(database.getPassword());
dataSource.setUrl(
String.format("jdbc:mariadb://%s:%d/%s?", database.getHostname(), database.getPort(), database.getDatabase()));
return dataSource;
}
}
Loading…
Cancel
Save