DATAJDBC-331 Introduce @MappedCollection annotation.

We now provide a @MappedCollection annotation as replacement for @Column's keyColumn attribute.

class Person {
  @MappedCollection(idColumn = "the_id", keyColumn = "the_key")
  private List<Integer> mappedList;
}

Original pull request: #117.
This commit is contained in:
Bastian Wilhelm
2019-02-08 22:18:47 +01:00
committed by Mark Paluch
parent 1c0bd4fdb8
commit 6fcff3ea80
7 changed files with 98 additions and 17 deletions

View File

@@ -31,6 +31,7 @@ import org.springframework.data.annotation.Id;
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 @@ public class BasicJdbcPersistentPropertyUnitTests {
private final List<String> listField;
private final UUID uuid;
@Column(value = "dummy_column_name", keyColumn = "dummy_key_column_name") private List<Integer> someList;
@MappedCollection(idColumn = "dummy_column_name", keyColumn = "dummy_key_column_name") private List<Integer> someList;
// DATACMNS-106
private @Column("dummy_name") String name;

View File

@@ -32,6 +32,7 @@ import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
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 @@ public class JdbcRepositoryEmbeddedWithCollectionIntegrationTests {
@Data
private static class Embeddable {
@Column(value = "id", keyColumn = "order_key")
@MappedCollection(idColumn = "id", keyColumn = "order_key")
List<DummyEntity2> list = new ArrayList<>();
String test;

View File

@@ -21,8 +21,10 @@ import java.time.temporal.Temporal;
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<Optional<String>> columnName;
private final Lazy<Optional<String>> keyColumnName;
private final Lazy<Optional<String>> collectionIdColumnName;
private final Lazy<Optional<String>> collectionKeyColumnName;
private final Lazy<Boolean> isEmbedded;
private final Lazy<String> embeddedPrefix;
private final Lazy<Class<?>> columnType = Lazy.of(this::doGetColumnType);
@@ -88,13 +91,37 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent
this.columnName = Lazy.of(() -> 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 class BasicRelationalPersistentProperty extends AnnotationBasedPersistent
@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;
}

View File

@@ -26,6 +26,7 @@ import java.lang.annotation.Target;
*
* @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 @@ public @interface Column {
/**
* 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 "";
}

View File

@@ -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 "";
}

View File

@@ -182,7 +182,7 @@ public class BasicRelationalPersistentPropertyUnitTests {
private final List<OtherEntity> listOfEntity;
private final OtherEntity[] arrayOfEntity;
@Column(value = "dummy_column_name", keyColumn = "dummy_key_column_name") private List<Integer> someList;
@MappedCollection(idColumn = "dummy_column_name", keyColumn = "dummy_key_column_name") private List<Integer> someList;
// DATACMNS-106
private @Column("dummy_name") String name;

View File

@@ -136,7 +136,7 @@ You can change this name by implementing `NamingStrategy.getReverseColumnName(Re
* `Map<simple type, some entity>` 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<some entity>` is mapped as a `Map<Integer, some entity>`.
@@ -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<MySubEntity> 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<MySubEntity> name;
}