From 4eb92abd8f5453573006704807491d5bbbc9e187 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 8 Jun 2020 10:53:50 +0200 Subject: [PATCH] DATAJDBC-256 - Run integration tests with Oracle. An Oracle database is started in Docker using Testcontainers. Tests that don't work yet are skipped using the TestDatabaseFeatures which allows central control, which database supports which feature. A suitable oracle image is deployed on DockerHub as "springci/spring-data-oracle-xe-prebuild:18.4.0". The official Oracle docker images as described here https://github.com/oracle/docker-images/blob/master/OracleDatabase/SingleInstance/README.md are not suitable since it needs about 15min to start and also TestContainers seems to break it by trying to us it to early. The referenced image was created based on these instructions: https://github.com/oracle/docker-images/tree/master/OracleDatabase/SingleInstance/samples/prebuiltdb The following features don't yet work for Oracle: * Loading of date like properties. * Ids with custom names. * Array support. * Boolean properties. * BigDecimals and BigInteger is limited to what fits into Oracles NUMBER data type. * Entities that use a join during load time, i.e. with a one to one relationship. Original pull request: #232. --- pom.xml | 2 +- .../JdbcAggregateChangeExecutionContext.java | 1 + .../convert/DefaultDataAccessStrategy.java | 34 +- .../repository/config/DialectResolver.java | 4 + ...JdbcAggregateTemplateIntegrationTests.java | 95 ++--- ...gregateTemplateSchemaIntegrationTests.java | 12 +- ...itoryCustomConversionIntegrationTests.java | 9 +- ...dedNotInAggregateRootIntegrationTests.java | 80 +++-- ...EmbeddedWithReferenceIntegrationTests.java | 79 +++-- .../JdbcRepositoryIntegrationTests.java | 109 +++--- ...oryPropertyConversionIntegrationTests.java | 223 ++++++------ ...sitoryWithCollectionsIntegrationTests.java | 61 ++-- ...bcRepositoryWithListsIntegrationTests.java | 62 ++-- ...dbcRepositoryWithMapsIntegrationTests.java | 9 +- .../data/jdbc/testing/AssumeFeatureRule.java | 51 +++ .../testing/DatabaseProfileValueSource.java | 50 --- .../DatabaseProfileValueSourceUnitTests.java | 67 ---- .../testing/Db2DataSourceConfiguration.java | 1 + .../testing/MySqlDataSourceConfiguration.java | 1 + .../OracleDataSourceConfiguration.java | 41 ++- .../data/jdbc/testing/RequiredFeature.java | 27 ++ .../data/jdbc/testing/TestConfiguration.java | 6 + .../jdbc/testing/TestDatabaseFeatures.java | 140 ++++++++ .../src/test/resources/logback.xml | 4 +- ...gregateTemplateIntegrationTests-oracle.sql | 327 ++++++++++++++++++ ...eTemplateSchemaIntegrationTests-oracle.sql | 18 + ...dbcRepositoriesIntegrationTests-oracle.sql | 3 + ...toryConcurrencyIntegrationTests-oracle.sql | 14 + ...ustomConversionIntegrationTests-oracle.sql | 14 + ...beddedImmutableIntegrationTests-oracle.sql | 7 + ...ositoryEmbeddedIntegrationTests-oracle.sql | 9 + ...InAggregateRootIntegrationTests-oracle.sql | 12 + ...dWithCollectionIntegrationTests-oracle.sql | 17 + ...edWithReferenceIntegrationTests-oracle.sql | 16 + ...oryIdGenerationIntegrationTests-oracle.sql | 18 + .../JdbcRepositoryIntegrationTests-oracle.sql | 8 + ...PropertyConversionIntegrationTests-db2.sql | 3 +- ...pertyConversionIntegrationTests-oracle.sql | 12 + ...ultSetExtractorIntegrationTests-oracle.sql | 14 + ...WithCollectionsIntegrationTests-oracle.sql | 13 + ...sitoryWithListsIntegrationTests-oracle.sql | 14 + ...ositoryWithMapsIntegrationTests-oracle.sql | 17 + ...ngConfigurationIntegrationTests-oracle.sql | 2 + .../data/relational/core/dialect/Dialect.java | 4 + .../relational/core/dialect/IdGeneration.java | 45 +++ .../core/dialect/OracleDialect.java | 54 +++ 46 files changed, 1305 insertions(+), 504 deletions(-) create mode 100644 spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/AssumeFeatureRule.java delete mode 100644 spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/DatabaseProfileValueSource.java delete mode 100644 spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/DatabaseProfileValueSourceUnitTests.java create mode 100644 spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/RequiredFeature.java create mode 100644 spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestDatabaseFeatures.java create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-oracle.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateSchemaIntegrationTests-oracle.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository.config/EnableJdbcRepositoriesIntegrationTests-oracle.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryConcurrencyIntegrationTests-oracle.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-oracle.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedImmutableIntegrationTests-oracle.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedIntegrationTests-oracle.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests-oracle.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests-oracle.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests-oracle.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-oracle.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-oracle.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryPropertyConversionIntegrationTests-oracle.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryResultSetExtractorIntegrationTests-oracle.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsIntegrationTests-oracle.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-oracle.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithMapsIntegrationTests-oracle.sql create mode 100644 spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/StringBasedJdbcQueryMappingConfigurationIntegrationTests-oracle.sql create mode 100644 spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/IdGeneration.java create mode 100644 spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/OracleDialect.java diff --git a/pom.xml b/pom.xml index 7471461f..0dfc817c 100644 --- a/pom.xml +++ b/pom.xml @@ -204,7 +204,7 @@ **/*HsqlIntegrationTests.java - orcacle + oracle diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutionContext.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutionContext.java index bd67f5c4..996c620f 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutionContext.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutionContext.java @@ -72,6 +72,7 @@ class JdbcAggregateChangeExecutionContext { } void executeInsertRoot(DbAction.InsertRoot insert) { + RelationalPersistentEntity persistentEntity = getRequiredPersistentEntity(insert.getEntityType()); Object id; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java index afa4b500..8b1e729f 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java @@ -37,7 +37,7 @@ import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.PropertyHandler; -import org.springframework.data.relational.core.dialect.LockClause; +import org.springframework.data.relational.core.dialect.IdGeneration; import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; @@ -122,11 +122,20 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { KeyHolder holder = new GeneratedKeyHolder(); - operations.update( // - sqlGenerator.getInsert(new HashSet<>(parameterSource.getIdentifiers())), // - parameterSource, // - holder // - ); + IdGeneration idGeneration = sqlGeneratorSource.getDialect().getIdGeneration(); + String insertSql = sqlGenerator.getInsert(new HashSet<>(parameterSource.getIdentifiers())); + + if (idGeneration.driverRequiresKeyColumnNames()) { + + String[] keyColumnNames = getKeyColumnNames(domainType); + if (keyColumnNames.length == 0) { + operations.update(insertSql, parameterSource, holder); + } else { + operations.update(insertSql, parameterSource, holder, keyColumnNames); + } + } else { + operations.update(insertSql, parameterSource, holder); + } return getIdFromHolder(holder, persistentEntity); } @@ -567,6 +576,19 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { return sqlGeneratorSource.getSqlGenerator(domainType); } + private String[] getKeyColumnNames(Class domainType) { + + RelationalPersistentEntity requiredPersistentEntity = context.getRequiredPersistentEntity(domainType); + + if (!requiredPersistentEntity.hasIdProperty()) { + return new String[0]; + } + + SqlIdentifier idColumn = requiredPersistentEntity.getIdColumn(); + + return new String[] { idColumn.getReference(getIdentifierProcessing()) }; + } + /** * Utility to create {@link Predicate}s. */ diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java index 17c5c930..1c144e0c 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java @@ -33,6 +33,7 @@ import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.dialect.H2Dialect; import org.springframework.data.relational.core.dialect.HsqlDbDialect; import org.springframework.data.relational.core.dialect.MySqlDialect; +import org.springframework.data.relational.core.dialect.OracleDialect; import org.springframework.data.relational.core.dialect.PostgresDialect; import org.springframework.data.relational.core.dialect.SqlServerDialect; import org.springframework.data.relational.core.sql.IdentifierProcessing; @@ -131,6 +132,9 @@ public class DialectResolver { if (name.contains("db2")) { return Db2Dialect.INSTANCE; } + if (name.contains("oracle")) { + return OracleDialect.INSTANCE; + } LOG.info(String.format("Couldn't determine Dialect for \"%s\"", name) ); return null; diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java index 0b260c18..b8bb4ed8 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java @@ -17,6 +17,8 @@ package org.springframework.data.jdbc.core; import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.jdbc.testing.TestDatabaseFeatures.Feature.*; +import static org.springframework.test.context.TestExecutionListeners.MergeMode.*; import lombok.Data; import lombok.EqualsAndHashCode; @@ -35,7 +37,6 @@ import java.util.function.Function; import java.util.stream.IntStream; import org.assertj.core.api.SoftAssertions; -import org.junit.Assume; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; @@ -53,18 +54,17 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.JdbcConverter; -import org.springframework.data.jdbc.testing.DatabaseProfileValueSource; -import org.springframework.data.jdbc.testing.HsqlDbOnly; +import org.springframework.data.jdbc.testing.AssumeFeatureRule; +import org.springframework.data.jdbc.testing.RequiredFeature; import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.data.jdbc.testing.TestDatabaseFeatures; import org.springframework.data.relational.core.conversion.DbActionExecutionException; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.Table; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; -import org.springframework.test.annotation.IfProfileValue; -import org.springframework.test.annotation.ProfileValueSourceConfiguration; -import org.springframework.test.annotation.ProfileValueUtils; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.rules.SpringClassRule; import org.springframework.test.context.junit4.rules.SpringMethodRule; import org.springframework.transaction.annotation.Transactional; @@ -83,7 +83,7 @@ import org.springframework.transaction.annotation.Transactional; */ @ContextConfiguration @Transactional -@ProfileValueSourceConfiguration(DatabaseProfileValueSource.class) +@TestExecutionListeners(value = AssumeFeatureRule.class, mergeMode = MERGE_WITH_DEFAULTS) public class JdbcAggregateTemplateIntegrationTests { @ClassRule public static final SpringClassRule classRule = new SpringClassRule(); @@ -91,6 +91,7 @@ public class JdbcAggregateTemplateIntegrationTests { @Autowired JdbcAggregateOperations template; @Autowired NamedParameterJdbcOperations jdbcTemplate; + LegoSet legoSet = createLegoSet("Star Destroyer"); /** @@ -177,13 +178,6 @@ public class JdbcAggregateTemplateIntegrationTests { return "_" + i; } - private static void assumeNot(String dbProfileName) { - - Assume.assumeTrue("true" - .equalsIgnoreCase(ProfileValueUtils.retrieveProfileValueSource(JdbcAggregateTemplateIntegrationTests.class) - .get("current.database.is.not." + dbProfileName))); - } - private static LegoSet createLegoSet(String name) { LegoSet entity = new LegoSet(); @@ -197,6 +191,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-112 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void saveAndLoadAnEntityWithReferencedEntityById() { template.save(legoSet); @@ -218,6 +213,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-112 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void saveAndLoadManyEntitiesWithReferencedEntity() { template.save(legoSet); @@ -230,6 +226,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-101 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void saveAndLoadManyEntitiesWithReferencedEntitySorted() { template.save(createLegoSet("Lava")); @@ -244,20 +241,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-101 - public void saveAndLoadManyEntitiesWithReferencedEntityPaged() { - - template.save(createLegoSet("Lava")); - template.save(createLegoSet("Star")); - template.save(createLegoSet("Frozen")); - - Iterable reloadedLegoSets = template.findAll(LegoSet.class, PageRequest.of(1, 1)); - - assertThat(reloadedLegoSets) // - .extracting("name") // - .containsExactly("Star"); - } - - @Test // DATAJDBC-101 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void saveAndLoadManyEntitiesWithReferencedEntitySortedAndPaged() { template.save(createLegoSet("Lava")); @@ -272,6 +256,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-112 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void saveAndLoadManyEntitiesByIdWithReferencedEntity() { template.save(legoSet); @@ -283,6 +268,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-112 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void saveAndLoadAnEntityWithReferencedNullEntity() { legoSet.setManual(null); @@ -295,6 +281,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-112 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void saveAndDeleteAnEntityWithReferencedEntity() { template.save(legoSet); @@ -310,6 +297,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-112 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void saveAndDeleteAllWithReferencedEntity() { template.save(legoSet); @@ -325,7 +313,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-112 - @IfProfileValue(name = "current.database.is.not.mssql", value = "true") // DATAJDBC-278 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void updateReferencedEntityFromNull() { legoSet.setManual(null); @@ -344,6 +332,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-112 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void updateReferencedEntityToNull() { template.save(legoSet); @@ -374,6 +363,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-112 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void replaceReferencedEntity() { template.save(legoSet); @@ -395,7 +385,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-112 - @IfProfileValue(name = "current.database.is.not.mssql", value = "true") // DATAJDBC-278 + @RequiredFeature({SUPPORTS_QUOTED_IDS, TestDatabaseFeatures.Feature.SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES}) public void changeReferencedEntity() { template.save(legoSet); @@ -410,6 +400,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-266 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void oneToOneChildWithoutId() { OneToOneParent parent = new OneToOneParent(); @@ -426,6 +417,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-266 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void oneToOneNullChildWithoutId() { OneToOneParent parent = new OneToOneParent(); @@ -441,6 +433,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-266 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void oneToOneNullAttributes() { OneToOneParent parent = new OneToOneParent(); @@ -456,6 +449,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-125 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void saveAndLoadAnEntityWithSecondaryReferenceNull() { template.save(legoSet); @@ -468,6 +462,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-125 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void saveAndLoadAnEntityWithSecondaryReferenceNotNull() { legoSet.alternativeInstructions = new Manual(); @@ -489,6 +484,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-276 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void saveAndLoadAnEntityWithListOfElementsWithoutId() { ListParent entity = new ListParent(); @@ -507,15 +503,9 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-259 + @RequiredFeature(SUPPORTS_ARRAYS) public void saveAndLoadAnEntityWithArray() { - // MySQL and other do not support array datatypes. See - // https://dev.mysql.com/doc/refman/8.0/en/data-type-overview.html - assumeNot("mysql"); - assumeNot("mariadb"); - assumeNot("mssql"); - assumeNot("db2"); - ArrayOwner arrayOwner = new ArrayOwner(); arrayOwner.digits = new String[] { "one", "two", "three" }; @@ -531,17 +521,9 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-259, DATAJDBC-512 + @RequiredFeature(SUPPORTS_MULTIDIMENSIONAL_ARRAYS) public void saveAndLoadAnEntityWithMultidimensionalArray() { - // MySQL and other do not support array datatypes. See - // https://dev.mysql.com/doc/refman/8.0/en/data-type-overview.html - assumeNot("h2"); - assumeNot("mysql"); - assumeNot("mariadb"); - assumeNot("mssql"); - assumeNot("hsqldb"); - assumeNot("db2"); - ArrayOwner arrayOwner = new ArrayOwner(); arrayOwner.multidimensional = new String[][] { { "one-a", "two-a", "three-a" }, { "one-b", "two-b", "three-b" } }; @@ -558,15 +540,9 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-259 + @RequiredFeature(SUPPORTS_ARRAYS) public void saveAndLoadAnEntityWithList() { - // MySQL and others do not support array datatypes. See - // https://dev.mysql.com/doc/refman/8.0/en/data-type-overview.html - assumeNot("mysql"); - assumeNot("mariadb"); - assumeNot("mssql"); - assumeNot("db2"); - ListOwner arrayOwner = new ListOwner(); arrayOwner.digits.addAll(Arrays.asList("one", "two", "three")); @@ -582,15 +558,9 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-259 + @RequiredFeature(SUPPORTS_ARRAYS) public void saveAndLoadAnEntityWithSet() { - // MySQL and others do not support array datatypes. See - // https://dev.mysql.com/doc/refman/8.0/en/data-type-overview.html - assumeNot("mysql"); - assumeNot("mariadb"); - assumeNot("mssql"); - assumeNot("db2"); - SetOwner setOwner = new SetOwner(); setOwner.digits.addAll(Arrays.asList("one", "two", "three")); @@ -621,6 +591,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-340 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void saveAndLoadLongChain() { Chain4 chain4 = new Chain4(); @@ -649,6 +620,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-359 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void saveAndLoadLongChainWithoutIds() { NoIdChain4 chain4 = new NoIdChain4(); @@ -733,7 +705,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-431 - @HsqlDbOnly + @RequiredFeature(IS_HSQL) public void readOnlyGetsLoadedButNotWritten() { WithReadOnly entity = new WithReadOnly(); @@ -841,6 +813,7 @@ public class JdbcAggregateTemplateIntegrationTests { } @Test // DATAJDBC-462 + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void resavingAnUnversionedEntity() { LegoSet legoSet = new LegoSet(); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateSchemaIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateSchemaIntegrationTests.java index 3ce135e1..0be9a8f6 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateSchemaIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateSchemaIntegrationTests.java @@ -15,6 +15,10 @@ */ package org.springframework.data.jdbc.core; +import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.jdbc.testing.TestDatabaseFeatures.Feature.*; +import static org.springframework.test.context.TestExecutionListeners.MergeMode.*; + import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; @@ -26,17 +30,18 @@ import org.springframework.context.annotation.Import; import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.JdbcConverter; +import org.springframework.data.jdbc.testing.AssumeFeatureRule; +import org.springframework.data.jdbc.testing.RequiredFeature; import org.springframework.data.jdbc.testing.TestConfiguration; import org.springframework.data.relational.core.mapping.NamingStrategy; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.rules.SpringClassRule; import org.springframework.test.context.junit4.rules.SpringMethodRule; import org.springframework.transaction.annotation.Transactional; -import static org.assertj.core.api.Assertions.*; - /** * Integration tests for {@link JdbcAggregateTemplate} using an entity mapped with an explicite schema. * @@ -44,6 +49,7 @@ import static org.assertj.core.api.Assertions.*; */ @ContextConfiguration @Transactional +@TestExecutionListeners(value = AssumeFeatureRule.class, mergeMode = MERGE_WITH_DEFAULTS) public class JdbcAggregateTemplateSchemaIntegrationTests { @ClassRule public static final SpringClassRule classRule = new SpringClassRule(); @@ -52,8 +58,8 @@ public class JdbcAggregateTemplateSchemaIntegrationTests { @Autowired JdbcAggregateOperations template; @Autowired NamedParameterJdbcOperations jdbcTemplate; - @Test + @RequiredFeature(SUPPORTS_QUOTED_IDS) public void insertFindUpdateDelete() { DummyEntity entity = new DummyEntity(); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java index ef9d339a..bd9f4b49 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java @@ -17,6 +17,7 @@ package org.springframework.data.jdbc.repository; import static java.util.Arrays.*; import static org.assertj.core.api.Assertions.*; +import static org.springframework.test.context.TestExecutionListeners.MergeMode.*; import java.math.BigDecimal; import java.sql.JDBCType; @@ -37,9 +38,12 @@ import org.springframework.data.convert.WritingConverter; import org.springframework.data.jdbc.core.convert.JdbcCustomConversions; import org.springframework.data.jdbc.core.convert.JdbcValue; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; +import org.springframework.data.jdbc.testing.AssumeFeatureRule; import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.data.jdbc.testing.TestDatabaseFeatures; import org.springframework.data.repository.CrudRepository; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.rules.SpringClassRule; import org.springframework.test.context.junit4.rules.SpringMethodRule; import org.springframework.transaction.annotation.Transactional; @@ -52,6 +56,7 @@ import org.springframework.transaction.annotation.Transactional; */ @ContextConfiguration @Transactional +@TestExecutionListeners(value = AssumeFeatureRule.class, mergeMode = MERGE_WITH_DEFAULTS) public class JdbcRepositoryCustomConversionIntegrationTests { @Configuration @@ -105,7 +110,7 @@ public class JdbcRepositoryCustomConversionIntegrationTests { public void saveAndLoadAnEntity() { EntityWithStringyBigDecimal entity = new EntityWithStringyBigDecimal(); - entity.stringyNumber = "123456.78910"; + entity.stringyNumber = "123456.78912"; repository.save(entity); @@ -121,7 +126,7 @@ public class JdbcRepositoryCustomConversionIntegrationTests { public void saveAndLoadAnEntityWithReference() { EntityWithStringyBigDecimal entity = new EntityWithStringyBigDecimal(); - entity.stringyNumber = "123456.78910"; + entity.stringyNumber = "123456.78912"; entity.reference = new OtherEntity(); entity.reference.created = new Date(); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests.java index c4df3155..1170f55d 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests.java @@ -17,6 +17,7 @@ package org.springframework.data.jdbc.repository; import static java.util.Arrays.*; import static org.assertj.core.api.Assertions.*; +import static org.springframework.test.context.TestExecutionListeners.MergeMode.*; import lombok.Data; @@ -31,7 +32,9 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; +import org.springframework.data.jdbc.testing.AssumeFeatureRule; import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.data.jdbc.testing.TestDatabaseFeatures; import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Embedded; @@ -41,6 +44,7 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.rules.SpringClassRule; import org.springframework.test.context.junit4.rules.SpringMethodRule; import org.springframework.test.jdbc.JdbcTestUtils; @@ -53,33 +57,32 @@ import org.springframework.transaction.annotation.Transactional; */ @ContextConfiguration @Transactional +@TestExecutionListeners(value = AssumeFeatureRule.class, mergeMode = MERGE_WITH_DEFAULTS) public class JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests { - @Configuration - @Import(TestConfiguration.class) - static class Config { - - @Autowired JdbcRepositoryFactory factory; - - @Bean - Class testClass() { - return JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests.class; - } - - @Bean - DummyEntityRepository dummyEntityRepository() { - return factory.getRepository(DummyEntityRepository.class); - } - - } - @ClassRule public static final SpringClassRule classRule = new SpringClassRule(); @Rule public SpringMethodRule methodRule = new SpringMethodRule(); - @Autowired NamedParameterJdbcTemplate template; @Autowired DummyEntityRepository repository; @Autowired Dialect dialect; + private static DummyEntity createDummyEntity() { + DummyEntity entity = new DummyEntity(); + + entity.setTest("rootTest"); + + final DummyEntity2 dummyEntity2 = new DummyEntity2(); + dummyEntity2.setTest("c1"); + + final Embeddable embeddable = new Embeddable(); + embeddable.setAttr(1L); + dummyEntity2.setEmbeddable(embeddable); + + entity.setDummyEntity2(dummyEntity2); + + return entity; + } + @Test // DATAJDBC-111 public void savesAnEntity() throws SQLException { @@ -94,8 +97,7 @@ public class JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests { SqlIdentifier id = SqlIdentifier.quoted("ID"); String whereClause = id.toSql(dialect.getIdentifierProcessing()) + " = " + idValue; - return JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), - name, whereClause); + return JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), name, whereClause); } @Test // DATAJDBC-111 @@ -187,6 +189,7 @@ public class JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests { @Test // DATAJDBC-111 public void deleteByEntity() { + DummyEntity one = repository.save(createDummyEntity()); DummyEntity two = repository.save(createDummyEntity()); DummyEntity three = repository.save(createDummyEntity()); @@ -226,25 +229,26 @@ public class JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests { assertThat(repository.findAll()).isEmpty(); } - private static DummyEntity createDummyEntity() { - DummyEntity entity = new DummyEntity(); - - entity.setTest("rootTest"); - - final DummyEntity2 dummyEntity2 = new DummyEntity2(); - dummyEntity2.setTest("c1"); - - final Embeddable embeddable = new Embeddable(); - embeddable.setAttr(1L); - dummyEntity2.setEmbeddable(embeddable); - - entity.setDummyEntity2(dummyEntity2); - - return entity; - } - interface DummyEntityRepository extends CrudRepository {} + @Configuration + @Import(TestConfiguration.class) + static class Config { + + @Autowired JdbcRepositoryFactory factory; + + @Bean + Class testClass() { + return JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests.class; + } + + @Bean + DummyEntityRepository dummyEntityRepository() { + return factory.getRepository(DummyEntityRepository.class); + } + + } + @Data static class DummyEntity { @Column("ID") @Id Long id; diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests.java index db3c0059..41f80044 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests.java @@ -17,6 +17,7 @@ package org.springframework.data.jdbc.repository; import static java.util.Arrays.*; import static org.assertj.core.api.Assertions.*; +import static org.springframework.test.context.TestExecutionListeners.MergeMode.*; import lombok.Data; @@ -31,7 +32,9 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; +import org.springframework.data.jdbc.testing.AssumeFeatureRule; import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.data.jdbc.testing.TestDatabaseFeatures; import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Embedded; @@ -41,6 +44,7 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.rules.SpringClassRule; import org.springframework.test.context.junit4.rules.SpringMethodRule; import org.springframework.test.jdbc.JdbcTestUtils; @@ -54,33 +58,33 @@ import org.springframework.transaction.annotation.Transactional; */ @ContextConfiguration @Transactional +@TestExecutionListeners(value = AssumeFeatureRule.class, mergeMode = MERGE_WITH_DEFAULTS) public class JdbcRepositoryEmbeddedWithReferenceIntegrationTests { - @Configuration - @Import(TestConfiguration.class) - static class Config { - - @Autowired JdbcRepositoryFactory factory; - - @Bean - Class testClass() { - return JdbcRepositoryEmbeddedWithReferenceIntegrationTests.class; - } - - @Bean - DummyEntityRepository dummyEntityRepository() { - return factory.getRepository(DummyEntityRepository.class); - } - - } - @ClassRule public static final SpringClassRule classRule = new SpringClassRule(); @Rule public SpringMethodRule methodRule = new SpringMethodRule(); - @Autowired NamedParameterJdbcTemplate template; @Autowired DummyEntityRepository repository; @Autowired Dialect dialect; + private static DummyEntity createDummyEntity() { + + DummyEntity entity = new DummyEntity(); + entity.setTest("root"); + + final Embeddable embeddable = new Embeddable(); + embeddable.setTest("embedded"); + + final DummyEntity2 dummyEntity2 = new DummyEntity2(); + dummyEntity2.setTest("entity"); + + embeddable.setDummyEntity2(dummyEntity2); + + entity.setEmbeddable(embeddable); + + return entity; + } + @Test // DATAJDBC-111 public void savesAnEntity() { @@ -187,6 +191,7 @@ public class JdbcRepositoryEmbeddedWithReferenceIntegrationTests { @Test // DATAJDBC-111 public void deleteByEntity() { + DummyEntity one = repository.save(createDummyEntity()); DummyEntity two = repository.save(createDummyEntity()); DummyEntity three = repository.save(createDummyEntity()); @@ -246,28 +251,28 @@ public class JdbcRepositoryEmbeddedWithReferenceIntegrationTests { } - private static DummyEntity createDummyEntity() { - - DummyEntity entity = new DummyEntity(); - entity.setTest("root"); - - final Embeddable embeddable = new Embeddable(); - embeddable.setTest("embedded"); - - final DummyEntity2 dummyEntity2 = new DummyEntity2(); - dummyEntity2.setTest("entity"); - - embeddable.setDummyEntity2(dummyEntity2); - - entity.setEmbeddable(embeddable); - - return entity; - } - interface DummyEntityRepository extends CrudRepository { List findByTest(String test); } + @Configuration + @Import(TestConfiguration.class) + static class Config { + + @Autowired JdbcRepositoryFactory factory; + + @Bean + Class testClass() { + return JdbcRepositoryEmbeddedWithReferenceIntegrationTests.class; + } + + @Bean + DummyEntityRepository dummyEntityRepository() { + return factory.getRepository(DummyEntityRepository.class); + } + + } + @Data private static class DummyEntity { diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java index 28662235..1d8d88e5 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java @@ -18,6 +18,8 @@ package org.springframework.data.jdbc.repository; import static java.util.Arrays.*; import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.SoftAssertions.*; +import static org.springframework.data.jdbc.testing.TestDatabaseFeatures.Feature.*; +import static org.springframework.test.context.TestExecutionListeners.MergeMode.*; import lombok.Data; @@ -41,7 +43,10 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.repository.query.Query; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; +import org.springframework.data.jdbc.testing.AssumeFeatureRule; +import org.springframework.data.jdbc.testing.RequiredFeature; import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.data.jdbc.testing.TestDatabaseFeatures; import org.springframework.data.relational.core.mapping.event.AbstractRelationalEvent; import org.springframework.data.relational.core.mapping.event.AfterLoadEvent; import org.springframework.data.repository.CrudRepository; @@ -51,6 +56,7 @@ import org.springframework.data.repository.query.Param; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.rules.SpringClassRule; import org.springframework.test.context.junit4.rules.SpringMethodRule; import org.springframework.test.jdbc.JdbcTestUtils; @@ -63,56 +69,23 @@ import org.springframework.transaction.annotation.Transactional; * @author Mark Paluch */ @Transactional +@TestExecutionListeners(value = AssumeFeatureRule.class, mergeMode = MERGE_WITH_DEFAULTS) public class JdbcRepositoryIntegrationTests { - @Configuration - @Import(TestConfiguration.class) - static class Config { - - @Autowired JdbcRepositoryFactory factory; - - @Bean - Class testClass() { - return JdbcRepositoryIntegrationTests.class; - } - - @Bean - DummyEntityRepository dummyEntityRepository() { - return factory.getRepository(DummyEntityRepository.class); - } - - @Bean - NamedQueries namedQueries() throws IOException { - - PropertiesFactoryBean properties = new PropertiesFactoryBean(); - properties.setLocation(new ClassPathResource("META-INF/jdbc-named-queries.properties")); - properties.afterPropertiesSet(); - return new PropertiesBasedNamedQueries(properties.getObject()); - } - - @Bean - MyEventListener eventListener() { - return new MyEventListener(); - } - } - - static class MyEventListener implements ApplicationListener> { - - private List> events = new ArrayList<>(); - - @Override - public void onApplicationEvent(AbstractRelationalEvent event) { - events.add(event); - } - } - @ClassRule public static final SpringClassRule classRule = new SpringClassRule(); @Rule public SpringMethodRule methodRule = new SpringMethodRule(); - @Autowired NamedParameterJdbcTemplate template; @Autowired DummyEntityRepository repository; @Autowired MyEventListener eventListener; + private static DummyEntity createDummyEntity() { + + DummyEntity entity = new DummyEntity(); + entity.setName("Entity Name"); + + return entity; + } + @Before public void before() { eventListener.events.clear(); @@ -289,6 +262,7 @@ public class JdbcRepositoryIntegrationTests { } @Test // DATAJDBC-464, DATAJDBC-318 + @RequiredFeature(SUPPORTS_DATE_DATATYPES) public void executeQueryWithParameterRequiringConversion() { Instant now = Instant.now(); @@ -382,14 +356,6 @@ public class JdbcRepositoryIntegrationTests { assertThat(repository.countByName(one.getName())).isEqualTo(2); } - private static DummyEntity createDummyEntity() { - - DummyEntity entity = new DummyEntity(); - entity.setName("Entity Name"); - - return entity; - } - interface DummyEntityRepository extends CrudRepository { List findAllByNamedQuery(); @@ -413,11 +379,52 @@ public class JdbcRepositoryIntegrationTests { int countByName(String name); } + @Configuration + @Import(TestConfiguration.class) + static class Config { + + @Autowired JdbcRepositoryFactory factory; + + @Bean + Class testClass() { + return JdbcRepositoryIntegrationTests.class; + } + + @Bean + DummyEntityRepository dummyEntityRepository() { + return factory.getRepository(DummyEntityRepository.class); + } + + @Bean + NamedQueries namedQueries() throws IOException { + + PropertiesFactoryBean properties = new PropertiesFactoryBean(); + properties.setLocation(new ClassPathResource("META-INF/jdbc-named-queries.properties")); + properties.afterPropertiesSet(); + return new PropertiesBasedNamedQueries(properties.getObject()); + } + + @Bean + MyEventListener eventListener() { + return new MyEventListener(); + } + } + + static class MyEventListener implements ApplicationListener> { + + private List> events = new ArrayList<>(); + + @Override + public void onApplicationEvent(AbstractRelationalEvent event) { + events.add(event); + } + } + @Data static class DummyEntity { String name; - @Id private Long idProp; Instant pointInTime; + @Id private Long idProp; } static class CustomRowMapper implements RowMapper { diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryPropertyConversionIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryPropertyConversionIntegrationTests.java index 8ae91526..85749fe7 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryPropertyConversionIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryPropertyConversionIntegrationTests.java @@ -17,6 +17,8 @@ package org.springframework.data.jdbc.repository; import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.jdbc.testing.TestDatabaseFeatures.Feature.*; +import static org.springframework.test.context.TestExecutionListeners.MergeMode.*; import lombok.Data; @@ -40,13 +42,14 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; -import org.springframework.data.jdbc.testing.DatabaseProfileValueSource; +import org.springframework.data.jdbc.testing.AssumeFeatureRule; +import org.springframework.data.jdbc.testing.RequiredFeature; import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.data.jdbc.testing.TestDatabaseFeatures; import org.springframework.data.relational.core.mapping.event.BeforeSaveEvent; import org.springframework.data.repository.CrudRepository; -import org.springframework.test.annotation.IfProfileValue; -import org.springframework.test.annotation.ProfileValueSourceConfiguration; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.rules.SpringClassRule; import org.springframework.test.context.junit4.rules.SpringMethodRule; import org.springframework.transaction.annotation.Transactional; @@ -59,10 +62,106 @@ import org.springframework.transaction.annotation.Transactional; * @author Thomas Lang */ @ContextConfiguration -@ProfileValueSourceConfiguration(DatabaseProfileValueSource.class) @Transactional +@TestExecutionListeners(value = AssumeFeatureRule.class, mergeMode = MERGE_WITH_DEFAULTS) public class JdbcRepositoryPropertyConversionIntegrationTests { + @ClassRule public static final SpringClassRule classRule = new SpringClassRule(); + @Rule public SpringMethodRule methodRule = new SpringMethodRule(); + @Autowired DummyEntityRepository repository; + + private static EntityWithColumnsRequiringConversions createDummyEntity() { + + EntityWithColumnsRequiringConversions entity = new EntityWithColumnsRequiringConversions(); + entity.setSomeEnum(SomeEnum.VALUE); + entity.setBigDecimal(new BigDecimal("123456789012345678901234567890123456789012345678901234567890")); + entity.setBool(true); + // Postgres doesn't seem to be able to handle BigInts larger then a Long, since the driver reads them as Long + entity.setBigInteger(BigInteger.valueOf(Long.MAX_VALUE)); + entity.setDate(Date.from(getNow().toInstant(ZoneOffset.UTC))); + entity.setLocalDateTime(getNow()); + + return entity; + } + + // DATAJDBC-119 + private static LocalDateTime getNow() { + return LocalDateTime.now().withNano(0); + } + + @Test // DATAJDBC-95 + @RequiredFeature(SUPPORTS_HUGE_NUMBERS) + public void saveAndLoadAnEntity() { + + EntityWithColumnsRequiringConversions entity = repository.save(createDummyEntity()); + + assertThat(repository.findById(entity.getIdTimestamp())).hasValueSatisfying(it -> { + SoftAssertions softly = new SoftAssertions(); + softly.assertThat(it.getIdTimestamp()).isEqualTo(entity.getIdTimestamp()); + softly.assertThat(it.getSomeEnum()).isEqualTo(entity.getSomeEnum()); + softly.assertThat(it.getBigDecimal()).isEqualTo(entity.getBigDecimal()); + softly.assertThat(it.isBool()).isEqualTo(entity.isBool()); + softly.assertThat(it.getBigInteger()).isEqualTo(entity.getBigInteger()); + softly.assertThat(it.getDate()).is(representingTheSameAs(entity.getDate())); + softly.assertThat(it.getLocalDateTime()).isEqualTo(entity.getLocalDateTime()); + softly.assertAll(); + }); + } + + @Test // DATAJDBC-95 + @RequiredFeature(SUPPORTS_HUGE_NUMBERS) + public void existsById() { + + EntityWithColumnsRequiringConversions entity = repository.save(createDummyEntity()); + + assertThat(repository.existsById(entity.getIdTimestamp())).isTrue(); + } + + @Test // DATAJDBC-95 + @RequiredFeature(SUPPORTS_HUGE_NUMBERS) + public void findAllById() { + + EntityWithColumnsRequiringConversions entity = repository.save(createDummyEntity()); + + assertThat(repository.findAllById(Collections.singletonList(entity.getIdTimestamp()))).hasSize(1); + } + + @Test // DATAJDBC-95 + @RequiredFeature(SUPPORTS_HUGE_NUMBERS) + public void deleteAll() { + + EntityWithColumnsRequiringConversions entity = repository.save(createDummyEntity()); + + repository.deleteAll(singletonList(entity)); + + assertThat(repository.findAll()).hasSize(0); + } + + @Test // DATAJDBC-95 + @RequiredFeature(SUPPORTS_HUGE_NUMBERS) + public void deleteById() { + + EntityWithColumnsRequiringConversions entity = repository.save(createDummyEntity()); + + repository.deleteById(entity.getIdTimestamp()); + + assertThat(repository.findAll()).hasSize(0); + } + + private Condition representingTheSameAs(Date other) { + + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + String expected = format.format(other); + + return new Condition<>(date -> format.format(date).equals(expected), expected); + } + + enum SomeEnum { + VALUE + } + + interface DummyEntityRepository extends CrudRepository {} + @Configuration @Import(TestConfiguration.class) static class Config { @@ -84,123 +183,19 @@ public class JdbcRepositoryPropertyConversionIntegrationTests { return (ApplicationListener) beforeInsert -> ((EntityWithColumnsRequiringConversions) beforeInsert .getEntity()).setIdTimestamp(getNow()); } - } - @ClassRule public static final SpringClassRule classRule = new SpringClassRule(); - @Rule public SpringMethodRule methodRule = new SpringMethodRule(); - - @Autowired DummyEntityRepository repository; - - @Test // DATAJDBC-95 - @IfProfileValue(name = "current.database.is.not.mssql", value = "true") // DATAJDBC-278 - public void saveAndLoadAnEntity() { - - EntityWithColumnsRequiringConversions entity = repository.save(createDummyEntity()); - - assertThat(repository.findById(entity.getIdTimestamp())).hasValueSatisfying(it -> { - SoftAssertions softly = new SoftAssertions(); - softly.assertThat(it.getIdTimestamp()).isEqualTo(entity.getIdTimestamp()); - softly.assertThat(it.getSomeEnum()).isEqualTo(entity.getSomeEnum()); - softly.assertThat(it.getBigDecimal()).isEqualTo(entity.getBigDecimal()); - softly.assertThat(it.isBool()).isEqualTo(entity.isBool()); - softly.assertThat(it.getBigInteger()).isEqualTo(entity.getBigInteger()); - softly.assertThat(it.getDate()).is(representingTheSameAs(entity.getDate())); - softly.assertThat(it.getLocalDateTime()).isEqualTo(entity.getLocalDateTime()); - softly.assertAll(); - }); - } - - @Test // DATAJDBC-95 - @IfProfileValue(name = "current.database.is.not.mssql", value = "true") // DATAJDBC-278 - public void existsById() { - - EntityWithColumnsRequiringConversions entity = repository.save(createDummyEntity()); - - assertThat(repository.existsById(entity.getIdTimestamp())).isTrue(); - } - - @Test // DATAJDBC-95 - @IfProfileValue(name = "current.database.is.not.mssql", value = "true") // DATAJDBC-278 - public void findAllById() { - - EntityWithColumnsRequiringConversions entity = repository.save(createDummyEntity()); - - assertThat(repository.findAllById(Collections.singletonList(entity.getIdTimestamp()))).hasSize(1); - } - - @Test // DATAJDBC-95 - @IfProfileValue(name = "current.database.is.not.mssql", value = "true") // DATAJDBC-278 - public void deleteAll() { - - EntityWithColumnsRequiringConversions entity = repository.save(createDummyEntity()); - - repository.deleteAll(singletonList(entity)); - - assertThat(repository.findAll()).hasSize(0); - } - - @Test // DATAJDBC-95 - @IfProfileValue(name = "current.database.is.not.mssql", value = "true") // DATAJDBC-278 - public void deleteById() { - - EntityWithColumnsRequiringConversions entity = repository.save(createDummyEntity()); - - repository.deleteById(entity.getIdTimestamp()); - - assertThat(repository.findAll()).hasSize(0); - } - - private static EntityWithColumnsRequiringConversions createDummyEntity() { - - EntityWithColumnsRequiringConversions entity = new EntityWithColumnsRequiringConversions(); - entity.setSomeEnum(SomeEnum.VALUE); - entity.setBigDecimal(new BigDecimal("123456789012345678901234567890123456789012345678901234567890")); - entity.setBool(true); - // Postgres doesn't seem to be able to handle BigInts larger then a Long, since the driver reads them as Long - entity.setBigInteger(BigInteger.valueOf(Long.MAX_VALUE)); - entity.setDate(Date.from(getNow().toInstant(ZoneOffset.UTC))); - entity.setLocalDateTime(getNow()); - - return entity; - } - - // DATAJDBC-119 - private static LocalDateTime getNow() { - return LocalDateTime.now().withNano(0); - } - - private Condition representingTheSameAs(Date other) { - - SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); - String expected = format.format(other); - - return new Condition<>(date -> format.format(date).equals(expected), expected); - } - - interface DummyEntityRepository extends CrudRepository {} - @Data static class EntityWithColumnsRequiringConversions { + boolean bool; + SomeEnum someEnum; + BigDecimal bigDecimal; + BigInteger bigInteger; + Date date; + LocalDateTime localDateTime; // ensures conversion on id querying @Id private LocalDateTime idTimestamp; - boolean bool; - - SomeEnum someEnum; - - BigDecimal bigDecimal; - - BigInteger bigInteger; - - Date date; - - LocalDateTime localDateTime; - - } - - enum SomeEnum { - VALUE } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithCollectionsIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithCollectionsIntegrationTests.java index de8a1844..728e2047 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithCollectionsIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithCollectionsIntegrationTests.java @@ -16,6 +16,7 @@ package org.springframework.data.jdbc.repository; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.jdbc.testing.TestDatabaseFeatures.Feature.*; import junit.framework.AssertionFailedError; import lombok.Data; @@ -34,12 +35,11 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; -import org.springframework.data.jdbc.testing.DatabaseProfileValueSource; +import org.springframework.data.jdbc.testing.RequiredFeature; import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.data.jdbc.testing.TestDatabaseFeatures; import org.springframework.data.repository.CrudRepository; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; -import org.springframework.test.annotation.IfProfileValue; -import org.springframework.test.annotation.ProfileValueSourceConfiguration; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.rules.SpringClassRule; import org.springframework.test.context.junit4.rules.SpringMethodRule; @@ -52,33 +52,22 @@ import org.springframework.transaction.annotation.Transactional; * @author Thomas Lang */ @ContextConfiguration -@ProfileValueSourceConfiguration(DatabaseProfileValueSource.class) @Transactional public class JdbcRepositoryWithCollectionsIntegrationTests { - @Configuration - @Import(TestConfiguration.class) - static class Config { - - @Autowired JdbcRepositoryFactory factory; - - @Bean - Class testClass() { - return JdbcRepositoryWithCollectionsIntegrationTests.class; - } - - @Bean - DummyEntityRepository dummyEntityRepository() { - return factory.getRepository(DummyEntityRepository.class); - } - } - @ClassRule public static final SpringClassRule classRule = new SpringClassRule(); @Rule public SpringMethodRule methodRule = new SpringMethodRule(); @Autowired NamedParameterJdbcTemplate template; @Autowired DummyEntityRepository repository; + private static DummyEntity createDummyEntity() { + + DummyEntity entity = new DummyEntity(); + entity.setName("Entity Name"); + return entity; + } + @Test // DATAJDBC-113 public void saveAndLoadEmptySet() { @@ -139,7 +128,7 @@ public class JdbcRepositoryWithCollectionsIntegrationTests { } @Test // DATAJDBC-113 - @IfProfileValue(name = "current.database.is.not.mssql", value = "true") // DATAJDBC-278 + @RequiredFeature(SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES) public void updateSet() { Element element1 = createElement("one"); @@ -203,29 +192,39 @@ public class JdbcRepositoryWithCollectionsIntegrationTests { return element; } - private static DummyEntity createDummyEntity() { - - DummyEntity entity = new DummyEntity(); - entity.setName("Entity Name"); - return entity; - } - interface DummyEntityRepository extends CrudRepository {} + @Configuration + @Import(TestConfiguration.class) + static class Config { + + @Autowired JdbcRepositoryFactory factory; + + @Bean + Class testClass() { + return JdbcRepositoryWithCollectionsIntegrationTests.class; + } + + @Bean + DummyEntityRepository dummyEntityRepository() { + return factory.getRepository(DummyEntityRepository.class); + } + } + @Data static class DummyEntity { - @Id private Long id; String name; Set content = new HashSet<>(); + @Id private Long id; } @RequiredArgsConstructor static class Element { - @Id private Long id; String content; + @Id private Long id; } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithListsIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithListsIntegrationTests.java index 5e1b3b0d..88d01f24 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithListsIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithListsIntegrationTests.java @@ -16,6 +16,7 @@ package org.springframework.data.jdbc.repository; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.jdbc.testing.TestDatabaseFeatures.Feature.*; import junit.framework.AssertionFailedError; import lombok.Data; @@ -34,12 +35,11 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; -import org.springframework.data.jdbc.testing.DatabaseProfileValueSource; +import org.springframework.data.jdbc.testing.RequiredFeature; import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.data.jdbc.testing.TestDatabaseFeatures; import org.springframework.data.repository.CrudRepository; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; -import org.springframework.test.annotation.IfProfileValue; -import org.springframework.test.annotation.ProfileValueSourceConfiguration; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.rules.SpringClassRule; import org.springframework.test.context.junit4.rules.SpringMethodRule; @@ -52,33 +52,21 @@ import org.springframework.transaction.annotation.Transactional; * @author Thomas Lang */ @ContextConfiguration -@ProfileValueSourceConfiguration(DatabaseProfileValueSource.class) @Transactional public class JdbcRepositoryWithListsIntegrationTests { - @Configuration - @Import(TestConfiguration.class) - static class Config { - - @Autowired JdbcRepositoryFactory factory; - - @Bean - Class testClass() { - return JdbcRepositoryWithListsIntegrationTests.class; - } - - @Bean - DummyEntityRepository dummyEntityRepository() { - return factory.getRepository(DummyEntityRepository.class); - } - } - @ClassRule public static final SpringClassRule classRule = new SpringClassRule(); @Rule public SpringMethodRule methodRule = new SpringMethodRule(); - @Autowired NamedParameterJdbcTemplate template; @Autowired DummyEntityRepository repository; + private static DummyEntity createDummyEntity() { + + DummyEntity entity = new DummyEntity(); + entity.setName("Entity Name"); + return entity; + } + @Test // DATAJDBC-130 public void saveAndLoadEmptyList() { @@ -139,7 +127,7 @@ public class JdbcRepositoryWithListsIntegrationTests { } @Test // DATAJDBC-130 - @IfProfileValue(name = "current.database.is.not.mssql", value = "true") // DATAJDBC-278 + @RequiredFeature(SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES) public void updateList() { Element element1 = createElement("one"); @@ -205,29 +193,39 @@ public class JdbcRepositoryWithListsIntegrationTests { return element; } - private static DummyEntity createDummyEntity() { - - DummyEntity entity = new DummyEntity(); - entity.setName("Entity Name"); - return entity; - } - interface DummyEntityRepository extends CrudRepository {} + @Configuration + @Import(TestConfiguration.class) + static class Config { + + @Autowired JdbcRepositoryFactory factory; + + @Bean + Class testClass() { + return JdbcRepositoryWithListsIntegrationTests.class; + } + + @Bean + DummyEntityRepository dummyEntityRepository() { + return factory.getRepository(DummyEntityRepository.class); + } + } + @Data static class DummyEntity { - @Id private Long id; String name; List content = new ArrayList<>(); + @Id private Long id; } @RequiredArgsConstructor static class Element { - @Id private Long id; String content; + @Id private Long id; } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithMapsIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithMapsIntegrationTests.java index 6bdca9ac..471442c2 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithMapsIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryWithMapsIntegrationTests.java @@ -16,6 +16,7 @@ package org.springframework.data.jdbc.repository; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.jdbc.testing.TestDatabaseFeatures.Feature.*; import junit.framework.AssertionFailedError; import lombok.Data; @@ -33,12 +34,11 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; -import org.springframework.data.jdbc.testing.DatabaseProfileValueSource; +import org.springframework.data.jdbc.testing.RequiredFeature; import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.data.jdbc.testing.TestDatabaseFeatures; import org.springframework.data.repository.CrudRepository; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; -import org.springframework.test.annotation.IfProfileValue; -import org.springframework.test.annotation.ProfileValueSourceConfiguration; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.rules.SpringClassRule; import org.springframework.test.context.junit4.rules.SpringMethodRule; @@ -51,7 +51,6 @@ import org.springframework.transaction.annotation.Transactional; * @author Thomas Lang */ @ContextConfiguration -@ProfileValueSourceConfiguration(DatabaseProfileValueSource.class) @Transactional public class JdbcRepositoryWithMapsIntegrationTests { @@ -140,7 +139,7 @@ public class JdbcRepositoryWithMapsIntegrationTests { } @Test // DATAJDBC-131 - @IfProfileValue(name = "current.database.is.not.mssql", value = "true") // DATAJDBC-278 + @RequiredFeature(SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES) public void updateMap() { Element element1 = createElement("one"); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/AssumeFeatureRule.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/AssumeFeatureRule.java new file mode 100644 index 00000000..beea21a1 --- /dev/null +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/AssumeFeatureRule.java @@ -0,0 +1,51 @@ +/* + * Copyright 2020 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.jdbc.testing; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.TestContext; +import org.springframework.test.context.TestExecutionListener; + +public class AssumeFeatureRule implements TestExecutionListener { + + @Override + public void beforeTestMethod(TestContext testContext) throws Exception { + + ApplicationContext applicationContext = testContext.getApplicationContext(); + TestDatabaseFeatures databaseFeatures = applicationContext.getBean(TestDatabaseFeatures.class); + + List requiredFeatures = new ArrayList<>(); + + RequiredFeature classAnnotation = testContext.getTestClass().getAnnotation(RequiredFeature.class); + if (classAnnotation != null) { + requiredFeatures.addAll(Arrays.asList(classAnnotation.value())); + } + + RequiredFeature methodAnnotation = testContext.getTestMethod().getAnnotation(RequiredFeature.class); + if (methodAnnotation != null) { + requiredFeatures.addAll(Arrays.asList(methodAnnotation.value())); + } + + for (TestDatabaseFeatures.Feature requiredFeature : requiredFeatures) { + requiredFeature.test(databaseFeatures); + } + + } +} diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/DatabaseProfileValueSource.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/DatabaseProfileValueSource.java deleted file mode 100644 index 7b82f4b8..00000000 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/DatabaseProfileValueSource.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2018-2020 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.jdbc.testing; - -import org.springframework.test.annotation.ProfileValueSource; - -/** - * This {@link ProfileValueSource} offers a single set of keys {@code current.database.is.not.} where - * {@code } is a database as used in active profiles to enable integration tests to run with a certain - * database. The value returned for these keys is {@code "true"} or {@code "false"} depending on if the database is - * actually the one currently used by integration tests. - * - * @author Jens Schauder - */ -public class DatabaseProfileValueSource implements ProfileValueSource { - - static final String SPRING_PROFILES_ACTIVE = "spring.profiles.active"; - static final String CURRENT_DATABASE_IS_NOT = "current.database.is.not."; - - private final String currentDatabase; - - DatabaseProfileValueSource() { - - String fromEnvironment = System.getenv(SPRING_PROFILES_ACTIVE); - currentDatabase = fromEnvironment == null ? System.getProperty(SPRING_PROFILES_ACTIVE, "hsqldb") : fromEnvironment; - } - - @Override - public String get(String key) { - - if (!key.startsWith(CURRENT_DATABASE_IS_NOT)) { - return null; - } - - return Boolean.toString(!key.endsWith(currentDatabase)).toLowerCase(); - } -} diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/DatabaseProfileValueSourceUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/DatabaseProfileValueSourceUnitTests.java deleted file mode 100644 index 93c8213b..00000000 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/DatabaseProfileValueSourceUnitTests.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2019-2020 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.jdbc.testing; - -import static org.assertj.core.api.Assertions.*; -import static org.springframework.data.jdbc.testing.DatabaseProfileValueSource.*; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -/** - * Unit tests for {@link DatabaseProfileValueSource}. - * - * @author Jens Schauder - */ -public class DatabaseProfileValueSourceUnitTests { - - String oldSystemPropertyValue; - - @Before - public void before() { - oldSystemPropertyValue = System.getProperty(SPRING_PROFILES_ACTIVE); - } - - @After - public void after() { - - if (oldSystemPropertyValue == null) { - System.clearProperty(SPRING_PROFILES_ACTIVE); - } else { - System.setProperty(SPRING_PROFILES_ACTIVE, oldSystemPropertyValue); - } - } - - @Test // DATAJDBC-461 - public void returnNullForUnrelatedProperty() { - - DatabaseProfileValueSource source = new DatabaseProfileValueSource(); - assertThat(source.get("blah")).isNull(); - } - - @Test // DATAJDBC-461 - public void worksWithSystemProperty() { - - System.setProperty(SPRING_PROFILES_ACTIVE, "testProfile"); - - DatabaseProfileValueSource source = new DatabaseProfileValueSource(); - - assertThat(source.get(CURRENT_DATABASE_IS_NOT + "other")).isEqualTo("true"); - assertThat(source.get(CURRENT_DATABASE_IS_NOT + "testProfile")).isEqualTo("false"); - } - -} diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/Db2DataSourceConfiguration.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/Db2DataSourceConfiguration.java index fd06126c..3dfa0d2d 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/Db2DataSourceConfiguration.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/Db2DataSourceConfiguration.java @@ -17,6 +17,7 @@ package org.springframework.data.jdbc.testing; import java.sql.Connection; import java.sql.SQLException; +import java.util.concurrent.TimeUnit; import javax.sql.DataSource; diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MySqlDataSourceConfiguration.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MySqlDataSourceConfiguration.java index d1a4c273..69e4fe3a 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MySqlDataSourceConfiguration.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MySqlDataSourceConfiguration.java @@ -41,6 +41,7 @@ import com.mysql.jdbc.jdbc2.optional.MysqlDataSource; @Configuration @Profile("mysql") class MySqlDataSourceConfiguration extends DataSourceConfiguration { + private static MySQLContainer MYSQL_CONTAINER; /* diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/OracleDataSourceConfiguration.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/OracleDataSourceConfiguration.java index 2895533a..2325fdf4 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/OracleDataSourceConfiguration.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/OracleDataSourceConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 the original author or authors. + * Copyright 2020 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. @@ -17,11 +17,22 @@ package org.springframework.data.jdbc.testing; import javax.sql.DataSource; +import org.awaitility.Awaitility; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.testcontainers.containers.OracleContainer; +import java.sql.Connection; +import java.sql.SQLException; +import java.time.temporal.ChronoUnit; +import java.util.concurrent.TimeUnit; + +import static org.awaitility.pollinterval.FibonacciPollInterval.*; + /** * {@link DataSource} setup for Oracle Database XE. Starts a docker container with a Oracle database. * @@ -36,6 +47,8 @@ import org.testcontainers.containers.OracleContainer; @Profile("oracle") public class OracleDataSourceConfiguration extends DataSourceConfiguration { + private static final Logger LOG = LoggerFactory.getLogger(OracleDataSourceConfiguration.class); + private static OracleContainer ORACLE_CONTAINER; /* @@ -47,14 +60,36 @@ public class OracleDataSourceConfiguration extends DataSourceConfiguration { if (ORACLE_CONTAINER == null) { - OracleContainer container = new OracleContainer("name_of_your_oracle_xe_image"); + OracleContainer container = new OracleContainer("springci/spring-data-oracle-xe-prebuild:18.4.0").withReuse(true); container.start(); ORACLE_CONTAINER = container; } - return new DriverManagerDataSource(ORACLE_CONTAINER.getJdbcUrl(), ORACLE_CONTAINER.getUsername(), + String jdbcUrl = ORACLE_CONTAINER.getJdbcUrl().replace(":xe", "/XEPDB1"); + + DataSource dataSource = new DriverManagerDataSource(jdbcUrl, ORACLE_CONTAINER.getUsername(), ORACLE_CONTAINER.getPassword()); + + // Oracle container says its ready but it's like with a cat that denies service and still wants food although it had + // its food. Therefore, we make sure that we can properly establish a connection instead of trusting the cat + // ...err... Oracle. + Awaitility.await() + .atMost(5L, TimeUnit.MINUTES ) + .pollInterval(fibonacci(TimeUnit.SECONDS)) + .ignoreException(SQLException.class).until(() -> { + + try (Connection connection = dataSource.getConnection()) { + return true; + } + }); + + + return dataSource; } + @Override + protected void customizePopulator(ResourceDatabasePopulator populator) { + populator.setIgnoreFailedDrops(true); + } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/RequiredFeature.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/RequiredFeature.java new file mode 100644 index 00000000..903c3fe8 --- /dev/null +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/RequiredFeature.java @@ -0,0 +1,27 @@ +/* + * Copyright 2020 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.jdbc.testing; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE}) +public @interface RequiredFeature { + TestDatabaseFeatures.Feature[] value(); +} diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java index 6e69246f..7e8b1ccd 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java @@ -127,4 +127,10 @@ public class TestConfiguration { Dialect jdbcDialect(NamedParameterJdbcOperations operations) { return DialectResolver.getDialect(operations.getJdbcOperations()); } + + @Lazy + @Bean + TestDatabaseFeatures features(NamedParameterJdbcOperations operations) { + return new TestDatabaseFeatures(operations.getJdbcOperations()); + } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestDatabaseFeatures.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestDatabaseFeatures.java new file mode 100644 index 00000000..54f7db72 --- /dev/null +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestDatabaseFeatures.java @@ -0,0 +1,140 @@ +/* + * Copyright 2020 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.jdbc.testing; + +import static org.assertj.core.api.Assumptions.*; + +import java.util.Arrays; +import java.util.Locale; +import java.util.function.Consumer; + +import org.springframework.jdbc.core.ConnectionCallback; +import org.springframework.jdbc.core.JdbcOperations; + +/** + * This class provides information about which features a database integration supports in order to react on the + * presence or absence of features in tests. + * + * @author Jens Schauder + */ +public class TestDatabaseFeatures { + + private final Database database; + + public TestDatabaseFeatures(JdbcOperations jdbcTemplate) { + + String productName = jdbcTemplate.execute( + (ConnectionCallback) c -> c.getMetaData().getDatabaseProductName().toLowerCase(Locale.ENGLISH)); + + database = Arrays.stream(Database.values()).filter(db -> db.matches(productName)).findFirst().get(); + } + + /** + * Oracle returns an oracle.sql.TIMESTAMP which currently cannot be converted to an Instant. See DATAJDBC-569 for + * reference. + */ + private void supportsDateDataTypes() { + assumeThat(database).isNotEqualTo(Database.Oracle); + } + + /** + * Not all databases support really huge numbers as represented by {@link java.math.BigDecimal} and similar. + */ + private void supportsHugeNumbers() { + assumeThat(database).isNotIn(Database.Oracle, Database.SqlServer); + } + + /** + * Oracle does not allow to specify an alias for a joined table with {@code AS}. See DATAJDBC-570 for reference. + */ + private void supportsAsForJoinAlias() { + assumeThat(database).isNotEqualTo(Database.Oracle); + } + + /** + * Oracles JDBC driver seems to have a bug that makes it impossible to acquire generated keys when the column is + * quoted. See + * https://stackoverflow.com/questions/62263576/how-to-get-the-generated-key-for-a-column-with-lowercase-characters-from-oracle + */ + private void supportsQuotedIds() { + assumeThat(database).isNotEqualTo(Database.Oracle); + } + + /** + * Microsoft SqlServer does not allow explicitly setting ids in columns where the value gets generated by the + * database. Such columns therefore must not be used in referenced entities, since we do a delete and insert, which + * must not recreate an id. See https://jira.spring.io/browse/DATAJDBC-210 + */ + private void supportsGeneratedIdsInReferencedEntities() { + assumeThat(database).isNotEqualTo(Database.SqlServer); + } + + private void supportsArrays() { + + assumeThat(database).isNotIn(Database.MySql, Database.MariaDb, Database.SqlServer, Database.Db2, Database.Oracle); + } + + private void supportsMultiDimensionalArrays() { + + supportsArrays(); + assumeThat(database).isNotIn(Database.H2, Database.Hsql); + } + + public void databaseIs(Database database) { + assumeThat(this.database).isEqualTo(database); + } + + public enum Database { + Hsql, H2, MySql, MariaDb, PostgreSql, SqlServer("microsoft"), Db2, Oracle; + + private final String identification; + + Database(String identification) { + this.identification = identification; + } + + Database() { + this.identification = null; + } + + boolean matches(String productName) { + + String identification = this.identification == null ? name().toLowerCase() : this.identification; + return productName.contains(identification); + } + } + + public enum Feature { + + SUPPORTS_DATE_DATATYPES(TestDatabaseFeatures::supportsDateDataTypes), // + SUPPORTS_MULTIDIMENSIONAL_ARRAYS(TestDatabaseFeatures::supportsMultiDimensionalArrays), // + SUPPORTS_QUOTED_IDS(TestDatabaseFeatures::supportsQuotedIds), // + SUPPORTS_HUGE_NUMBERS(TestDatabaseFeatures::supportsHugeNumbers), // + SUPPORTS_ARRAYS(TestDatabaseFeatures::supportsArrays), // + SUPPORTS_GENERATED_IDS_IN_REFERENCED_ENTITIES(TestDatabaseFeatures::supportsGeneratedIdsInReferencedEntities), // + IS_HSQL(f -> f.databaseIs(Database.Hsql)); + + private final Consumer featureMethod; + + Feature(Consumer featureMethod) { + this.featureMethod = featureMethod; + } + + void test(TestDatabaseFeatures features) { + featureMethod.accept(features); + } + } +} diff --git a/spring-data-jdbc/src/test/resources/logback.xml b/spring-data-jdbc/src/test/resources/logback.xml index 5288df9b..67cda4af 100644 --- a/spring-data-jdbc/src/test/resources/logback.xml +++ b/spring-data-jdbc/src/test/resources/logback.xml @@ -7,8 +7,8 @@ - - + + diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-oracle.sql new file mode 100644 index 00000000..65592859 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-oracle.sql @@ -0,0 +1,327 @@ +DROP TABLE MANUAL CASCADE CONSTRAINTS PURGE; +DROP TABLE LEGO_SET CASCADE CONSTRAINTS PURGE; +DROP TABLE CHILD_NO_ID CASCADE CONSTRAINTS PURGE; +DROP TABLE ONE_TO_ONE_PARENT CASCADE CONSTRAINTS PURGE; +DROP TABLE ELEMENT_NO_ID CASCADE CONSTRAINTS PURGE; +DROP TABLE LIST_PARENT CASCADE CONSTRAINTS PURGE; +DROP TABLE BYTE_ARRAY_OWNER CASCADE CONSTRAINTS PURGE; +DROP TABLE CHAIN0 CASCADE CONSTRAINTS PURGE; +DROP TABLE CHAIN1 CASCADE CONSTRAINTS PURGE; +DROP TABLE CHAIN2 CASCADE CONSTRAINTS PURGE; +DROP TABLE CHAIN3 CASCADE CONSTRAINTS PURGE; +DROP TABLE CHAIN4 CASCADE CONSTRAINTS PURGE; +DROP TABLE NO_ID_CHAIN0 CASCADE CONSTRAINTS PURGE; +DROP TABLE NO_ID_CHAIN1 CASCADE CONSTRAINTS PURGE; +DROP TABLE NO_ID_CHAIN2 CASCADE CONSTRAINTS PURGE; +DROP TABLE NO_ID_CHAIN3 CASCADE CONSTRAINTS PURGE; +DROP TABLE NO_ID_CHAIN4 CASCADE CONSTRAINTS PURGE; +DROP TABLE NO_ID_LIST_CHAIN0 CASCADE CONSTRAINTS PURGE; +DROP TABLE NO_ID_LIST_CHAIN1 CASCADE CONSTRAINTS PURGE; +DROP TABLE NO_ID_LIST_CHAIN2 CASCADE CONSTRAINTS PURGE; +DROP TABLE NO_ID_LIST_CHAIN3 CASCADE CONSTRAINTS PURGE; +DROP TABLE NO_ID_LIST_CHAIN4 CASCADE CONSTRAINTS PURGE; +DROP TABLE NO_ID_MAP_CHAIN0 CASCADE CONSTRAINTS PURGE; +DROP TABLE NO_ID_MAP_CHAIN1 CASCADE CONSTRAINTS PURGE; +DROP TABLE NO_ID_MAP_CHAIN2 CASCADE CONSTRAINTS PURGE; +DROP TABLE NO_ID_MAP_CHAIN3 CASCADE CONSTRAINTS PURGE; +DROP TABLE NO_ID_MAP_CHAIN4 CASCADE CONSTRAINTS PURGE; +DROP TABLE VERSIONED_AGGREGATE CASCADE CONSTRAINTS PURGE; +DROP TABLE WITH_READ_ONLY CASCADE CONSTRAINTS PURGE; + +CREATE TABLE LEGO_SET +( + "id1" NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + NAME VARCHAR(30) +); +CREATE TABLE MANUAL +( + "id2" NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + LEGO_SET NUMBER, + ALTERNATIVE NUMBER, + CONTENT VARCHAR(2000) +); + +ALTER TABLE MANUAL + ADD FOREIGN KEY (LEGO_SET) + REFERENCES LEGO_SET ("id1"); + +CREATE TABLE ONE_TO_ONE_PARENT +( + "id3" NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + content VARCHAR(30) +); +CREATE TABLE Child_No_Id +( + ONE_TO_ONE_PARENT INTEGER PRIMARY KEY, + "content" VARCHAR(30) +); + +CREATE TABLE LIST_PARENT +( + "id4" NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + NAME VARCHAR(100) +); +CREATE TABLE element_no_id +( + CONTENT VARCHAR(100), + LIST_PARENT_key NUMBER, + LIST_PARENT NUMBER +); + +CREATE TABLE BYTE_ARRAY_OWNER +( + ID NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + BINARY_DATA RAW(100) NOT NULL +); + +CREATE TABLE CHAIN4 +( + FOUR NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + FOUR_VALUE VARCHAR(20) +); + + +CREATE TABLE CHAIN3 +( + THREE NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + THREE_VALUE VARCHAR(20), + CHAIN4 NUMBER, + FOREIGN KEY (CHAIN4) REFERENCES CHAIN4 (FOUR) +); + +CREATE TABLE CHAIN2 +( + TWO NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + TWO_VALUE VARCHAR(20), + CHAIN3 NUMBER, + FOREIGN KEY (CHAIN3) REFERENCES CHAIN3 (THREE) +); + +CREATE TABLE CHAIN1 +( + ONE NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + ONE_VALUE VARCHAR(20), + CHAIN2 NUMBER, + FOREIGN KEY (CHAIN2) REFERENCES CHAIN2 (TWO) +); + +CREATE TABLE CHAIN0 +( + ZERO NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + ZERO_VALUE VARCHAR(20), + CHAIN1 NUMBER, + FOREIGN KEY (CHAIN1) REFERENCES CHAIN1 (ONE) +); + +CREATE TABLE NO_ID_CHAIN4 +( + FOUR NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + FOUR_VALUE VARCHAR(20) +); + +CREATE TABLE NO_ID_CHAIN3 +( + THREE_VALUE VARCHAR(20), + NO_ID_CHAIN4 NUMBER, + FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) +); + +CREATE TABLE NO_ID_CHAIN2 +( + TWO_VALUE VARCHAR(20), + NO_ID_CHAIN4 NUMBER, + FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) +); + +CREATE TABLE NO_ID_CHAIN1 +( + ONE_VALUE VARCHAR(20), + NO_ID_CHAIN4 NUMBER, + FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) +); + +CREATE TABLE NO_ID_CHAIN0 +( + ZERO_VALUE VARCHAR(20), + NO_ID_CHAIN4 NUMBER, + FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) +); + +CREATE TABLE NO_ID_LIST_CHAIN4 +( + FOUR NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + FOUR_VALUE VARCHAR(20) +); + +CREATE TABLE NO_ID_LIST_CHAIN3 +( + THREE_VALUE VARCHAR(20), + NO_ID_LIST_CHAIN4 NUMBER, + NO_ID_LIST_CHAIN4_KEY NUMBER, + PRIMARY KEY (NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY), + FOREIGN KEY (NO_ID_LIST_CHAIN4) REFERENCES NO_ID_LIST_CHAIN4 (FOUR) +); + +CREATE TABLE NO_ID_LIST_CHAIN2 +( + TWO_VALUE VARCHAR(20), + NO_ID_LIST_CHAIN4 NUMBER, + NO_ID_LIST_CHAIN4_KEY NUMBER, + NO_ID_LIST_CHAIN3_KEY NUMBER, + PRIMARY KEY (NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY), + FOREIGN KEY ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY + ) REFERENCES NO_ID_LIST_CHAIN3 ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY + ) +); + +CREATE TABLE NO_ID_LIST_CHAIN1 +( + ONE_VALUE VARCHAR(20), + NO_ID_LIST_CHAIN4 NUMBER, + NO_ID_LIST_CHAIN4_KEY NUMBER, + NO_ID_LIST_CHAIN3_KEY NUMBER, + NO_ID_LIST_CHAIN2_KEY NUMBER, + PRIMARY KEY (NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY, + NO_ID_LIST_CHAIN2_KEY), + FOREIGN KEY ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY + ) REFERENCES NO_ID_LIST_CHAIN2 ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY + ) +); + +CREATE TABLE NO_ID_LIST_CHAIN0 +( + ZERO_VALUE VARCHAR(20), + NO_ID_LIST_CHAIN4 NUMBER, + NO_ID_LIST_CHAIN4_KEY NUMBER, + NO_ID_LIST_CHAIN3_KEY NUMBER, + NO_ID_LIST_CHAIN2_KEY NUMBER, + NO_ID_LIST_CHAIN1_KEY NUMBER, + PRIMARY KEY (NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY, + NO_ID_LIST_CHAIN2_KEY, + NO_ID_LIST_CHAIN1_KEY), + FOREIGN KEY ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY, + NO_ID_LIST_CHAIN2_KEY + ) REFERENCES NO_ID_LIST_CHAIN1 ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY, + NO_ID_LIST_CHAIN2_KEY + ) +); + + + +CREATE TABLE NO_ID_MAP_CHAIN4 +( + FOUR NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + FOUR_VALUE VARCHAR(20) +); + +CREATE TABLE NO_ID_MAP_CHAIN3 +( + THREE_VALUE VARCHAR(20), + NO_ID_MAP_CHAIN4 NUMBER, + NO_ID_MAP_CHAIN4_KEY VARCHAR(20), + PRIMARY KEY (NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY), + FOREIGN KEY (NO_ID_MAP_CHAIN4) REFERENCES NO_ID_MAP_CHAIN4 (FOUR) +); + +CREATE TABLE NO_ID_MAP_CHAIN2 +( + TWO_VALUE VARCHAR(20), + NO_ID_MAP_CHAIN4 NUMBER, + NO_ID_MAP_CHAIN4_KEY VARCHAR(20), + NO_ID_MAP_CHAIN3_KEY VARCHAR(20), + PRIMARY KEY (NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY), + FOREIGN KEY ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY + ) REFERENCES NO_ID_MAP_CHAIN3 ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY + ) +); + +CREATE TABLE NO_ID_MAP_CHAIN1 +( + ONE_VALUE VARCHAR(20), + NO_ID_MAP_CHAIN4 NUMBER, + NO_ID_MAP_CHAIN4_KEY VARCHAR(20), + NO_ID_MAP_CHAIN3_KEY VARCHAR(20), + NO_ID_MAP_CHAIN2_KEY VARCHAR(20), + PRIMARY KEY (NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY, + NO_ID_MAP_CHAIN2_KEY), + FOREIGN KEY ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY + ) REFERENCES NO_ID_MAP_CHAIN2 ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY + ) +); + +CREATE TABLE NO_ID_MAP_CHAIN0 +( + ZERO_VALUE VARCHAR(20), + NO_ID_MAP_CHAIN4 NUMBER, + NO_ID_MAP_CHAIN4_KEY VARCHAR(20), + NO_ID_MAP_CHAIN3_KEY VARCHAR(20), + NO_ID_MAP_CHAIN2_KEY VARCHAR(20), + NO_ID_MAP_CHAIN1_KEY VARCHAR(20), + PRIMARY KEY (NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY, + NO_ID_MAP_CHAIN2_KEY, + NO_ID_MAP_CHAIN1_KEY), + FOREIGN KEY ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY, + NO_ID_MAP_CHAIN2_KEY + ) REFERENCES NO_ID_MAP_CHAIN1 ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY, + NO_ID_MAP_CHAIN2_KEY + ) +); + +CREATE TABLE VERSIONED_AGGREGATE +( + ID NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + VERSION NUMBER +); + +CREATE TABLE WITH_READ_ONLY +( + ID NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + NAME VARCHAR(200), + READ_ONLY VARCHAR(200) DEFAULT 'from-db' +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateSchemaIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateSchemaIntegrationTests-oracle.sql new file mode 100644 index 00000000..b93ef418 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateSchemaIntegrationTests-oracle.sql @@ -0,0 +1,18 @@ +DROP USER OTHER CASCADE; + + +CREATE USER OTHER; + +CREATE TABLE OTHER.DUMMY_ENTITY +( + ID NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + NAME VARCHAR2(30) +); + + +CREATE TABLE OTHER.REFERENCED +( + DUMMY_ENTITY INTEGER, + NAME VARCHAR2(30) +); + diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository.config/EnableJdbcRepositoriesIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository.config/EnableJdbcRepositoriesIntegrationTests-oracle.sql new file mode 100644 index 00000000..24f9f775 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository.config/EnableJdbcRepositoriesIntegrationTests-oracle.sql @@ -0,0 +1,3 @@ +DROP TABLE DUMMY_ENTITY; + +CREATE TABLE DUMMY_ENTITY ( id NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY); \ No newline at end of file diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryConcurrencyIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryConcurrencyIntegrationTests-oracle.sql new file mode 100644 index 00000000..e6d9bfb0 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryConcurrencyIntegrationTests-oracle.sql @@ -0,0 +1,14 @@ +DROP TABLE DUMMY_ENTITY CASCADE CONSTRAINTS PURGE; +DROP TABLE ELEMENT CASCADE CONSTRAINTS PURGE; + +CREATE TABLE DUMMY_ENTITY ( + ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, + NAME VARCHAR2(100) +); + +CREATE TABLE ELEMENT ( + ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, + CONTENT NUMBER, + DUMMY_ENTITY_KEY NUMBER , + DUMMY_ENTITY NUMBER +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-oracle.sql new file mode 100644 index 00000000..1b02ef72 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-oracle.sql @@ -0,0 +1,14 @@ +DROP TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL CASCADE CONSTRAINTS PURGE; +DROP TABLE OTHER_ENTITY CASCADE CONSTRAINTS PURGE; + +CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( + ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, + STRINGY_NUMBER DECIMAL(20,10) +); + +CREATE TABLE OTHER_ENTITY ( + ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, + CREATED DATE, + ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER +); + diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedImmutableIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedImmutableIntegrationTests-oracle.sql new file mode 100644 index 00000000..1a27e7c6 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedImmutableIntegrationTests-oracle.sql @@ -0,0 +1,7 @@ +DROP TABLE DUMMY_ENTITY CASCADE CONSTRAINTS PURGE; + +CREATE TABLE DUMMY_ENTITY ( + ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, + PREFIX_ATTR1 NUMBER, + PREFIX_ATTR2 VARCHAR2(100) +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedIntegrationTests-oracle.sql new file mode 100644 index 00000000..4ad71309 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedIntegrationTests-oracle.sql @@ -0,0 +1,9 @@ +DROP TABLE DUMMY_ENTITY CASCADE CONSTRAINTS PURGE; + +CREATE TABLE DUMMY_ENTITY ( + ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, + TEST VARCHAR2(100), + PREFIX2_ATTR NUMBER , + PREFIX_TEST VARCHAR2(100), + PREFIX_PREFIX2_ATTR NUMBER +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests-oracle.sql new file mode 100644 index 00000000..0ab81989 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests-oracle.sql @@ -0,0 +1,12 @@ +DROP TABLE DUMMY_ENTITY CASCADE CONSTRAINTS PURGE; +DROP TABLE DUMMY_ENTITY2 CASCADE CONSTRAINTS PURGE; + +CREATE TABLE DUMMY_ENTITY ( + ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, + TEST VARCHAR2(100) +); +CREATE TABLE DUMMY_ENTITY2 ( + ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, + TEST VARCHAR2(100), + PREFIX_ATTR NUMBER +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests-oracle.sql new file mode 100644 index 00000000..66f369fc --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests-oracle.sql @@ -0,0 +1,17 @@ +DROP TABLE DUMMY_ENTITY2 CASCADE CONSTRAINTS PURGE; +DROP TABLE DUMMY_ENTITY CASCADE CONSTRAINTS PURGE; + +CREATE TABLE DUMMY_ENTITY +( + ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, + TEST VARCHAR2(100), + PREFIX_TEST VARCHAR2(100) +); + +CREATE TABLE DUMMY_ENTITY2 +( + ID NUMBER, + ORDER_KEY NUMBER, + TEST VARCHAR2(100), + PRIMARY KEY (ID, ORDER_KEY) +) diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests-oracle.sql new file mode 100644 index 00000000..40437415 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests-oracle.sql @@ -0,0 +1,16 @@ +DROP TABLE DUMMY_ENTITY2 CASCADE CONSTRAINTS PURGE; +DROP TABLE DUMMY_ENTITY CASCADE CONSTRAINTS PURGE; + + + +CREATE TABLE dummy_entity +( + ID NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + TEST VARCHAR2(100), + PREFIX_TEST VARCHAR2(100) +); +CREATE TABLE dummy_entity2 +( + ID NUMBER , + TEST VARCHAR2(100) +) diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-oracle.sql new file mode 100644 index 00000000..772fdfc1 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-oracle.sql @@ -0,0 +1,18 @@ +DROP TABLE ReadOnlyIdEntity; +DROP TABLE PrimitiveIdEntity; +DROP TABLE ImmutableWithManualIdentity; + +CREATE TABLE ReadOnlyIdEntity ( + ID NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + NAME VARCHAR2(100) +); + +CREATE TABLE PrimitiveIdEntity ( + ID NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + NAME VARCHAR2(100) +); + +CREATE TABLE ImmutableWithManualIdentity ( + ID NUMBER PRIMARY KEY, + NAME VARCHAR2(100) +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-oracle.sql new file mode 100644 index 00000000..28b2d80e --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-oracle.sql @@ -0,0 +1,8 @@ +DROP TABLE DUMMY_ENTITY CASCADE CONSTRAINTS PURGE; + +CREATE TABLE DUMMY_ENTITY +( + ID_PROP NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, + NAME VARCHAR2(100), + POINT_IN_TIME TIMESTAMP +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryPropertyConversionIntegrationTests-db2.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryPropertyConversionIntegrationTests-db2.sql index 9ddb7fe3..dba16a76 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryPropertyConversionIntegrationTests-db2.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryPropertyConversionIntegrationTests-db2.sql @@ -2,7 +2,8 @@ DROP TABLE ENTITY_WITH_COLUMNS_REQUIRING_CONVERSIONS; CREATE TABLE ENTITY_WITH_COLUMNS_REQUIRING_CONVERSIONS ( id_Timestamp DATETIME NOT NULL PRIMARY KEY, - bool boolean, SOME_ENUM VARCHAR(100), + bool boolean, + SOME_ENUM VARCHAR(100), big_Decimal VARCHAR(100), big_Integer BIGINT, date DATETIME, diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryPropertyConversionIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryPropertyConversionIntegrationTests-oracle.sql new file mode 100644 index 00000000..bfa63a53 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryPropertyConversionIntegrationTests-oracle.sql @@ -0,0 +1,12 @@ +DROP TABLE ENTITY_WITH_COLUMNS_REQUIRING_CONVERSIONS; + +CREATE TABLE ENTITY_WITH_COLUMNS_REQUIRING_CONVERSIONS ( + ID_TIMESTAMP TIMESTAMP PRIMARY KEY, + BOOL CHAR(1), + SOME_ENUM VARCHAR2(100), + BIG_DECIMAL DECIMAL (38), + BIG_INTEGER NUMBER(38, 0), + "DATE" TIMESTAMP, + LOCAL_DATE_TIME TIMESTAMP, + ZONED_DATE_TIME VARCHAR2(30) +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryResultSetExtractorIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryResultSetExtractorIntegrationTests-oracle.sql new file mode 100644 index 00000000..ab6fb458 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryResultSetExtractorIntegrationTests-oracle.sql @@ -0,0 +1,14 @@ +DROP TABLE ADDRESS; +DROP TABLE PERSON; + +CREATE TABLE PERSON ( + ID NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + NAME VARCHAR2(100) +); + +CREATE TABLE ADDRESS ( + ID NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + STREET VARCHAR2(100), + PERSON_ID NUMBER); + +ALTER TABLE ADDRESS ADD FOREIGN KEY (PERSON_ID) REFERENCES PERSON(ID); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsIntegrationTests-oracle.sql new file mode 100644 index 00000000..52dbc96a --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithCollectionsIntegrationTests-oracle.sql @@ -0,0 +1,13 @@ +DROP TABLE ELEMENT; +DROP TABLE DUMMY_ENTITY; + +CREATE TABLE DUMMY_ENTITY ( + ID NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + NAME VARCHAR2(100) +); + +CREATE TABLE ELEMENT ( + ID NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + CONTENT VARCHAR(100), + DUMMY_ENTITY NUMBER +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-oracle.sql new file mode 100644 index 00000000..47b21124 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithListsIntegrationTests-oracle.sql @@ -0,0 +1,14 @@ +DROP TABLE ELEMENT; +DROP TABLE DUMMY_ENTITY; + +CREATE TABLE DUMMY_ENTITY ( + ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, + NAME VARCHAR2(100) +); + +CREATE TABLE ELEMENT ( + ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, + CONTENT VARCHAR(100), + DUMMY_ENTITY_KEY NUMBER, + DUMMY_ENTITY NUMBER +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithMapsIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithMapsIntegrationTests-oracle.sql new file mode 100644 index 00000000..6fb0e811 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryWithMapsIntegrationTests-oracle.sql @@ -0,0 +1,17 @@ +DROP TABLE ELEMENT; +DROP TABLE DUMMY_ENTITY; + +CREATE TABLE DUMMY_ENTITY ( + ID NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + NAME VARCHAR2(100) +); + +CREATE TABLE ELEMENT ( + ID NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, + CONTENT VARCHAR2(100), + DUMMY_ENTITY_KEY VARCHAR2(100), + DUMMY_ENTITY NUMBER ); + +ALTER TABLE ELEMENT + ADD FOREIGN KEY (DUMMY_ENTITY) + REFERENCES DUMMY_ENTITY(ID); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/StringBasedJdbcQueryMappingConfigurationIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/StringBasedJdbcQueryMappingConfigurationIntegrationTests-oracle.sql new file mode 100644 index 00000000..18c251e1 --- /dev/null +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/StringBasedJdbcQueryMappingConfigurationIntegrationTests-oracle.sql @@ -0,0 +1,2 @@ +DROP TABLE CAR; +CREATE TABLE CAR ( id NUMBER GENERATED by default on null as IDENTITY PRIMARY KEY, model VARCHAR(100)); diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java index aa1bad1a..e9182841 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java @@ -81,4 +81,8 @@ public interface Dialect { default Escaper getLikeEscaper() { return Escaper.DEFAULT; } + + default IdGeneration getIdGeneration(){ + return IdGeneration.DEFAULT; + }; } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/IdGeneration.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/IdGeneration.java new file mode 100644 index 00000000..19285ff3 --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/IdGeneration.java @@ -0,0 +1,45 @@ +/* + * Copyright 2020 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.relational.core.dialect; + +import java.sql.Connection; + +/** + * Describes the how obtaining generated ids after an insert works for a given JDBC driver. + * + * @author Jens Schauder + * @since 2.1 + */ +public interface IdGeneration { + /** + * A default instance working for many databases and equivalent to Spring Data JDBCs behavior before version 2.1. + */ + IdGeneration DEFAULT = new IdGeneration() {}; + + /** + * Does the driver require the specification of those columns for which a generated id shall be returned. + *

