diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntityImpl.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntityImpl.java index 4f7c563c71..939d804a99 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntityImpl.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntityImpl.java @@ -17,6 +17,7 @@ import java.util.Optional; +import org.jetbrains.annotations.NotNull; import org.springframework.data.mapping.model.BasicPersistentEntity; import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.util.Lazy; @@ -29,12 +30,14 @@ * @author Jens Schauder * @author Greg Turnquist * @author Bastian Wilhelm + * @author Mikhail Polivakha */ class RelationalPersistentEntityImpl extends BasicPersistentEntity implements RelationalPersistentEntity { private final NamingStrategy namingStrategy; private final Lazy> tableName; + private final Lazy> schemaName; private boolean forceQuote = true; /** @@ -43,16 +46,20 @@ class RelationalPersistentEntityImpl extends BasicPersistentEntity information, NamingStrategy namingStrategy) { - super(information); + final Optional optionalTableAnnotation = Optional.ofNullable(findAnnotation(Table.class)); this.namingStrategy = namingStrategy; - this.tableName = Lazy.of(() -> Optional.ofNullable( // - findAnnotation(Table.class)) // - .map(Table::value) // - .filter(StringUtils::hasText) // - .map(this::createSqlIdentifier) // + this.tableName = Lazy.of(() -> optionalTableAnnotation + .map(Table::value) + .filter(StringUtils::hasText) + .map(this::createSqlIdentifier) ); + + this.schemaName = Lazy.of(() -> optionalTableAnnotation + .map(Table::schema) + .filter(StringUtils::hasText) + .map(this::createSqlIdentifier)); } private SqlIdentifier createSqlIdentifier(String name) { @@ -77,14 +84,30 @@ public void setForceQuote(boolean forceQuote) { */ @Override public SqlIdentifier getTableName() { - return tableName.get().orElseGet(() -> { - - String schema = namingStrategy.getSchema(); - SqlIdentifier tableName = createDerivedSqlIdentifier(namingStrategy.getTableName(getType())); + final Optional schema = determineCurrentEntitySchema(); + final Optional explicitlySpecifiedTableName = tableName.get(); + if (schema.isPresent()) { + return explicitlySpecifiedTableName + .map(sqlIdentifier -> SqlIdentifier.from(schema.get(), sqlIdentifier)) + .orElse(SqlIdentifier.from(schema.get(), createDerivedSqlIdentifier(namingStrategy.getTableName(getType())))); + } else { + return explicitlySpecifiedTableName.orElse(createDerivedSqlIdentifier(namingStrategy.getTableName(getType()))); + } + } - return StringUtils.hasText(schema) ? SqlIdentifier.from(createDerivedSqlIdentifier(schema), tableName) - : tableName; - }); + /** + * @return Optional of {@link SqlIdentifier} representing the current entity schema. If the schema is not specified neither + * explicitly, nor via {@link NamingStrategy}, then return {@link Optional#empty()} + */ + @NotNull + private Optional determineCurrentEntitySchema() { + final Optional explicitlySpecifiedSchema = schemaName.get(); + if (explicitlySpecifiedSchema.isPresent()) { + return explicitlySpecifiedSchema; + } + return StringUtils.hasText(namingStrategy.getSchema()) + ? Optional.of(createDerivedSqlIdentifier(namingStrategy.getSchema())) + : Optional.empty(); } /* diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/Table.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/Table.java index 6b848c1cd9..19b4e21dcc 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/Table.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/Table.java @@ -15,6 +15,8 @@ */ package org.springframework.data.relational.core.mapping; +import org.springframework.core.annotation.AliasFor; + import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; @@ -27,6 +29,7 @@ * * @author Kazuki Shimizu * @author Bastian Wilhelm + * @author Mikhail Polivakha */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @@ -37,5 +40,23 @@ /** * The mapping table name. */ + @AliasFor("name") String value() default ""; -} + + /** + * The mapping table name. + */ + @AliasFor("value") + String name() default ""; + + /** + * Name of the schema (or user, for example in case of oracle), in which this table resides in + * The behavior is the following:
+ * If the {@link Table#schema()} is specified, then it will be + * used as a schema of current table, i.e. as a prefix to the name of the table, which can + * be specified in {@link Table#value()}.
+ * If the {@link Table#schema()} is not specified, then spring data will assume the default schema, + * The default schema itself can be provided by the means of {@link NamingStrategy#getSchema()} + */ + String schema() default ""; +} \ No newline at end of file diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntityImplUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntityImplUnitTests.java index c5d0e5ba73..8c9943bac0 100644 --- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntityImplUnitTests.java +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntityImplUnitTests.java @@ -31,6 +31,7 @@ * @author Kazuki Shimizu * @author Bastian Wilhelm * @author Mark Paluch + * @author Mikhail Polivakha */ public class RelationalPersistentEntityImplUnitTests { @@ -72,6 +73,33 @@ public void namingStrategyWithSchemaReturnsCompositeTableName() { .isEqualTo("\"MY_SCHEMA\".\"DUMMY_ENTITY_WITH_EMPTY_ANNOTATION\""); } + @Test // DATAJDBC-1099 + void testRelationalPersistentEntitySchemaNameChoice() { + mappingContext = new RelationalMappingContext(NamingStrategyWithSchema.INSTANCE); + final RelationalPersistentEntity persistentEntity = mappingContext.getPersistentEntity(EntityWithExplicitSchema.class); + final SqlIdentifier tableName = persistentEntity.getTableName(); + assertThat(tableName).isEqualTo(SqlIdentifier.from(SqlIdentifier.quoted("DART_VADER"), quoted("I_AM_THE_SENATE"))); + assertThat(tableName.toString()).isEqualTo("\"DART_VADER\".\"I_AM_THE_SENATE\""); + } + + @Test // DATAJDBC-1099 + void testRelationalPersistentEntityTableOnlySchemaSpecified() { + final RelationalPersistentEntity persistentEntity = mappingContext.getPersistentEntity(EntityWithSchemaFromNamingStrategy.class); + final SqlIdentifier tableName = persistentEntity.getTableName(); + assertThat(tableName).isEqualTo(SqlIdentifier.from(quoted("ANAKYN_SKYWALKER"), quoted("ENTITY_WITH_SCHEMA_FROM_NAMING_STRATEGY"))); + assertThat(tableName.toString()).isEqualTo("\"ANAKYN_SKYWALKER\".\"ENTITY_WITH_SCHEMA_FROM_NAMING_STRATEGY\""); + } + + @Table(schema = "ANAKYN_SKYWALKER") + static class EntityWithSchemaFromNamingStrategy { + @Id private Long id; + } + + @Table(schema = "DART_VADER", name = "I_AM_THE_SENATE") + static class EntityWithExplicitSchema { + @Id private Long id; + } + @Table("dummy_sub_entity") static class DummySubEntity { @Id @Column("renamedId") Long id;