Search in sources :

Example 1 with JoinPath

use of io.micronaut.data.model.query.JoinPath in project micronaut-data by micronaut-projects.

the class DefaultStoredQuery method getJoinFetchPaths.

@NonNull
@Override
public Set<JoinPath> getJoinFetchPaths() {
    if (joinFetchPaths == null) {
        Set<JoinPath> set = method.getAnnotationValuesByType(Join.class).stream().filter(this::isJoinFetch).map(av -> {
            String path = av.stringValue().orElseThrow(() -> new IllegalStateException("Should not include annotations without a value definition"));
            String alias = av.stringValue("alias").orElse(null);
            // only the alias and path is needed, don't materialize the rest
            return new JoinPath(path, new Association[0], Join.Type.DEFAULT, alias);
        }).collect(Collectors.toSet());
        this.joinFetchPaths = set.isEmpty() ? Collections.emptySet() : Collections.unmodifiableSet(set);
    }
    return joinFetchPaths;
}
Also used : DataType(io.micronaut.data.model.DataType) QueryParameterBinding(io.micronaut.data.model.runtime.QueryParameterBinding) Join(io.micronaut.data.annotation.Join) RepositoryConfiguration(io.micronaut.data.annotation.RepositoryConfiguration) TypeRole(io.micronaut.data.annotation.TypeRole) HashMap(java.util.HashMap) Internal(io.micronaut.core.annotation.Internal) ExecutableMethod(io.micronaut.inject.ExecutableMethod) ArrayList(java.util.ArrayList) DataMethod(io.micronaut.data.intercept.annotation.DataMethod) StoredQuery(io.micronaut.data.model.runtime.StoredQuery) SqlQueryBuilder(io.micronaut.data.model.query.builder.sql.SqlQueryBuilder) Map(java.util.Map) META_MEMBER_PAGE_SIZE(io.micronaut.data.intercept.annotation.DataMethod.META_MEMBER_PAGE_SIZE) Query(io.micronaut.data.annotation.Query) Set(java.util.Set) Collectors(java.util.stream.Collectors) ReflectionUtils(io.micronaut.core.reflect.ReflectionUtils) QueryHint(io.micronaut.data.annotation.QueryHint) Objects(java.util.Objects) NonNull(io.micronaut.core.annotation.NonNull) StringUtils(io.micronaut.core.util.StringUtils) JoinPath(io.micronaut.data.model.query.JoinPath) List(java.util.List) Association(io.micronaut.data.model.Association) AnnotationValue(io.micronaut.core.annotation.AnnotationValue) Optional(java.util.Optional) AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) HintsCapableRepository(io.micronaut.data.operations.HintsCapableRepository) DataMethodQueryParameter(io.micronaut.data.intercept.annotation.DataMethodQueryParameter) Collections(java.util.Collections) DefaultStoredDataOperation(io.micronaut.data.model.runtime.DefaultStoredDataOperation) Association(io.micronaut.data.model.Association) JoinPath(io.micronaut.data.model.query.JoinPath) NonNull(io.micronaut.core.annotation.NonNull)

Example 2 with JoinPath

use of io.micronaut.data.model.query.JoinPath in project micronaut-data by micronaut-projects.

the class MongoQueryBuilder method addLookups.