+ * This should be {@literal false} for most dialects. One notable exception is Oracle. + * + * @return {@literal true} if the a list of column names should get passed to the JDBC driver for which ids shall be + * generated. + * + * @see Connection#prepareStatement(String, String[])? + */ + default boolean driverRequiresKeyColumnNames() { + return false; + } +} diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/OracleDialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/OracleDialect.java new file mode 100644 index 00000000..d1f23c3f --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/OracleDialect.java @@ -0,0 +1,54 @@ +/* + * Copyright 2019-2020 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.relational.core.dialect; + +import java.util.List; + +import org.springframework.data.relational.core.sql.IdentifierProcessing; +import org.springframework.data.relational.core.sql.LockOptions; +import org.springframework.data.relational.core.sql.Table; +import org.springframework.data.relational.core.sql.IdentifierProcessing.LetterCasing; +import org.springframework.data.relational.core.sql.IdentifierProcessing.Quoting; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; + +/** + * An SQL dialect for Oracle. + * + * @author Jens Schauder + * @since 2.1 + */ +public class OracleDialect extends AnsiDialect { + + /** + * Singleton instance. + */ + public static final OracleDialect INSTANCE = new OracleDialect(); + + private static final IdGeneration ID_GENERATION = new IdGeneration() { + @Override + public boolean driverRequiresKeyColumnNames() { + return true; + } + }; + + protected OracleDialect() {} + + @Override + public IdGeneration getIdGeneration() { + return ID_GENERATION; + } +}