diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/mapping/BasicJdbcPersistentPropertyUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/mapping/BasicJdbcPersistentPropertyUnitTests.java index 9c690dc417..5b631d6807 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/mapping/BasicJdbcPersistentPropertyUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/mapping/BasicJdbcPersistentPropertyUnitTests.java @@ -31,6 +31,7 @@ import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.relational.core.mapping.BasicRelationalPersistentProperty; import org.springframework.data.relational.core.mapping.Column; +import org.springframework.data.relational.core.mapping.MappedCollection; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; @@ -137,7 +138,7 @@ private static class DummyEntity { private final List listField; private final UUID uuid; - @Column(value = "dummy_column_name", keyColumn = "dummy_key_column_name") private List someList; + @MappedCollection(idColumn = "dummy_column_name", keyColumn = "dummy_key_column_name") private List someList; // DATACMNS-106 private @Column("dummy_name") String name; diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests.java index e965e37608..0f35ce8c21 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests.java @@ -32,6 +32,7 @@ import org.springframework.data.jdbc.testing.TestConfiguration; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Embedded; +import org.springframework.data.relational.core.mapping.MappedCollection; import org.springframework.data.repository.CrudRepository; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; @@ -254,7 +255,7 @@ private static class DummyEntity { @Data private static class Embeddable { - @Column(value = "id", keyColumn = "order_key") + @MappedCollection(idColumn = "id", keyColumn = "order_key") List list = new ArrayList<>(); String test; diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java index d6fc49805d..91e3b5f137 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java @@ -21,8 +21,10 @@ import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.stream.Stream; import org.springframework.data.mapping.Association; import org.springframework.data.mapping.PersistentEntity; @@ -57,7 +59,8 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent private final RelationalMappingContext context; private final Lazy> columnName; - private final Lazy> keyColumnName; + private final Lazy> collectionIdColumnName; + private final Lazy> collectionKeyColumnName; private final Lazy isEmbedded; private final Lazy embeddedPrefix; private final Lazy> columnType = Lazy.of(this::doGetColumnType); @@ -88,13 +91,37 @@ public BasicRelationalPersistentProperty(Property property, PersistentEntity Optional.ofNullable( // findAnnotation(Column.class)) // .map(Column::value) // - .filter(StringUtils::hasText) // + .filter(StringUtils::hasText)// ); - this.keyColumnName = Lazy.of(() -> Optional.ofNullable( // - findAnnotation(Column.class)) // - .map(Column::keyColumn) // - .filter(StringUtils::hasText) // + this.collectionIdColumnName = Lazy.of(() -> + Stream.concat( // + Stream.of( // + findAnnotation(MappedCollection.class)) // + .filter(Objects::nonNull) // + .map(MappedCollection::idColumn), // + Stream.of( // + findAnnotation(Column.class)) // + .filter(Objects::nonNull) // + .map(Column::value) // + ) + .filter(StringUtils::hasText) + .findFirst() + ); + + this.collectionKeyColumnName = Lazy.of(() -> + Stream.concat( // + Stream.of( // + findAnnotation(MappedCollection.class)) // + .filter(Objects::nonNull) // + .map(MappedCollection::keyColumn), // + Stream.of( // + findAnnotation(Column.class)) // + .filter(Objects::nonNull) // + .map(Column::keyColumn) // + ) + .filter(StringUtils::hasText) + .findFirst() ); } @@ -173,14 +200,14 @@ public RelationalPersistentEntity getOwner() { @Override public String getReverseColumnName() { - return columnName.get().orElseGet(() -> context.getNamingStrategy().getReverseColumnName(this)); + return collectionIdColumnName.get().orElseGet(() -> context.getNamingStrategy().getReverseColumnName(this)); } @Override public String getKeyColumn() { if (isQualified()) { - return keyColumnName.get().orElseGet(() -> context.getNamingStrategy().getKeyColumn(this)); + return collectionKeyColumnName.get().orElseGet(() -> context.getNamingStrategy().getKeyColumn(this)); } else { return null; } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/Column.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/Column.java index cbb01e90cb..549e61b40e 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/Column.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/Column.java @@ -26,6 +26,7 @@ * * @author Kazuki Shimizu * @author Florian Lüdiger + * @author Bastian Wilhelm */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @@ -39,6 +40,9 @@ /** * The column name for key columns of List or Map collections. + * + * @deprecated since 1.1, was used for collection mapping. Use {@link MappedCollection} instead of this. */ + @Deprecated String keyColumn() default ""; } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/MappedCollection.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/MappedCollection.java new file mode 100644 index 0000000000..1df6a893b0 --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/MappedCollection.java @@ -0,0 +1,48 @@ +/* + * Copyright 2019 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 + * + * http://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.mapping; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The annotation to configure the mapping from an collection in the database. + * + * @since 1.1 + * @author Bastian Wilhelm + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE }) +@Documented +public @interface MappedCollection { + + /** + * The column name for id column in the corresponding relationship table. + * If the default value (empty String) is used, the column name is resolved by the used + * {@link NamingStrategy} method {@link NamingStrategy#getReverseColumnName(RelationalPersistentProperty)} + */ + String idColumn() default ""; + + /** + * The column name for key columns of List or Map collections in the corresponding relationship table. + * If the default value (empty String) is used, the column name is resolved by the used + * {@link NamingStrategy} method {@link NamingStrategy#getKeyColumn(RelationalPersistentProperty)} + */ + String keyColumn() default ""; +} diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java index aea002609a..28741fd6a8 100644 --- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java @@ -182,7 +182,7 @@ private static class DummyEntity { private final List listOfEntity; private final OtherEntity[] arrayOfEntity; - @Column(value = "dummy_column_name", keyColumn = "dummy_key_column_name") private List someList; + @MappedCollection(idColumn = "dummy_column_name", keyColumn = "dummy_key_column_name") private List someList; // DATACMNS-106 private @Column("dummy_name") String name; diff --git a/src/main/asciidoc/jdbc.adoc b/src/main/asciidoc/jdbc.adoc index 8796935cff..07fcf33e35 100644 --- a/src/main/asciidoc/jdbc.adoc +++ b/src/main/asciidoc/jdbc.adoc @@ -136,7 +136,7 @@ You can change this name by implementing `NamingStrategy.getReverseColumnName(Re * `Map` is considered a qualified one-to-many relationship. The table of the referenced entity is expected to have two additional columns: One named the same as the table of the referencing entity for the foreign key and one with the same name and an additional `_key` suffix for the map key. You can change this behavior by implementing `NamingStrategy.getReverseColumnName(RelationalPersistentProperty property)` and `NamingStrategy.getKeyColumn(RelationalPersistentProperty property)`, respectively. -Alternatively you may annotate the attribute with `@Column(value="your_column_name", keyColumn="your_key_column_name")` +Alternatively you may annotate the attribute with `@MappedCollection(idColumn="your_column_name", keyColumn="your_key_column_name")` * `List` is mapped as a `Map`. @@ -233,10 +233,10 @@ public class MyEntity { ---- ==== -The {javadoc-base}org/springframework/data/relational/core/mapping/Column.html[`@Column`] annotation can also be used on a reference type (one-to-one relationship) or on Sets, Lists, and Maps (one-to-many relationship) +The {javadoc-base}org/springframework/data/relational/core/mapping/MappedCollection.html[`@MappedCollection`] annotation can be used on a reference type (one-to-one relationship) or on Sets, Lists, and Maps (one-to-many relationship) On all these types the `value` element of the annotation is used to provide a custom name for the foreign key column referencing the id column in the other table. In the following example the corresponding table for the `MySubEntity` class has a name column, and the id column of the `MyEntity` id for relationship reasons. -The name of this `MySubEntity` class's id column can also be customized with the `value` element of the {javadoc-base}org/springframework/data/relational/core/mapping/Column.html[`@Column`] annotation: +The name of this `MySubEntity` class's id column can also be customized with the `idColumn` element of the {javadoc-base}org/springframework/data/relational/core/mapping/MappedCollection.html[`@MappedCollection`] annotation: ==== [source, java] @@ -245,7 +245,7 @@ public class MyEntity { @Id Integer id; - @Column("CUSTOM_COLUMN_NAME") + @MappedCollection(idColumn = "CUSTOM_COLUMN_NAME") Set name; } @@ -256,7 +256,7 @@ public class MySubEntity { ==== When using `List` and `Map` you must have an additional column for the position of a dataset in the `List` or the key value of the entity in the `Map`. -This additional column name may be customized with the `keyColumn` Element of the {javadoc-base}org/springframework/data/relational/core/mapping/Column.html[`@Column`] annotation: +This additional column name may be customized with the `keyColumn` Element of the {javadoc-base}org/springframework/data/relational/core/mapping/MappedCollection.html[`@MappedCollection`] annotation: ==== [source, java] @@ -265,7 +265,7 @@ public class MyEntity { @Id Integer id; - @Column(value = "CUSTOM_COLUMN_NAME", keyColumn = "CUSTOM_KEY_COLUMN_NAME") + @MappedCollection(idColumn = "CUSTOM_COLUMN_NAME", keyColumn = "CUSTOM_KEY_COLUMN_NAME") List name; }