private void addLookups(Collection<JoinPath> joins, QueryState queryState) {
    if (joins.isEmpty()) {
        return;
    }
    List<String> joined = joins.stream().map(JoinPath::getPath).sorted((o1, o2) -> Comparator.comparingInt(String::length).thenComparing(String::compareTo).compare(o1, o2)).collect(Collectors.toList());
    for (String join : joined) {
        StringJoiner rootPath = new StringJoiner(".");
        StringJoiner currentEntityPath = new StringJoiner(".");
        LookupsStage currentLookup = queryState.rootLookups;
        for (String path : StringUtils.splitOmitEmptyStrings(join, '.')) {
            rootPath.add(path);
            currentEntityPath.add(path);
            String thisPath = currentEntityPath.toString();
            if (currentLookup.subLookups.containsKey(thisPath)) {
                currentLookup = currentLookup.subLookups.get(path);
                currentEntityPath = new StringJoiner(".");
                continue;
            }
            PersistentPropertyPath propertyPath = currentLookup.persistentEntity.getPropertyPath(thisPath);
            PersistentProperty property = propertyPath.getProperty();
            if (!(property instanceof Association)) {
                continue;
            }
            Association association = (Association) property;
            if (association.getKind() == Relation.Kind.EMBEDDED) {
                continue;
            }
            LookupsStage lookupStage = new LookupsStage(association.getAssociatedEntity());
            List<Map<String, Object>> pipeline = currentLookup.pipeline;
            Optional<Association> inverseSide = association.getInverseSide().map(Function.identity());
            PersistentEntity persistentEntity = association.getOwner();
            String joinedCollectionName = association.getAssociatedEntity().getPersistedName();
            String ownerCollectionName = persistentEntity.getPersistedName();
            if (association.getKind() == Relation.Kind.MANY_TO_MANY || association.isForeignKey() && !inverseSide.isPresent()) {
                PersistentEntity associatedEntity = association.getAssociatedEntity();
                PersistentEntity associationOwner = association.getOwner();
                // JOIN TABLE
                PersistentProperty identity = associatedEntity.getIdentity();
                if (identity == null) {
                    throw new IllegalArgumentException("Associated entity [" + associatedEntity.getName() + "] defines no ID. Cannot join.");
                }
                final PersistentProperty associatedId = associationOwner.getIdentity();
                if (associatedId == null) {
                    throw new MappingException("Cannot join on entity [" + associationOwner.getName() + "] that has no declared ID");
                }
                Association owningAssociation = inverseSide.orElse(association);
                boolean isAssociationOwner = !association.getInverseSide().isPresent();
                NamingStrategy namingStrategy = associationOwner.getNamingStrategy();
                AnnotationMetadata annotationMetadata = owningAssociation.getAnnotationMetadata();
                List<String> ownerJoinFields = resolveJoinTableAssociatedFields(annotationMetadata, isAssociationOwner, associationOwner, namingStrategy);
                List<String> ownerJoinCollectionFields = resolveJoinTableJoinFields(annotationMetadata, isAssociationOwner, associationOwner, namingStrategy);
                List<String> associationJoinFields = resolveJoinTableAssociatedFields(annotationMetadata, !isAssociationOwner, associatedEntity, namingStrategy);
                List<String> associationJoinCollectionFields = resolveJoinTableJoinFields(annotationMetadata, !isAssociationOwner, associatedEntity, namingStrategy);
                String joinCollectionName = namingStrategy.mappedName(owningAssociation);
                // String joinTableName = annotationMetadata
                // .stringValue(ANN_JOIN_TABLE, "name")
                // .orElseGet(() -> namingStrategy.mappedName(association));
                List<Map<String, Object>> joinCollectionLookupPipeline = new ArrayList<>();
                pipeline.add(lookup(joinCollectionName, "_id", ownerCollectionName, joinCollectionLookupPipeline, thisPath));
                joinCollectionLookupPipeline.add(lookup(joinedCollectionName, joinedCollectionName, "_id", lookupStage.pipeline, joinedCollectionName));
                joinCollectionLookupPipeline.add(unwind("$" + joinedCollectionName, true));
                joinCollectionLookupPipeline.add(singletonMap("$replaceRoot", singletonMap("newRoot", "$" + joinedCollectionName)));
            } else {
                String currentPath = asPath(propertyPath.getAssociations(), propertyPath.getProperty());
                if (association.isForeignKey()) {
                    String mappedBy = association.getAnnotationMetadata().stringValue(Relation.class, "mappedBy").orElseThrow(IllegalStateException::new);
                    PersistentPropertyPath mappedByPath = association.getAssociatedEntity().getPropertyPath(mappedBy);
                    if (mappedByPath == null) {
                        throw new IllegalStateException("Cannot find mapped path: " + mappedBy);
                    }
                    if (!(mappedByPath.getProperty() instanceof Association)) {
                        throw new IllegalStateException("Expected association as a mapped path: " + mappedBy);
                    }
                    List<String> localMatchFields = new ArrayList<>();
                    List<String> foreignMatchFields = new ArrayList<>();
                    traversePersistentProperties(currentLookup.persistentEntity.getIdentity(), (associations, p) -> {
                        String fieldPath = asPath(associations, p);
                        localMatchFields.add(fieldPath);
                    });
                    List<Association> mappedAssociations = new ArrayList<>(mappedByPath.getAssociations());
                    mappedAssociations.add((Association) mappedByPath.getProperty());
                    traversePersistentProperties(mappedAssociations, currentLookup.persistentEntity.getIdentity(), (associations, p) -> {
                        String fieldPath = asPath(associations, p);
                        foreignMatchFields.add(fieldPath);
                    });
                    pipeline.add(lookup(joinedCollectionName, localMatchFields, foreignMatchFields, lookupStage.pipeline, currentPath));
                } else {
                    List<Association> mappedAssociations = new ArrayList<>(propertyPath.getAssociations());
                    mappedAssociations.add((Association) propertyPath.getProperty());
                    List<String> localMatchFields = new ArrayList<>();
                    List<String> foreignMatchFields = new ArrayList<>();
                    PersistentProperty identity = lookupStage.persistentEntity.getIdentity();
                    if (identity == null) {
                        throw new IllegalStateException("Null identity of persistent entity: " + lookupStage.persistentEntity);
                    }
                    traversePersistentProperties(mappedAssociations, identity, (associations, p) -> {
                        String fieldPath = asPath(associations, p);
                        localMatchFields.add(fieldPath);
                    });
                    traversePersistentProperties(identity, (associations, p) -> {
                        String fieldPath = asPath(associations, p);
                        foreignMatchFields.add(fieldPath);
                    });
                    pipeline.add(lookup(joinedCollectionName, localMatchFields, foreignMatchFields, lookupStage.pipeline, currentPath));
                }
                if (association.getKind().isSingleEnded()) {
                    pipeline.add(unwind("$" + currentPath, true));
                }
            }
            currentLookup.subLookups.put(currentEntityPath.toString(), lookupStage);
        }
        queryState.joinPaths.add(join);
    }
}
Also used : Arrays(java.util.Arrays) TypeRole(io.micronaut.data.annotation.TypeRole) Internal(io.micronaut.core.annotation.Internal) Collections.singletonList(java.util.Collections.singletonList) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Locale(java.util.Locale) Arrays.asList(java.util.Arrays.asList) Map(java.util.Map) QueryResult(io.micronaut.data.model.query.builder.QueryResult) ArgumentUtils(io.micronaut.core.util.ArgumentUtils) PersistentPropertyPath(io.micronaut.data.model.PersistentPropertyPath) PersistentProperty(io.micronaut.data.model.PersistentProperty) MappingException(io.micronaut.data.exceptions.MappingException) Sort(io.micronaut.data.model.Sort) Collection(java.util.Collection) Set(java.util.Set) NotNull(javax.validation.constraints.NotNull) Collectors(java.util.stream.Collectors) StringUtils(io.micronaut.core.util.StringUtils) List(java.util.List) Optional(java.util.Optional) Pattern(java.util.regex.Pattern) Relation(io.micronaut.data.annotation.Relation) QueryParameterBinding(io.micronaut.data.model.query.builder.QueryParameterBinding) MongoAnnotations(io.micronaut.data.document.mongo.MongoAnnotations) QueryModel(io.micronaut.data.model.query.QueryModel) HashMap(java.util.HashMap) Function(java.util.function.Function) TreeSet(java.util.TreeSet) ArrayList(java.util.ArrayList) LinkedHashMap(java.util.LinkedHashMap) Embedded(io.micronaut.data.model.Embedded) SerdeConfig(io.micronaut.serde.config.annotation.SerdeConfig) Nullable(io.micronaut.core.annotation.Nullable) Pageable(io.micronaut.data.model.Pageable) BiConsumer(java.util.function.BiConsumer) Collections.singletonMap(java.util.Collections.singletonMap) NamingStrategy(io.micronaut.data.model.naming.NamingStrategy) Iterator(java.util.Iterator) NonNull(io.micronaut.core.annotation.NonNull) JoinPath(io.micronaut.data.model.query.JoinPath) BindingParameter(io.micronaut.data.model.query.BindingParameter) Association(io.micronaut.data.model.Association) PersistentEntity(io.micronaut.data.model.PersistentEntity) CollectionUtils(io.micronaut.core.util.CollectionUtils) StringJoiner(java.util.StringJoiner) QueryBuilder(io.micronaut.data.model.query.builder.QueryBuilder) AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) Comparator(java.util.Comparator) Collections(java.util.Collections) JoinPath(io.micronaut.data.model.query.JoinPath) PersistentEntity(io.micronaut.data.model.PersistentEntity) ArrayList(java.util.ArrayList) PersistentPropertyPath(io.micronaut.data.model.PersistentPropertyPath) PersistentProperty(io.micronaut.data.model.PersistentProperty) AnnotationMetadata(io.micronaut.core.annotation.AnnotationMetadata) MappingException(io.micronaut.data.exceptions.MappingException) NamingStrategy(io.micronaut.data.model.naming.NamingStrategy) Relation(io.micronaut.data.annotation.Relation) Association(io.micronaut.data.model.Association) Map(java.util.Map) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) Collections.singletonMap(java.util.Collections.singletonMap) StringJoiner(java.util.StringJoiner)

Example 3 with JoinPath

use of io.micronaut.data.model.query.JoinPath in project micronaut-data by micronaut-projects.

the class AbstractSqlLikeQueryBuilder method buildQuery.

@Override
public QueryResult buildQuery(@NonNull AnnotationMetadata annotationMetadata, @NonNull QueryModel query) {
    ArgumentUtils.requireNonNull("annotationMetadata", annotationMetadata);
    ArgumentUtils.requireNonNull("query", query);
    QueryState queryState = newQueryState(query, true, true);
    List<JoinPath> joinPaths = new ArrayList<>(query.getJoinPaths());
    joinPaths.sort((o1, o2) -> Comparator.comparingInt(String::length).thenComparing(String::compareTo).compare(o1.getPath(), o2.getPath()));
    for (JoinPath joinPath : joinPaths) {
        queryState.applyJoin(joinPath);
    }
    StringBuilder select = new StringBuilder(SELECT_CLAUSE);
    buildSelectClause(query, queryState, select);
    appendForUpdate(QueryPosition.AFTER_TABLE_NAME, query, select);
    queryState.getQuery().insert(0, select);
    QueryModel.Junction criteria = query.getCriteria();
    if (!criteria.isEmpty() || annotationMetadata.hasStereotype(WhereSpecifications.class) || queryState.getEntity().getAnnotationMetadata().hasStereotype(WhereSpecifications.class)) {
        buildWhereClause(annotationMetadata, criteria, queryState);
    }
    appendOrder(query, queryState);
    appendForUpdate(QueryPosition.END_OF_QUERY, query, queryState.getQuery());
    return QueryResult.of(queryState.getFinalQuery(), queryState.getQueryParts(), queryState.getParameterBindings(), queryState.getAdditionalRequiredParameters(), query.getMax(), query.getOffset());
}
Also used : JoinPath(io.micronaut.data.model.query.JoinPath) ArrayList(java.util.ArrayList) QueryModel(io.micronaut.data.model.query.QueryModel) WhereSpecifications(io.micronaut.data.annotation.repeatable.WhereSpecifications)

