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

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

The 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 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.CollectionExpression;
import com.querydsl.core.types.ConstantImpl;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.PathMetadata;
import com.querydsl.core.types.PathType;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.SubQueryExpression;
import com.querydsl.core.types.dsl.CollectionPathBase;
import jakarta.persistence.Entity;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jetbrains.annotations.Nullable;

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

  private final Set> paths = new HashSet<>();

  private final Map, Path> aliases = new HashMap<>();

  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 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();
  }

  @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) {
    var 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) {
    var metadata = path.getMetadata();
    // at least three levels
    if (metadata.getParent() != null && !metadata.getParent().getMetadata().isRoot()) {
      var md = getMetadata();
      var exprs = new HashSet<>(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 = new ArrayList<>();
      // 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);
    var element = path.getMetadata().getElement();
    if (!(element instanceof Expression)) {
      element = ConstantImpl.create(element);
    }
    var condition = ExpressionUtils.eq(index, (Expression) element);
    if (where) {
      super.where(condition);
    } else {
      super.having(condition);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy