Search in sources :

Example 26 with Association

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]);
}
Also used : Association(io.micronaut.data.model.Association) ArrayList(java.util.ArrayList)

Example 27 with Association

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);
        }
    }
}
Also used : RuntimeAssociation(io.micronaut.data.model.runtime.RuntimeAssociation) Association(io.micronaut.data.model.Association) RuntimeAssociation(io.micronaut.data.model.runtime.RuntimeAssociation) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map)

Example 28 with Association

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;
}
Also used : RuntimeAssociation(io.micronaut.data.model.runtime.RuntimeAssociation) Association(io.micronaut.data.model.Association) ArrayList(java.util.ArrayList) RuntimeAssociation(io.micronaut.data.model.runtime.RuntimeAssociation) PersistentAssociationPath(io.micronaut.data.model.PersistentAssociationPath) BeanProperty(io.micronaut.core.beans.BeanProperty)

Example 29 with Association

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);
    }
}
Also used : AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) Relation(io.micronaut.data.annotation.Relation) RuntimeAssociation(io.micronaut.data.model.runtime.RuntimeAssociation) Association(io.micronaut.data.model.Association) Embedded(io.micronaut.data.model.Embedded) DataAccessException(io.micronaut.data.exceptions.DataAccessException) RuntimeAssociation(io.micronaut.data.model.runtime.RuntimeAssociation) EmbeddedId(io.micronaut.data.annotation.EmbeddedId) Embeddable(io.micronaut.data.annotation.Embeddable) BeanProperty(io.micronaut.core.beans.BeanProperty) RuntimePersistentProperty(io.micronaut.data.model.runtime.RuntimePersistentProperty) InstantiationException(io.micronaut.core.reflect.exception.InstantiationException) Nullable(io.micronaut.core.annotation.Nullable)

Example 30 with Association

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());
            }
        }
    }
}
Also used : ListIterator(java.util.ListIterator) BeanProperty(io.micronaut.core.beans.BeanProperty) ArgumentConversionContext(io.micronaut.core.convert.ArgumentConversionContext) ConversionErrorException(io.micronaut.core.convert.exceptions.ConversionErrorException) Internal(io.micronaut.core.annotation.Internal) ArrayList(java.util.ArrayList) Embedded(io.micronaut.data.model.Embedded) List(java.util.List) ConversionContext(io.micronaut.core.convert.ConversionContext) Association(io.micronaut.data.model.Association) CollectionUtils(io.micronaut.core.util.CollectionUtils) AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) Argument(io.micronaut.core.type.Argument) RuntimeAssociation(io.micronaut.data.model.runtime.RuntimeAssociation) RuntimePersistentEntity(io.micronaut.data.model.runtime.RuntimePersistentEntity) ConversionService(io.micronaut.core.convert.ConversionService) Collections(java.util.Collections) Relation(io.micronaut.data.annotation.Relation) PersistentAssociationPath(io.micronaut.data.model.PersistentAssociationPath) RuntimePersistentEntity(io.micronaut.data.model.runtime.RuntimePersistentEntity) ArrayList(java.util.ArrayList) BeanProperty(io.micronaut.core.beans.BeanProperty) Embedded(io.micronaut.data.model.Embedded) PersistentAssociationPath(io.micronaut.data.model.PersistentAssociationPath)

Aggregations

Association (io.micronaut.data.model.Association)46 PersistentProperty (io.micronaut.data.model.PersistentProperty)21 ArrayList (java.util.ArrayList)21 Embedded (io.micronaut.data.model.Embedded)17 StringJoiner (java.util.StringJoiner)16 PersistentPropertyPath (io.micronaut.data.model.PersistentPropertyPath)15 NonNull (io.micronaut.core.annotation.NonNull)14 PersistentEntity (io.micronaut.data.model.PersistentEntity)14 List (java.util.List)14 AnnotationMetadata (io.micronaut.core.annotation.AnnotationMetadata)13 Relation (io.micronaut.data.annotation.Relation)11 NamingStrategy (io.micronaut.data.model.naming.NamingStrategy)11 JoinPath (io.micronaut.data.model.query.JoinPath)11 Map (java.util.Map)11 HashMap (java.util.HashMap)10 Optional (java.util.Optional)10 Collectors (java.util.stream.Collectors)10 MappedEntity (io.micronaut.data.annotation.MappedEntity)9 QueryModel (io.micronaut.data.model.query.QueryModel)9 Collections (java.util.Collections)9