DATAJPA-413 - Improved handling of entities annotated with @IdClass.

Added special handling for IdClass' typed id values to prevent
nested identifier classes from failing to be populated in JpaMetamodelEntityInformation.

Original pull request: #133.
This commit is contained in:
Thomas Darimont
2015-02-10 15:34:51 +01:00
committed by Oliver Gierke
parent edf63705c9
commit b0d03050cb

View File

@@ -36,6 +36,7 @@ import org.springframework.beans.BeanWrapperImpl;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Implementation of {@link org.springframework.data.repository.core.EntityInformation} that uses JPA {@link Metamodel}
@@ -321,20 +322,63 @@ public class JpaMetamodelEntityInformation<T, ID extends Serializable> extends J
@Override
public void setPropertyValue(String propertyName, Object value) {
if (isIdentifierDerivationNecessary(value)) {
// Derive the identifer from the nested entity that is part of the composite key.
@SuppressWarnings({ "rawtypes", "unchecked" })
JpaMetamodelEntityInformation nestedEntityInformation = new JpaMetamodelEntityInformation(value.getClass(),
this.metamodel);
Object nestedIdPropertyValue = new DirectFieldAccessFallbackBeanWrapper(value)
.getPropertyValue(nestedEntityInformation.getIdAttribute().getName());
super.setPropertyValue(propertyName, nestedIdPropertyValue);
if (!isIdentifierDerivationNecessary(value)) {
super.setPropertyValue(propertyName, value);
return;
}
super.setPropertyValue(propertyName, value);
// Derive the identifier from the nested entity that is part of the composite key.
@SuppressWarnings({ "rawtypes", "unchecked" })
JpaMetamodelEntityInformation nestedEntityInformation = new JpaMetamodelEntityInformation(value.getClass(),
this.metamodel);
if (!nestedEntityInformation.getJavaType().isAnnotationPresent(IdClass.class)) {
Object nestedIdPropertyValue = new DirectFieldAccessFallbackBeanWrapper(value)
.getPropertyValue(nestedEntityInformation.getIdAttribute().getName());
super.setPropertyValue(propertyName, nestedIdPropertyValue);
return;
}
// We have an IdClass property, we need to inspect the current value in order to map potentially multiple id
// properties correctly.
BeanWrapper sourceIdValueWrapper = new DirectFieldAccessFallbackBeanWrapper(value);
BeanWrapper targetIdClassTypeWrapper = new BeanWrapperImpl(nestedEntityInformation.getIdType());
for (String idAttributeName : (Iterable<String>) nestedEntityInformation.getIdAttributeNames()) {
targetIdClassTypeWrapper.setPropertyValue(idAttributeName,
extractActualIdPropertyValue(sourceIdValueWrapper, idAttributeName));
}
super.setPropertyValue(propertyName, targetIdClassTypeWrapper.getWrappedInstance());
}
private Object extractActualIdPropertyValue(BeanWrapper sourceIdValueWrapper, String idAttributeName) {
Object idPropertyValue = sourceIdValueWrapper.getPropertyValue(idAttributeName);
Class<? extends Object> idPropertyValueType = idPropertyValue.getClass();
if (ClassUtils.isPrimitiveOrWrapper(idPropertyValueType)) {
return idPropertyValue;
}
return new DirectFieldAccessFallbackBeanWrapper(idPropertyValue)
.getPropertyValue(tryFindSingularIdAttributeNameOrUseFallback(idPropertyValueType, idAttributeName));
}
private String tryFindSingularIdAttributeNameOrUseFallback(Class<? extends Object> idPropertyValueType,
String fallbackIdTypePropertyName) {
ManagedType<? extends Object> idPropertyType = metamodel.managedType(idPropertyValueType);
for (SingularAttribute<?, ?> sa : idPropertyType.getSingularAttributes()) {
if (sa.isId()) {
return sa.getName();
}
}
return fallbackIdTypePropertyName;
}
/**