All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.querydsl.jpa.JPAQueryMixin Maven / Gradle / Ivy

There is a newer version: 5.1.0
Show newest version
/*
 * Copyright 2015, The Querydsl Team (http://www.querydsl.com/team)
 *
 * 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 com.querydsl.jpa;

import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nullable;
import javax.persistence.Entity;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.querydsl.core.DefaultQueryMetadata;
import com.querydsl.core.JoinFlag;
import com.querydsl.core.QueryMetadata;
import com.querydsl.core.support.Context;
import com.querydsl.core.support.PathsExtractor;
import com.querydsl.core.support.QueryMixin;
import com.querydsl.core.support.ReplaceVisitor;
import com.querydsl.core.types.*;
import com.querydsl.core.types.dsl.CollectionPathBase;

/**
 * {@code JPAQueryMixin} extends {@link QueryMixin} to support JPQL join construction
 *
 * @author tiwe
 *
 * @param 
 */
public class JPAQueryMixin extends QueryMixin {

    private final Set> paths = Sets.newHashSet();

    private final Map, Path> aliases = Maps.newHashMap();

    private final JPAMapAccessVisitor mapAccessVisitor;

    private final JPAListAccessVisitor listAccessVisitor;

    private final JPACollectionAnyVisitor collectionAnyVisitor;

    private final ReplaceVisitor replaceVisitor =  new ReplaceVisitor() {
        @Override
        public Expression visit(Path expr, Void context) {
            return convertPathForOrder(expr);
        }
        @Override
        public Expression visit(SubQueryExpression expr, @Nullable Void context) {
            // don't shorten paths inside subquery expressions
            return expr;
        }
    };


    public static final JoinFlag FETCH = new JoinFlag("fetch ");

    public static final JoinFlag FETCH_ALL_PROPERTIES = new JoinFlag(" fetch all properties");

    public JPAQueryMixin() {
        this(null, new DefaultQueryMetadata());
    }

    public JPAQueryMixin(QueryMetadata metadata) {
        this(null, metadata);
    }

    public JPAQueryMixin(T self, QueryMetadata metadata) {
        super(self, metadata);
        mapAccessVisitor = new JPAMapAccessVisitor(metadata, aliases);
        listAccessVisitor = new JPAListAccessVisitor(metadata, aliases);
        collectionAnyVisitor = new JPACollectionAnyVisitor();
    }

    public T fetchJoin() {
        addJoinFlag(FETCH);
        return getSelf();
    }

    public T fetchAll() {
        addJoinFlag(FETCH_ALL_PROPERTIES);
        return getSelf();
    }

    @Override
    protected  Expression createAlias(Expression expr, Path alias) {
        aliases.put(expr, alias);
        return super.createAlias(expr, alias);
    }

    static boolean isEntityPath(Path path) {
        if (path instanceof CollectionPathBase) {
            return isEntityPath((Path) ((CollectionPathBase) path).any());
        } else {
            return path instanceof EntityPath
                || path.getType().isAnnotationPresent(Entity.class);
        }
    }

    @SuppressWarnings("unchecked")
    static  Class getElementTypeOrType(Path path) {
        if (path instanceof CollectionExpression) {
            return ((CollectionExpression) path).getParameter(0);
        } else {
            return (Class) path.getType();
        }
    }

    @SuppressWarnings("unchecked")
    private  Path shorten(Path path, List> paths) {
        PathMetadata metadata = path.getMetadata();
        if (metadata.isRoot() || paths.contains(path)) {
            return path;
        } else if (aliases.containsKey(path)) {
            return (Path) aliases.get(path);
        } else if (metadata.getPathType() == PathType.COLLECTION_ANY) {
            return (Path) shorten(metadata.getParent(), paths);
        } else if (!isEntityPath(path)) {
            Path parent = shorten(metadata.getParent(), paths);
            if (parent.equals(metadata.getParent())) {
                return path;
            } else {
                return ExpressionUtils.path(path.getType(),
                        new PathMetadata(parent, metadata.getElement(), metadata.getPathType()));
            }
        } else if (metadata.getParent().getMetadata().isRoot()) {
            Class type = getElementTypeOrType(path);
            Path newPath = ExpressionUtils.path(type, ExpressionUtils.createRootVariable(path));
            leftJoin(path, newPath);
            return newPath;
        } else {
            Class type = getElementTypeOrType(path);
            Path parent = shorten(metadata.getParent(), paths);
            Path oldPath = ExpressionUtils.path(path.getType(),
                    new PathMetadata(parent, metadata.getElement(), metadata.getPathType()));
            Path newPath = ExpressionUtils.path(type, ExpressionUtils.createRootVariable(oldPath));
            aliases.put(path, newPath);
            leftJoin(oldPath, newPath);
            return newPath;
        }
    }

    private  Path convertPathForOrder(Path path) {
        PathMetadata metadata = path.getMetadata();
        // at least three levels
        if (metadata.getParent() != null && !metadata.getParent().getMetadata().isRoot()) {
            Set> exprs = Sets.newHashSet();
            QueryMetadata md = getMetadata();
            exprs.addAll(md.getGroupBy());
            if (md.getProjection() != null) {
                exprs.add(md.getProjection());
            }
            if (md.getWhere() != null) {
                exprs.add(md.getWhere());
            }
            if (md.getHaving() != null) {
                exprs.add(md.getHaving());
            }
            List> paths = Lists.newArrayList();
            // extract paths
            PathsExtractor.DEFAULT.visit(exprs, paths);

            if (!paths.contains(path) && !paths.contains(metadata.getParent())) {
                Path shortened = shorten(metadata.getParent(), paths);
                return ExpressionUtils.path(path.getType(),
                        new PathMetadata(shortened, metadata.getElement(), metadata.getPathType()));
            } else {
                return path;
            }
        } else {
            return path;
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public  Expression convert(Expression expr, Role role) {
        expr = (Expression) expr.accept(mapAccessVisitor, null);
        expr = (Expression) expr.accept(listAccessVisitor, null);
        if (role == Role.ORDER_BY) {
            if (expr instanceof Path) {
                expr = convertPathForOrder((Path) expr);
            } else {
                expr = (Expression) expr.accept(replaceVisitor, null);
            }
        }
        return Conversions.convert(super.convert(expr, role));
    }

    @Override
    protected Predicate convert(Predicate predicate, Role role) {
        if (predicate != null) {
            predicate = (Predicate) ExpressionUtils.extract(predicate);
        }
        if (predicate != null) {
            predicate = (Predicate) predicate.accept(mapAccessVisitor, null);
            predicate = (Predicate) predicate.accept(listAccessVisitor, null);
        }
        if (predicate != null) {
            // transform any usage
            predicate = (Predicate) predicate.accept(collectionAnyVisitor, new Context());
            return predicate;
        } else {
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    private void addCondition(Context context, int i, Path path, boolean where) {
        paths.add(path);
        EntityPath alias = context.replacements.get(i);
        leftJoin((Expression) path.getMetadata().getParent(), context.replacements.get(i));
        Expression index = ExpressionUtils.operation(Integer.class, JPQLOps.INDEX, alias);
        Object element = path.getMetadata().getElement();
        if (!(element instanceof Expression)) {
            element = ConstantImpl.create(element);
        }
        Predicate condition = ExpressionUtils.eq(index, (Expression) element);
        if (where) {
            super.where(condition);
        } else {
            super.having(condition);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy