use of io.micronaut.data.model.Association in project micronaut-data by micronaut-projects.
the class SourceParameterExpressionImpl method asStringPath.
private String[] asStringPath(List<Association> associations, PersistentProperty property) {
if (associations.isEmpty()) {
return new String[] { property.getName() };
}
List<String> path = new ArrayList<>(associations.size() + 1);
for (Association association : associations) {
path.add(association.getName());
}
path.add(property.getName());
return path.toArray(new String[0]);
}
use of io.micronaut.data.model.Association in project micronaut-data by micronaut-projects.
the class SqlResultEntityTypeMapper method readChildren.
private void readChildren(RS rs, Object instance, Object parent, MappingContext<R> ctx) {
if (ctx.manyAssociations != null) {
Object id = readEntityId(rs, ctx);
MappingContext associatedCtx = ctx.manyAssociations.get(id);
if (associatedCtx == null) {
associatedCtx = ctx.copy();
R entity = (R) readEntity(rs, associatedCtx, parent, id);
Objects.requireNonNull(id);
ctx.associate(associatedCtx, id, entity);
} else {
readChildren(rs, instance, parent, associatedCtx);
}
return;
}
if (ctx.associations != null) {
for (Map.Entry<Association, MappingContext> e : ctx.associations.entrySet()) {
MappingContext associationCtx = e.getValue();
RuntimeAssociation runtimeAssociation = (RuntimeAssociation) e.getKey();
Object in = instance == null || !runtimeAssociation.getKind().isSingleEnded() ? null : runtimeAssociation.getProperty().get(instance);
readChildren(rs, in, instance, associationCtx);
}
}
}
use of io.micronaut.data.model.Association in project micronaut-data by micronaut-projects.
the class SqlResultEntityTypeMapper method setChildrenAndTriggerPostLoad.
private Object setChildrenAndTriggerPostLoad(Object instance, MappingContext<?> ctx, Object parent) {
if (ctx.manyAssociations != null) {
List<Object> values = new ArrayList<>(ctx.manyAssociations.size());
for (MappingContext associationCtx : ctx.manyAssociations.values()) {
values.add(setChildrenAndTriggerPostLoad(associationCtx.entity, associationCtx, parent));
}
return values;
} else if (ctx.associations != null) {
for (Map.Entry<Association, MappingContext> e : ctx.associations.entrySet()) {
MappingContext associationCtx = e.getValue();
RuntimeAssociation runtimeAssociation = (RuntimeAssociation) e.getKey();
BeanProperty beanProperty = runtimeAssociation.getProperty();
if (runtimeAssociation.getKind().isSingleEnded() && (associationCtx.manyAssociations == null || associationCtx.manyAssociations.isEmpty())) {
Object value = beanProperty.get(instance);
Object newValue = setChildrenAndTriggerPostLoad(value, associationCtx, instance);
if (newValue != value) {
instance = setProperty(beanProperty, instance, newValue);
}
} else {
Object newValue = setChildrenAndTriggerPostLoad(null, associationCtx, instance);
newValue = resultReader.convertRequired(newValue == null ? new ArrayList<>() : newValue, beanProperty.getType());
instance = setProperty(beanProperty, instance, newValue);
}
}
}
if (instance != null && (ctx.association == null || ctx.jp != null)) {
if (parent != null && ctx.association != null && ctx.association.isBidirectional()) {
PersistentAssociationPath inverse = ctx.association.getInversePathSide().orElseThrow(IllegalStateException::new);
Association association = inverse.getAssociation();
if (association.getKind().isSingleEnded()) {
Object inverseInstance = inverse.getPropertyValue(instance);
if (inverseInstance != parent) {
instance = inverse.setPropertyValue(instance, parent);
}
}
}
triggerPostLoad(ctx.persistentEntity, instance);
}
return instance;
}
use of io.micronaut.data.model.Association in project micronaut-data by micronaut-projects.
the class SqlResultEntityTypeMapper method readEntity.
@Nullable
private <K> K readEntity(RS rs, MappingContext<K> ctx, @Nullable Object parent, @Nullable Object resolveId) {
RuntimePersistentEntity<K> persistentEntity = ctx.persistentEntity;
BeanIntrospection<K> introspection = persistentEntity.getIntrospection();
RuntimePersistentProperty<K>[] constructorArguments = persistentEntity.getConstructorArguments();
try {
RuntimePersistentProperty<K> identity = persistentEntity.getIdentity();
final boolean isAssociation = ctx.association != null;
final boolean isEmbedded = ctx.association instanceof Embedded;
final boolean nullableEmbedded = isEmbedded && ctx.association.isOptional();
Object id = resolveId == null ? readEntityId(rs, ctx) : resolveId;
if (id == null && !isEmbedded && isAssociation) {
return null;
}
K entity;
if (ArrayUtils.isEmpty(constructorArguments)) {
entity = introspection.instantiate();
} else {
int len = constructorArguments.length;
Object[] args = new Object[len];
for (int i = 0; i < len; i++) {
RuntimePersistentProperty<K> prop = constructorArguments[i];
if (prop != null) {
if (prop instanceof Association) {
RuntimeAssociation entityAssociation = (RuntimeAssociation) prop;
if (prop instanceof Embedded) {
args[i] = readEntity(rs, ctx.embedded((Embedded) prop), null, null);
} else {
final Relation.Kind kind = entityAssociation.getKind();
final boolean isInverse = parent != null && isAssociation && ctx.association.getOwner() == entityAssociation.getAssociatedEntity();
if (isInverse && kind.isSingleEnded()) {
args[i] = parent;
} else {
MappingContext<K> joinCtx = ctx.join(joinPaths, entityAssociation);
Object resolvedId = null;
if (!entityAssociation.isForeignKey()) {
resolvedId = readEntityId(rs, ctx.path(entityAssociation));
}
if (kind.isSingleEnded()) {
if (joinCtx.jp == null || resolvedId == null && !entityAssociation.isForeignKey()) {
args[i] = buildIdOnlyEntity(rs, ctx.path(entityAssociation), resolvedId);
} else {
args[i] = readEntity(rs, joinCtx, null, resolvedId);
}
} else if (entityAssociation.getProperty().isReadOnly()) {
// For constructor-only properties (records) always set empty collection and replace later
args[i] = resultReader.convertRequired(new ArrayList<>(0), entityAssociation.getProperty().getType());
if (joinCtx.jp != null) {
MappingContext<K> associatedCtx = joinCtx.copy();
if (resolvedId == null) {
resolvedId = readEntityId(rs, associatedCtx);
}
Object associatedEntity = null;
if (resolvedId != null || entityAssociation.isForeignKey()) {
associatedEntity = readEntity(rs, associatedCtx, null, resolvedId);
}
if (associatedEntity != null) {
joinCtx.associate(associatedCtx, resolvedId, associatedEntity);
}
}
}
}
}
} else {
Object v;
if (resolveId != null && prop.equals(identity)) {
v = resolveId;
} else {
v = readProperty(rs, ctx, prop);
if (v == null) {
if (!prop.isOptional() && !nullableEmbedded) {
AnnotationMetadata entityAnnotationMetadata = ctx.persistentEntity.getAnnotationMetadata();
if (entityAnnotationMetadata.hasAnnotation(Embeddable.class) || entityAnnotationMetadata.hasAnnotation(EmbeddedId.class)) {
return null;
}
throw new DataAccessException("Null value read for non-null constructor argument [" + prop.getName() + "] of type: " + persistentEntity.getName());
} else {
args[i] = null;
continue;
}
}
}
args[i] = convert(prop, v);
}
} else {
throw new DataAccessException("Constructor argument [" + constructorArguments[i].getName() + "] must have an associated getter.");
}
}
if (nullableEmbedded && args.length > 0 && Arrays.stream(args).allMatch(Objects::isNull)) {
return null;
} else {
entity = introspection.instantiate(args);
}
}
if (id != null && identity != null) {
@SuppressWarnings("unchecked") BeanProperty<K, Object> idProperty = (BeanProperty<K, Object>) identity.getProperty();
entity = (K) convertAndSetWithValue(entity, identity, idProperty, id);
}
RuntimePersistentProperty<K> version = persistentEntity.getVersion();
if (version != null) {
Object v = readProperty(rs, ctx, version);
if (v != null) {
entity = (K) convertAndSetWithValue(entity, version, version.getProperty(), v);
}
}
for (RuntimePersistentProperty<K> rpp : persistentEntity.getPersistentProperties()) {
if (rpp.isReadOnly()) {
continue;
} else if (rpp.isConstructorArgument()) {
if (rpp instanceof Association) {
Association a = (Association) rpp;
final Relation.Kind kind = a.getKind();
if (kind.isSingleEnded()) {
continue;
}
} else {
continue;
}
}
@SuppressWarnings("unchecked") BeanProperty<K, Object> property = (BeanProperty<K, Object>) rpp.getProperty();
if (rpp instanceof Association) {
Association entityAssociation = (Association) rpp;
if (rpp instanceof Embedded) {
Object value = readEntity(rs, ctx.embedded((Embedded) rpp), parent == null ? entity : parent, null);
entity = setProperty(property, entity, value);
} else {
final boolean isInverse = parent != null && entityAssociation.getKind().isSingleEnded() && isAssociation && ctx.association.getOwner() == entityAssociation.getAssociatedEntity();
if (isInverse) {
entity = setProperty(property, entity, parent);
} else {
MappingContext<K> joinCtx = ctx.join(joinPaths, entityAssociation);
Object associatedId = null;
if (!entityAssociation.isForeignKey()) {
associatedId = readEntityId(rs, ctx.path(entityAssociation));
if (associatedId == null) {
continue;
}
}
if (joinCtx.jp != null) {
if (entityAssociation.getKind().isSingleEnded()) {
Object associatedEntity = readEntity(rs, joinCtx, entity, associatedId);
entity = setProperty(property, entity, associatedEntity);
} else {
MappingContext<K> associatedCtx = joinCtx.copy();
if (associatedId == null) {
associatedId = readEntityId(rs, associatedCtx);
}
Object associatedEntity = readEntity(rs, associatedCtx, entity, associatedId);
if (associatedEntity != null) {
Objects.requireNonNull(associatedId);
joinCtx.associate(associatedCtx, associatedId, associatedEntity);
}
}
} else if (entityAssociation.getKind().isSingleEnded() && !entityAssociation.isForeignKey()) {
Object value = buildIdOnlyEntity(rs, ctx.path(entityAssociation), associatedId);
entity = setProperty(property, entity, value);
}
}
}
} else {
Object v = readProperty(rs, ctx, rpp);
if (v != null) {
entity = (K) convertAndSetWithValue(entity, rpp, property, v);
}
}
}
return entity;
} catch (InstantiationException e) {
throw new DataAccessException("Error instantiating entity [" + persistentEntity.getName() + "]: " + e.getMessage(), e);
}
}
use of io.micronaut.data.model.Association in project micronaut-data by micronaut-projects.
the class AbstractCascadeOperations method cascade.
/**
* Cascade on the entity instance and collect cascade operations.
*
* @param annotationMetadata The annotationMetadata
* @param repositoryType The repositoryType
* @param fkOnly Is FK only
* @param cascadeType The cascadeType
* @param ctx The cascade context
* @param persistentEntity The persistent entity
* @param entity The entity instance
* @param cascadeOps The cascade operations
* @param <T> The entity type
*/
protected <T> void cascade(AnnotationMetadata annotationMetadata, Class<?> repositoryType, boolean fkOnly, Relation.Cascade cascadeType, CascadeContext ctx, RuntimePersistentEntity<T> persistentEntity, T entity, List<CascadeOp> cascadeOps) {
for (RuntimeAssociation<T> association : persistentEntity.getAssociations()) {
BeanProperty<T, Object> beanProperty = (BeanProperty<T, Object>) association.getProperty();
Object child = beanProperty.get(entity);
if (child == null) {
continue;
}
if (association instanceof Embedded) {
cascade(annotationMetadata, repositoryType, fkOnly, cascadeType, ctx.embedded(association), (RuntimePersistentEntity) association.getAssociatedEntity(), child, cascadeOps);
continue;
}
if (association.doesCascade(cascadeType) && (fkOnly || !association.isForeignKey())) {
if (association.getInverseSide().map(assoc -> ctx.rootAssociations.contains(assoc) || ctx.associations.contains(assoc)).orElse(false)) {
continue;
}
final RuntimePersistentEntity<Object> associatedEntity = (RuntimePersistentEntity<Object>) association.getAssociatedEntity();
switch(association.getKind()) {
case ONE_TO_ONE:
case MANY_TO_ONE:
cascadeOps.add(new CascadeOneOp(annotationMetadata, repositoryType, ctx.relation(association), cascadeType, associatedEntity, child));
continue;
case ONE_TO_MANY:
case MANY_TO_MANY:
final PersistentAssociationPath inverse = association.getInversePathSide().orElse(null);
Iterable<Object> children = (Iterable<Object>) association.getProperty().get(entity);
if (children == null || !children.iterator().hasNext()) {
continue;
}
if (inverse != null && inverse.getAssociation().getKind() == Relation.Kind.MANY_TO_ONE) {
List<Object> entities = new ArrayList<>(CollectionUtils.iterableToList(children));
for (ListIterator<Object> iterator = entities.listIterator(); iterator.hasNext(); ) {
Object c = iterator.next();
Object newC = inverse.setPropertyValue(c, entity);
if (c != newC) {
iterator.set(newC);
}
}
children = entities;
}
cascadeOps.add(new CascadeManyOp(annotationMetadata, repositoryType, ctx.relation(association), cascadeType, associatedEntity, children));
continue;
default:
throw new IllegalArgumentException("Cannot cascade for relation: " + association.getKind());
}
}
}
}
Aggregations