Example 4 with JoinPath

use of io.micronaut.data.model.query.JoinPath in project micronaut-data by micronaut-projects.

the class JpaQueryBuilder method buildJoin.

@Override
protected String[] buildJoin(String alias, JoinPath joinPath, String joinType, StringBuilder target, Map<String, String> appliedJoinPaths, QueryState queryState) {
    Association[] associationPath = joinPath.getAssociationPath();
    String[] joinAliases;
    if (ArrayUtils.isEmpty(associationPath)) {
        throw new IllegalArgumentException("Invalid association path [" + joinPath.getPath() + "]");
    }
    List<Association> joinAssociationsPath = new ArrayList<>(associationPath.length);
    joinAliases = new String[associationPath.length];
    StringJoiner pathSoFar = new StringJoiner(".");
    List<String> aliases = new ArrayList<>();
    for (int i = 0; i < associationPath.length; i++) {
        Association association = associationPath[i];
        pathSoFar.add(association.getName());
        if (association instanceof Embedded) {
            joinAssociationsPath.add(association);
            continue;
        }
        String currentPath = pathSoFar.toString();
        String existingAlias = appliedJoinPaths.get(currentPath);
        if (existingAlias != null) {
            joinAliases[i] = existingAlias;
            aliases.add(existingAlias);
        } else {
            int finalI = i;
            JoinPath joinPathToUse = queryState.getQueryModel().getJoinPath(currentPath).orElseGet(() -> new JoinPath(currentPath, Arrays.copyOfRange(associationPath, 0, finalI + 1), joinPath.getJoinType(), joinPath.getAlias().orElse(null)));
            String currentAlias = getAliasName(joinPathToUse);
            joinAliases[i] = currentAlias;
            String lastJoinAlias = aliases.isEmpty() ? alias : CollectionUtils.last(aliases);
            target.append(joinType).append(lastJoinAlias).append(DOT).append(association.getName()).append(SPACE).append(joinAliases[i]);
            aliases.add(currentAlias);
        }
        joinAssociationsPath.clear();
    }
    return joinAliases;
}
Also used : Association(io.micronaut.data.model.Association) JoinPath(io.micronaut.data.model.query.JoinPath) ArrayList(java.util.ArrayList) Embedded(io.micronaut.data.model.Embedded) StringJoiner(java.util.StringJoiner)

Example 5 with JoinPath

use of io.micronaut.data.model.query.JoinPath in project micronaut-data by micronaut-projects.

the class DefaultJdbcRepositoryOperations method findOne.

