diff --git a/pom.xml b/pom.xml index b21fbb2d..12251f81 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ 0.1.4 42.2.5 8.0.21 - 0.8.5.RELEASE + 0.9.1.RELEASE 7.1.2.jre8-preview 2.5.4 Borca-RELEASE @@ -224,7 +224,7 @@ - io.r2dbc + org.postgresql r2dbc-postgresql true @@ -241,12 +241,6 @@ test - - dev.miku - r2dbc-mysql - test - - org.mariadb r2dbc-mariadb diff --git a/src/test/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategyTestSupport.java b/src/test/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategyTestSupport.java index 849f8c95..2b4c73f6 100644 --- a/src/test/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategyTestSupport.java +++ b/src/test/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategyTestSupport.java @@ -18,6 +18,8 @@ package org.springframework.data.r2dbc.core; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import io.r2dbc.spi.Parameters; +import io.r2dbc.spi.R2dbcType; import io.r2dbc.spi.Row; import io.r2dbc.spi.RowMetadata; import io.r2dbc.spi.test.MockColumnMetadata; @@ -198,7 +200,7 @@ public abstract class ReactiveDataAccessStrategyTestSupport { ReactiveDataAccessStrategy strategy = getStrategy(); Row rowMock = mock(Row.class); RowMetadata metadataMock = MockRowMetadata.builder() - .columnMetadata(MockColumnMetadata.builder().name(fieldname).build()).build(); + .columnMetadata(MockColumnMetadata.builder().name(fieldname).type(Parameters.in(testValue.getClass()).getType()).build()).build(); PrimitiveTypes toSave = new PrimitiveTypes(); setter.accept(toSave, testValue); diff --git a/src/test/java/org/springframework/data/r2dbc/dialect/DialectResolverUnitTests.java b/src/test/java/org/springframework/data/r2dbc/dialect/DialectResolverUnitTests.java index bcf15d67..c26fb49e 100644 --- a/src/test/java/org/springframework/data/r2dbc/dialect/DialectResolverUnitTests.java +++ b/src/test/java/org/springframework/data/r2dbc/dialect/DialectResolverUnitTests.java @@ -2,8 +2,6 @@ package org.springframework.data.r2dbc.dialect; import static org.assertj.core.api.Assertions.*; -import dev.miku.r2dbc.mysql.MySqlConnectionConfiguration; -import dev.miku.r2dbc.mysql.MySqlConnectionFactory; import io.r2dbc.h2.H2ConnectionConfiguration; import io.r2dbc.h2.H2ConnectionFactory; import io.r2dbc.mssql.MssqlConnectionConfiguration; @@ -41,13 +39,10 @@ public class DialectResolverUnitTests { MssqlConnectionFactory mssql = new MssqlConnectionFactory(MssqlConnectionConfiguration.builder().host("localhost") .database("foo").username("bar").password("password").build()); H2ConnectionFactory h2 = new H2ConnectionFactory(H2ConnectionConfiguration.builder().inMemory("mem").build()); - MySqlConnectionFactory mysql = MySqlConnectionFactory - .from(MySqlConnectionConfiguration.builder().host("localhost").username("mysql").build()); 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 diff --git a/src/test/java/org/springframework/data/r2dbc/repository/AbstractR2dbcRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/r2dbc/repository/AbstractR2dbcRepositoryIntegrationTests.java index 2cc6b694..63bb91d8 100644 --- a/src/test/java/org/springframework/data/r2dbc/repository/AbstractR2dbcRepositoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/r2dbc/repository/AbstractR2dbcRepositoryIntegrationTests.java @@ -24,6 +24,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.Value; import reactor.core.publisher.Flux; +import reactor.core.publisher.Hooks; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -60,6 +61,10 @@ import org.springframework.transaction.reactive.TransactionalOperator; */ public abstract class AbstractR2dbcRepositoryIntegrationTests extends R2dbcIntegrationTestSupport { + static { + Hooks.onOperatorDebug(); + } + @Autowired private LegoSetRepository repository; @Autowired private ConnectionFactory connectionFactory; protected JdbcTemplate jdbc; diff --git a/src/test/java/org/springframework/data/r2dbc/repository/MySqlR2dbcRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/r2dbc/repository/MySqlR2dbcRepositoryIntegrationTests.java deleted file mode 100644 index fe79851d..00000000 --- a/src/test/java/org/springframework/data/r2dbc/repository/MySqlR2dbcRepositoryIntegrationTests.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2019-2021 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 lombok.AllArgsConstructor; -import lombok.Data; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.time.LocalDateTime; - -import javax.sql.DataSource; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.extension.RegisterExtension; - -import org.springframework.beans.factory.annotation.Autowired; -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.dao.DataAccessException; -import org.springframework.data.annotation.Id; -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.MySqlTestSupport; -import org.springframework.data.repository.reactive.ReactiveCrudRepository; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -/** - * Integration tests for {@link LegoSetRepository} using {@link R2dbcRepositoryFactory} against MySQL. - * - * @author Mark Paluch - */ -@ExtendWith(SpringExtension.class) -@ContextConfiguration -public class MySqlR2dbcRepositoryIntegrationTests extends AbstractR2dbcRepositoryIntegrationTests { - - @RegisterExtension public static final ExternalDatabase database = MySqlTestSupport.database(); - - @Autowired DateTestsRepository dateTestsRepository; - - @Configuration - @EnableR2dbcRepositories(considerNestedRepositories = true, - includeFilters = { @Filter(classes = MySqlLegoSetRepository.class, type = FilterType.ASSIGNABLE_TYPE), - @Filter(classes = DateTestsRepository.class, type = FilterType.ASSIGNABLE_TYPE) }) - static class IntegrationTestConfiguration extends AbstractR2dbcConfiguration { - - @Bean - @Override - public ConnectionFactory connectionFactory() { - return MySqlTestSupport.createConnectionFactory(database); - } - } - - @Override - protected DataSource createDataSource() { - return MySqlTestSupport.createDataSource(database); - } - - @Override - protected ConnectionFactory createConnectionFactory() { - return MySqlTestSupport.createConnectionFactory(database); - } - - @Override - protected String getCreateTableStatement() { - return MySqlTestSupport.CREATE_TABLE_LEGOSET_WITH_ID_GENERATION; - } - - @Override - protected Class getRepositoryInterfaceType() { - return MySqlLegoSetRepository.class; - } - - @Test - public void shouldUserJsr310Types() { - - JdbcTemplate jdbcTemplate = createJdbcTemplate(createDataSource()); - - try { - jdbcTemplate.execute("DROP TABLE date_tests"); - } catch (DataAccessException e) {} - - jdbcTemplate.execute("CREATE TABLE date_tests (id int, created_timestamp TIMESTAMP, created_date datetime);"); - - dateTestsRepository.save(new DateTests(null, LocalDateTime.now(), LocalDateTime.now())).as(StepVerifier::create) - .expectNextCount(1).verifyComplete(); - } - - @Data - @AllArgsConstructor - static class DateTests { - @Id Integer id; - LocalDateTime createdTimestamp; - LocalDateTime createdDate; - } - - interface MySqlLegoSetRepository extends LegoSetRepository { - - @Override - @Query("SELECT name FROM legoset") - Flux findAsProjection(); - - @Override - @Query("SELECT * FROM legoset WHERE manual = :manual") - Mono findByManual(int manual); - - @Override - @Query("SELECT id FROM legoset") - Flux findAllIds(); - } - - interface DateTestsRepository extends ReactiveCrudRepository { - - } -} diff --git a/src/test/java/org/springframework/data/r2dbc/repository/MySqlR2dbcRepositoryWithMixedCaseNamesIntegrationTests.java b/src/test/java/org/springframework/data/r2dbc/repository/MySqlR2dbcRepositoryWithMixedCaseNamesIntegrationTests.java deleted file mode 100644 index b8f4b14f..00000000 --- a/src/test/java/org/springframework/data/r2dbc/repository/MySqlR2dbcRepositoryWithMixedCaseNamesIntegrationTests.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2021 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 java.util.Optional; - -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.convert.R2dbcCustomConversions; -import org.springframework.data.r2dbc.mapping.R2dbcMappingContext; -import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; -import org.springframework.data.r2dbc.testing.ExternalDatabase; -import org.springframework.data.r2dbc.testing.MySqlTestSupport; -import org.springframework.data.relational.core.mapping.NamingStrategy; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -/** - * Integration tests for {@link LegoSetRepository} with table and column names that contain upper and lower case - * characters against MySql. - * - * @author Jens Schauder - */ -@ExtendWith(SpringExtension.class) -@ContextConfiguration -public class MySqlR2dbcRepositoryWithMixedCaseNamesIntegrationTests - extends AbstractR2dbcRepositoryWithMixedCaseNamesIntegrationTests { - - @RegisterExtension public static final ExternalDatabase database = MySqlTestSupport.database(); - - @Configuration - @EnableR2dbcRepositories(considerNestedRepositories = true, - includeFilters = @Filter(classes = { LegoSetRepository.class }, type = FilterType.ASSIGNABLE_TYPE)) - static class IntegrationTestConfiguration extends AbstractR2dbcConfiguration { - - @Bean - @Override - public ConnectionFactory connectionFactory() { - return MySqlTestSupport.createConnectionFactory(database); - } - - @Override - public R2dbcMappingContext r2dbcMappingContext(Optional namingStrategy, - R2dbcCustomConversions r2dbcCustomConversions) { - - R2dbcMappingContext r2dbcMappingContext = super.r2dbcMappingContext(namingStrategy, r2dbcCustomConversions); - r2dbcMappingContext.setForceQuote(true); - - return r2dbcMappingContext; - } - } - - @Override - protected DataSource createDataSource() { - return MySqlTestSupport.createDataSource(database); - } - - @Override - protected ConnectionFactory createConnectionFactory() { - return MySqlTestSupport.createConnectionFactory(database); - } - - @Override - protected String getCreateTableStatement() { - return MySqlTestSupport.CREATE_TABLE_LEGOSET_WITH_MIXED_CASE_NAMES; - } - - @Override - protected String getDropTableStatement() { - return MySqlTestSupport.DROP_TABLE_LEGOSET_WITH_MIXED_CASE_NAMES; - } -} diff --git a/src/test/java/org/springframework/data/r2dbc/testing/MySqlTestSupport.java b/src/test/java/org/springframework/data/r2dbc/testing/MySqlTestSupport.java deleted file mode 100644 index 24896d98..00000000 --- a/src/test/java/org/springframework/data/r2dbc/testing/MySqlTestSupport.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2019-2021 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 dev.miku.r2dbc.mysql.MySqlConnectionFactoryProvider; -import io.r2dbc.spi.ConnectionFactory; -import io.r2dbc.spi.ConnectionFactoryOptions; - -import java.util.function.Supplier; -import java.util.stream.Stream; - -import javax.sql.DataSource; - -import org.springframework.data.r2dbc.testing.ExternalDatabase.ProvidedDatabase; - -import org.testcontainers.containers.MySQLContainer; - -import com.mysql.cj.jdbc.MysqlDataSource; - -/** - * Utility class for testing against MySQL. - * - * @author Mark Paluch - * @author Bogdan Ilchyshyn - * @author Jens Schauder - */ -public class MySqlTestSupport { - - private static ExternalDatabase testContainerDatabase; - - public static String CREATE_TABLE_LEGOSET = "CREATE TABLE legoset (\n" // - + " id integer PRIMARY KEY,\n" // - + " version integer NULL,\n" // - + " name varchar(255) NOT NULL,\n" // - + " manual integer NULL\n," // - + " cert varbinary(255) NULL\n" // - + ") ENGINE=InnoDB;"; - - public static String CREATE_TABLE_LEGOSET_WITH_ID_GENERATION = "CREATE TABLE legoset (\n" // - + " id integer AUTO_INCREMENT PRIMARY KEY,\n" // - + " version integer NULL,\n" // - + " name varchar(255) NOT NULL,\n" // - + " flag boolean 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 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( // - MySqlTestSupport::local, // - MySqlTestSupport::testContainer // - ); - } else { - - return getFirstWorkingDatabase( // - MySqlTestSupport::testContainer, // - MySqlTestSupport::local // - ); - } - } - - @SafeVarargs - private static ExternalDatabase getFirstWorkingDatabase(Supplier... 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:mysql://localhost:3306/mysql?allowPublicKeyRetrieval=true") // - .build(); - } - - /** - * Returns a database provided via Testcontainers. - */ - private static ExternalDatabase testContainer() { - - if (testContainerDatabase == null) { - - try { - MySQLContainer container = new MySQLContainer("mysql:8.0.24"); - container.start(); - - testContainerDatabase = ProvidedDatabase.builder(container) // - .database(container.getDatabaseName()) // - .username("root") // - .build(); - } catch (IllegalStateException ise) { - // docker not available. - testContainerDatabase = ExternalDatabase.unavailable(); - } - } - - return testContainerDatabase; - } - - /** - * Creates a new R2DBC MySQL {@link ConnectionFactory} configured from the {@link ExternalDatabase}. - */ - public static ConnectionFactory createConnectionFactory(ExternalDatabase database) { - - ConnectionFactoryOptions options = ConnectionUtils.createOptions("mysql", database); - return new MySqlConnectionFactoryProvider().create(options); - } - - /** - * Creates a new {@link DataSource} configured from the {@link ExternalDatabase}. - */ - public static DataSource createDataSource(ExternalDatabase database) { - - MysqlDataSource dataSource = new MysqlDataSource(); - - dataSource.setUser(database.getUsername()); - dataSource.setPassword(database.getPassword()); - dataSource.setURL(database.getJdbcUrl() + "?useSSL=false&allowPublicKeyRetrieval=true"); - - return dataSource; - } -} diff --git a/src/test/java/org/springframework/data/r2dbc/testing/StatementRecorder.java b/src/test/java/org/springframework/data/r2dbc/testing/StatementRecorder.java index 46618378..abe098c1 100644 --- a/src/test/java/org/springframework/data/r2dbc/testing/StatementRecorder.java +++ b/src/test/java/org/springframework/data/r2dbc/testing/StatementRecorder.java @@ -23,10 +23,12 @@ import io.r2dbc.spi.ConnectionMetadata; import io.r2dbc.spi.IsolationLevel; import io.r2dbc.spi.Result; import io.r2dbc.spi.Statement; +import io.r2dbc.spi.TransactionDefinition; import io.r2dbc.spi.ValidationDepth; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; @@ -156,11 +158,17 @@ public class StatementRecorder implements ConnectionFactory { } class RecorderConnection implements Connection { + @Override public Publisher beginTransaction() { return createStatement("BEGIN").execute().then(); } + @Override + public Publisher beginTransaction(TransactionDefinition definition) { + return createStatement("BEGIN " + definition).execute().then(); + } + @Override public Publisher close() { return createStatement("CLOSE").execute().then(); @@ -238,6 +246,16 @@ public class StatementRecorder implements ConnectionFactory { return createStatement("SET AUTOCOMMIT " + autoCommit).execute().then(); } + @Override + public Publisher setLockWaitTimeout(Duration timeout) { + return createStatement("SET LOCK WAIT TIMEOUT " + timeout).execute().then(); + } + + @Override + public Publisher setStatementTimeout(Duration timeout) { + return createStatement("SET STATEMENT TIMEOUT " + timeout).execute().then(); + } + @Override public Publisher setTransactionIsolationLevel(IsolationLevel isolationLevel) { return createStatement("SET TRANSACTION ISOLATION LEVEL " + isolationLevel.asSql()).execute().then(); @@ -307,6 +325,16 @@ public class StatementRecorder implements ConnectionFactory { public Flux execute() { return Flux.fromIterable(results).doOnSubscribe(subscription -> executedStatements.add(this)); } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer(); + sb.append(getClass().getSimpleName()); + sb.append(" [sql='").append(sql).append('\''); + sb.append(", bindings=").append(bindings); + sb.append(']'); + return sb.toString(); + } } } diff --git a/src/test/resources/org/springframework/data/r2dbc/connectionfactory/init/users-schema-h2.sql b/src/test/resources/org/springframework/data/r2dbc/connectionfactory/init/users-schema-h2.sql new file mode 100644 index 00000000..8bfe3026 --- /dev/null +++ b/src/test/resources/org/springframework/data/r2dbc/connectionfactory/init/users-schema-h2.sql @@ -0,0 +1,8 @@ +DROP TABLE users IF EXISTS; + +CREATE TABLE users +( + id INTEGER NOT NULL AUTO_INCREMENT, + first_name VARCHAR(50) NOT NULL, + last_name VARCHAR(50) NOT NULL +);