DATAJDBC-221 - Added AggregateReference for references across aggregates.
AggregateReferences can be used to reference other aggregates via their aggregate root without making them part of the referencing aggregate. I.e. the referenced entities will not be included in SQL statements for the referencing aggregates, apart from their id. Conversion between AggregateReferences and their ids and vice versa is done by the RelationalConverter.
This commit is contained in:
committed by
Greg Turnquist
parent
c616e006cf
commit
577d7643dd
@@ -285,13 +285,14 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
|
||||
|
||||
persistentEntity.doWithProperties((PropertyHandler<RelationalPersistentProperty>) property -> {
|
||||
|
||||
if (!property.isEntity()) {
|
||||
|
||||
Object value = propertyAccessor.getProperty(property);
|
||||
|
||||
Object convertedValue = converter.writeValue(value, ClassTypeInformation.from(property.getColumnType()));
|
||||
parameters.addValue(property.getColumnName(), convertedValue, JdbcUtil.sqlTypeFor(property.getColumnType()));
|
||||
if (property.isEntity()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object value = propertyAccessor.getProperty(property);
|
||||
Object convertedValue = converter.writeValue(value, ClassTypeInformation.from(property.getColumnType()));
|
||||
parameters.addValue(property.getColumnName(), convertedValue, JdbcUtil.sqlTypeFor(property.getColumnType()));
|
||||
|
||||
});
|
||||
|
||||
return parameters;
|
||||
|
||||
@@ -99,7 +99,9 @@ public class EntityRowMapper<T> implements RowMapper<T> {
|
||||
|
||||
} else {
|
||||
|
||||
propertyAccessor.setProperty(property, readFrom(resultSet, property, ""));
|
||||
final Object value = readFrom(resultSet, property, "");
|
||||
|
||||
propertyAccessor.setProperty(property, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2018 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.jdbc.core.mapping;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A reference to the aggregate root of a different aggregate.
|
||||
*
|
||||
* @param <T> the type of the referenced aggregate root.
|
||||
* @param <ID> the type of the id of the referenced aggregate root.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface AggregateReference<T, ID> {
|
||||
|
||||
static <T, ID> AggregateReference<T, ID> to(ID id) {
|
||||
return new IdOnlyAggregateReference<>(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the id of the referenced aggregate. May be {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
ID getId();
|
||||
|
||||
/**
|
||||
* An {@link AggregateReference} that only holds the id of the referenced aggregate root.
|
||||
*
|
||||
* Note that there is no check that a matching aggregate for this id actually exists.
|
||||
*
|
||||
* @param <T>
|
||||
* @param <ID>
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
class IdOnlyAggregateReference<T, ID> implements AggregateReference<T, ID> {
|
||||
|
||||
private final ID id;
|
||||
|
||||
@Override
|
||||
public ID getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.data.convert.CustomConversions;
|
||||
import org.springframework.data.convert.CustomConversions.StoreConversions;
|
||||
import org.springframework.data.convert.EntityInstantiators;
|
||||
import org.springframework.data.jdbc.core.mapping.AggregateReference;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
@@ -49,6 +50,7 @@ import org.springframework.util.ClassUtils;
|
||||
* Conversion is configurable by providing a customized {@link CustomConversions}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
* @see MappingContext
|
||||
* @see SimpleTypeHolder
|
||||
* @see CustomConversions
|
||||
@@ -157,6 +159,14 @@ public class BasicRelationalConverter implements RelationalConverter {
|
||||
return conversionService.convert(value, type.getType());
|
||||
}
|
||||
|
||||
if (AggregateReference.class.isAssignableFrom(type.getType())) {
|
||||
|
||||
TypeInformation<?> idType = type.getSuperTypeInformation(AggregateReference.class)
|
||||
.getTypeArguments().get(1);
|
||||
|
||||
return AggregateReference.to(readValue(value, idType));
|
||||
}
|
||||
|
||||
return getPotentiallyConvertedSimpleRead(value, type.getType());
|
||||
}
|
||||
|
||||
@@ -172,6 +182,10 @@ public class BasicRelationalConverter implements RelationalConverter {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (AggregateReference.class.isAssignableFrom(value.getClass())) {
|
||||
return writeValue (((AggregateReference) value).getId(), type);
|
||||
}
|
||||
|
||||
Class<?> rawType = type.getType();
|
||||
RelationalPersistentEntity<?> persistentEntity = context.getPersistentEntity(value.getClass());
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.jdbc.core.mapping.AggregateReference;
|
||||
import org.springframework.data.jdbc.support.JdbcUtil;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
|
||||
@@ -96,6 +98,16 @@ class BasicRelationalPersistentProperty extends AnnotationBasedPersistentPropert
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEntity() {
|
||||
return super.isEntity() && !isReference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReference() {
|
||||
return AggregateReference.class.isAssignableFrom(getRawType());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.core.mapping.model.JdbcPersistentProperty#getColumnName()
|
||||
@@ -114,11 +126,20 @@ class BasicRelationalPersistentProperty extends AnnotationBasedPersistentPropert
|
||||
@Override
|
||||
public Class getColumnType() {
|
||||
|
||||
if (isReference()) {
|
||||
return columnTypeForReference();
|
||||
}
|
||||
|
||||
Class columnType = columnTypeIfEntity(getActualType());
|
||||
|
||||
return columnType == null ? columnTypeForNonEntity(getActualType()) : columnType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSqlType() {
|
||||
return JdbcUtil.sqlTypeFor(getColumnType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RelationalPersistentEntity<?> getOwner() {
|
||||
return (RelationalPersistentEntity<?>) super.getOwner();
|
||||
@@ -178,4 +199,13 @@ class BasicRelationalPersistentProperty extends AnnotationBasedPersistentPropert
|
||||
.findFirst() //
|
||||
.orElseGet(() -> ClassUtils.resolvePrimitiveIfNecessary(type));
|
||||
}
|
||||
|
||||
private Class columnTypeForReference() {
|
||||
|
||||
Class<?> componentType = getTypeInformation().getRequiredComponentType().getType();
|
||||
RelationalPersistentEntity<?> referencedEntity = context.getRequiredPersistentEntity(componentType);
|
||||
|
||||
return referencedEntity.getRequiredIdProperty().getColumnType();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.relational.core.mapping;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import org.springframework.data.jdbc.core.mapping.AggregateReference;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes;
|
||||
import org.springframework.data.mapping.context.AbstractMappingContext;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
@@ -78,4 +79,9 @@ public class RelationalMappingContext
|
||||
RelationalPersistentEntity<?> owner, SimpleTypeHolder simpleTypeHolder) {
|
||||
return new BasicRelationalPersistentProperty(property, owner, simpleTypeHolder, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> type) {
|
||||
return super.shouldCreatePersistentEntityFor(type) && !AggregateReference.class.isAssignableFrom(type.getType());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.relational.core.mapping;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -26,6 +27,8 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
public interface RelationalPersistentProperty extends PersistentProperty<RelationalPersistentProperty> {
|
||||
|
||||
boolean isReference();
|
||||
|
||||
/**
|
||||
* Returns the name of the column backing this property.
|
||||
*
|
||||
@@ -40,6 +43,14 @@ public interface RelationalPersistentProperty extends PersistentProperty<Relatio
|
||||
*/
|
||||
Class<?> getColumnType();
|
||||
|
||||
/**
|
||||
* The SQL type constant used when using this property as a parameter for a SQL statement.
|
||||
* @return Must not be {@code null}.
|
||||
*
|
||||
* @see java.sql.Types
|
||||
*/
|
||||
int getSqlType();
|
||||
|
||||
@Override
|
||||
RelationalPersistentEntity<?> getOwner();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user