@Nullable
@Override
public <T, R> R findOne(@NonNull PreparedQuery<T, R> preparedQuery) {
    return executeRead(connection -> {
        RuntimePersistentEntity<T> persistentEntity = getEntity(preparedQuery.getRootEntity());
        try (PreparedStatement ps = prepareStatement(connection, connection::prepareStatement, preparedQuery, false, true)) {
            try (ResultSet rs = ps.executeQuery()) {
                Class<R> resultType = preparedQuery.getResultType();
                if (preparedQuery.getResultDataType() == DataType.ENTITY) {
                    RuntimePersistentEntity<R> resultPersistentEntity = getEntity(resultType);
                    final Set<JoinPath> joinFetchPaths = preparedQuery.getJoinFetchPaths();
                    SqlResultEntityTypeMapper<ResultSet, R> mapper = new SqlResultEntityTypeMapper<>(resultPersistentEntity, columnNameResultSetReader, joinFetchPaths, jsonCodec, (loadedEntity, o) -> {
                        if (loadedEntity.hasPostLoadEventListeners()) {
                            return triggerPostLoad(o, loadedEntity, preparedQuery.getAnnotationMetadata());
                        } else {
                            return o;
                        }
                    }, conversionService);
                    SqlResultEntityTypeMapper.PushingMapper<ResultSet, R> oneMapper = mapper.readOneWithJoins();
                    if (rs.next()) {
                        oneMapper.processRow(rs);
                    }
                    while (!joinFetchPaths.isEmpty() && rs.next()) {
                        oneMapper.processRow(rs);
                    }
                    R result = oneMapper.getResult();
                    if (preparedQuery.hasResultConsumer()) {
                        preparedQuery.getParameterInRole(SqlResultConsumer.ROLE, SqlResultConsumer.class).ifPresent(consumer -> consumer.accept(result, newMappingContext(rs)));
                    }
                    return result;
                } else if (rs.next()) {
                    if (preparedQuery.isDtoProjection()) {
                        TypeMapper<ResultSet, R> introspectedDataMapper = new DTOMapper<>(persistentEntity, columnNameResultSetReader, jsonCodec, conversionService);
                        return introspectedDataMapper.map(rs, resultType);
                    } else {
                        Object v = columnIndexResultSetReader.readDynamic(rs, 1, preparedQuery.getResultDataType());
                        if (v == null) {
                            return null;
                        } else if (resultType.isInstance(v)) {
                            return (R) v;
                        } else {
                            return columnIndexResultSetReader.convertRequired(v, resultType);
                        }
                    }
                }
            }
        } catch (SQLException e) {
            throw new DataAccessException("Error executing SQL Query: " + e.getMessage(), e);
        }
        return null;
    });
}
Also used : TypeMapper(io.micronaut.data.runtime.mapper.TypeMapper) SqlTypeMapper(io.micronaut.data.runtime.mapper.sql.SqlTypeMapper) SqlResultEntityTypeMapper(io.micronaut.data.runtime.mapper.sql.SqlResultEntityTypeMapper) JoinPath(io.micronaut.data.model.query.JoinPath) SQLException(java.sql.SQLException) PreparedStatement(java.sql.PreparedStatement) SqlResultConsumer(io.micronaut.data.jdbc.mapper.SqlResultConsumer) ResultSet(java.sql.ResultSet) SqlResultEntityTypeMapper(io.micronaut.data.runtime.mapper.sql.SqlResultEntityTypeMapper) DataAccessException(io.micronaut.data.exceptions.DataAccessException) Nullable(io.micronaut.core.annotation.Nullable)

Aggregations

JoinPath (io.micronaut.data.model.query.JoinPath)11 ArrayList (java.util.ArrayList)7 Association (io.micronaut.data.model.Association)6 QueryModel (io.micronaut.data.model.query.QueryModel)5 StringJoiner (java.util.StringJoiner)5 NonNull (io.micronaut.core.annotation.NonNull)4 Embedded (io.micronaut.data.model.Embedded)4 List (java.util.List)4 Map (java.util.Map)4 AnnotationMetadata (io.micronaut.core.annotation.AnnotationMetadata)3 StringUtils (io.micronaut.core.util.StringUtils)3 Join (io.micronaut.data.annotation.Join)3 PersistentEntity (io.micronaut.data.model.PersistentEntity)3 PersistentPropertyPath (io.micronaut.data.model.PersistentPropertyPath)3 AnnotationValue (io.micronaut.core.annotation.AnnotationValue)2 Internal (io.micronaut.core.annotation.Internal)2 Nullable (io.micronaut.core.annotation.Nullable)2 ArgumentUtils (io.micronaut.core.util.ArgumentUtils)2 CollectionUtils (io.micronaut.core.util.CollectionUtils)2 Relation (io.micronaut.data.annotation.Relation)2