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

com.mysema.query.jpa.JPQLSerializer Maven / Gradle / Ivy

There is a newer version: 3.7.4
Show newest version
/*
 * Copyright (c) 2010 Mysema Ltd.
 * All rights reserved.
 *
 */
package com.mysema.query.jpa;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nullable;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;

import com.mysema.query.JoinExpression;
import com.mysema.query.JoinType;
import com.mysema.query.QueryMetadata;
import com.mysema.query.support.SerializerBase;
import com.mysema.query.types.*;
import com.mysema.util.MathUtils;

/**
 * JPQLSerializer serializes Querydsl expressions into JPQL syntax.
 *
 * @author tiwe
 */
public class JPQLSerializer extends SerializerBase {

    private static final Set> NUMERIC = new HashSet>(Arrays.>asList(
            Ops.ADD, Ops.SUB, Ops.MULT, Ops.DIV,
            Ops.LT, Ops.LOE, Ops.GT, Ops.GOE, Ops.BETWEEN,
            Ops.BEFORE, Ops.AFTER, Ops.BOE, Ops.AOE));

    private static final String COMMA = ", ";

    private static final String DELETE = "delete from ";

    private static final String FROM = "from ";

    private static final String GROUP_BY = "\ngroup by ";

    private static final String HAVING = "\nhaving ";

    private static final String ORDER_BY = "\norder by ";

    private static final String SELECT = "select ";

    private static final String SELECT_COUNT = "select count(";

    private static final String SELECT_COUNT_DISTINCT = "select count(distinct ";
    
    private static final String SELECT_DISTINCT = "select distinct ";

    private static final String SET = "\nset ";

    private static final String UPDATE = "update ";

    private static final String WHERE = "\nwhere ";

    private static final String WITH = " with ";

    private static final Map joinTypes = new HashMap();

    private final JPQLTemplates templates;

    private boolean inProjection = false;
    
    static{
        joinTypes.put(JoinType.DEFAULT, COMMA);
        joinTypes.put(JoinType.FULLJOIN, "\n  full join ");
        joinTypes.put(JoinType.INNERJOIN, "\n  inner join ");
        joinTypes.put(JoinType.JOIN, "\n  join ");
        joinTypes.put(JoinType.LEFTJOIN, "\n  left join ");
    }

    private boolean wrapElements = false;

    public JPQLSerializer(JPQLTemplates templates) {
        super(templates);
        this.templates = templates;
    }

    private void handleJoinTarget(JoinExpression je) {
        // type specifier
        if (je.getTarget() instanceof EntityPath) {
            EntityPath pe = (EntityPath) je.getTarget();
            if (pe.getMetadata().getParent() == null) {
                Entity entityAnnotation = pe.getAnnotatedElement().getAnnotation(Entity.class);
                if (entityAnnotation != null && entityAnnotation.name().length() > 0){
                    append(entityAnnotation.name());
                } else if (pe.getType().getPackage() != null) {
                    String pn = pe.getType().getPackage().getName();
                    String typeName = pe.getType().getName().substring(pn.length() + 1);
                    append(typeName);
                } else {
                    append(pe.getType().getName());
                }
                append(" ");
            }
        }
        handle(je.getTarget());
    }



    public void serialize(QueryMetadata metadata, boolean forCountRow, @Nullable String projection) {
        List> select = metadata.getProjection();
        List joins = metadata.getJoins();
        Predicate where = metadata.getWhere();
        List> groupBy = metadata.getGroupBy();
        Predicate having = metadata.getHaving();
        List> orderBy = metadata.getOrderBy();

        // select
        boolean inProjectionOrig = inProjection;
        inProjection = true;
        if (projection != null){
            append(SELECT).append(projection).append("\n");

        }else if (forCountRow) {
            if (!metadata.isDistinct()){
                append(SELECT_COUNT);
            }else{
                append(SELECT_COUNT_DISTINCT);
            }
            if(!select.isEmpty()){
                handle(COMMA, select);
            }else{
                handle(joins.get(0).getTarget());
            }
            append(")\n");

        } else if (!select.isEmpty()) {
            if (!metadata.isDistinct()) {
                append(SELECT);
            } else {
                append(SELECT_DISTINCT);
            }
            handle(COMMA, select).append("\n");

        }
        inProjection = inProjectionOrig;

        // from
        append(FROM);
        serializeSources(forCountRow, joins);

        // where
        if (where != null) {
            append(WHERE).handle(where);
        }

        // group by
        if (!groupBy.isEmpty()) {
            append(GROUP_BY).handle(COMMA, groupBy);
        }

        // having
        if (having != null) {
            append(HAVING).handle(having);
        }

        // order by
        if (!orderBy.isEmpty() && !forCountRow) {
            append(ORDER_BY);
            boolean first = true;
            for (OrderSpecifier os : orderBy) {
                if (!first){
                    append(COMMA);
                }
                handle(os.getTarget());
                append(" " + os.getOrder().toString().toLowerCase(Locale.ENGLISH));
                first = false;
            }
        }
    }

    public void serializeForDelete(QueryMetadata md) {
        append(DELETE);
        handleJoinTarget(md.getJoins().get(0));
        if (md.getWhere() != null) {
            append(WHERE).handle(md.getWhere());
        }
    }

    public void serializeForUpdate(QueryMetadata md) {
        append(UPDATE);
        handleJoinTarget(md.getJoins().get(0));
        append(SET);
        handle(COMMA, md.getProjection());
        if (md.getWhere() != null) {
            append(WHERE).handle(md.getWhere());
        }
    }

    private void serializeSources(boolean forCountRow, List joins) {
        for (int i = 0; i < joins.size(); i++) {
            JoinExpression je = joins.get(i);
            if (i > 0) {
                append(joinTypes.get(je.getType()));
            }
            if (je.hasFlag(JPQLQueryMixin.FETCH) && !forCountRow){
                handle(JPQLQueryMixin.FETCH);
            }
            handleJoinTarget(je);
            if (je.hasFlag(JPQLQueryMixin.FETCH_ALL_PROPERTIES) && !forCountRow){
                handle(JPQLQueryMixin.FETCH_ALL_PROPERTIES);
            }

            if (je.getCondition() != null) {
                append(WITH).handle(je.getCondition());
            }
        }
    }

    @Override
    public Void visit(Constant expr, Void context) {
        boolean wrap = templates.wrapConstant(expr);
        if (wrap) {
            append("(");
        }
        append(":");
        if (!getConstantToLabel().containsKey(expr.getConstant())) {
            String constLabel = getConstantPrefix() + (getConstantToLabel().size()+1);
            getConstantToLabel().put(expr.getConstant(), constLabel);
            append(constLabel);
        } else {
            append(getConstantToLabel().get(expr.getConstant()));
        }
        if (wrap) {
            append(")");
        }
        return null;
    }

    @Override
    public Void visit(ParamExpression param, Void context){
        append(":");
        super.visit(param, context);
        return null;
    }

    @Override
    public Void visit(SubQueryExpression query, Void context) {
        append("(");
        serialize(query.getMetadata(), false, null);
        append(")");
        return null;
    }

    @Override
    public Void visit(Path expr, Void context){
        // only wrap a PathCollection, if it the pathType is PROPERTY
        boolean wrap = wrapElements
        && (Collection.class.isAssignableFrom(expr.getType()) || Map.class.isAssignableFrom(expr.getType()))
        && expr.getMetadata().getPathType().equals(PathType.PROPERTY);
        if (wrap) {
            append("elements(");
        }
        super.visit(expr, context);
        if (wrap) {
            append(")");
        }
        return null;
    }

    @Override
    public Void visit(FactoryExpression expr, Void context) {
        if (!inProjection) {
            append("(");
            super.visit(expr, context);
            append(")");
        } else {
            super.visit(expr, context);    
        }
        return null;
    }

    @Override
    @SuppressWarnings("unchecked")
    protected void visitOperation(Class type, Operator operator, List> args) {
        boolean old = wrapElements;
        wrapElements = templates.wrapElements(operator);

        if (operator.equals(Ops.IN)){
            if (args.get(1) instanceof Path){
                if (!templates.isEnumInPathSupported() && args.get(0) instanceof Constant && Enum.class.isAssignableFrom(args.get(0).getType())) {
                    Enumerated enumerated = ((Path)args.get(1)).getAnnotatedElement().getAnnotation(Enumerated.class);
                    Enum constant = (Enum)((Constant)args.get(0)).getConstant();
                    if (enumerated == null || enumerated.value() == EnumType.ORDINAL) {
                        args = Arrays.asList(new ConstantImpl(constant.ordinal()), args.get(1));
                    } else {
                        args = Arrays.asList(new ConstantImpl(constant.name()), args.get(1));
                    }
                }                
                
                super.visitOperation(type, JPQLTemplates.MEMBER_OF, args);
            }else{
                super.visitOperation(type, operator, args);
            }

        } else if (operator.equals(Ops.INSTANCE_OF)) {
            if (templates.isTypeAsString()){
                List> newArgs = new ArrayList>(args);
                Class cl = ((Class) ((Constant) newArgs.get(1)).getConstant());
                // use discriminator value instead of fqnm
                if (cl.getAnnotation(DiscriminatorValue.class) != null){
                    newArgs.set(1, ConstantImpl.create(cl.getAnnotation(DiscriminatorValue.class).value()));
                }else{
                    newArgs.set(1, ConstantImpl.create(cl.getName()));
                }
                super.visitOperation(type, operator, newArgs);
            }else{
                super.visitOperation(type, operator, args);
            }

        } else if (operator.equals(Ops.NUMCAST)) {
            Class targetType = (Class) ((Constant) args.get(1)).getConstant();
            String typeName = targetType.getSimpleName().toLowerCase(Locale.ENGLISH);
            visitOperation(targetType, JPQLTemplates.CAST, Arrays.>asList(args.get(0), ConstantImpl.create(typeName)));

        } else if (operator.equals(Ops.EXISTS) && args.get(0) instanceof SubQueryExpression){
            SubQueryExpression subQuery = (SubQueryExpression) args.get(0);
            append("exists (");
            serialize(subQuery.getMetadata(), false, templates.getExistsProjection());
            append(")");

        } else if (operator.equals(Ops.MATCHES) || operator.equals(Ops.MATCHES_IC)){
            super.visitOperation(type, Ops.LIKE,
                    Arrays.asList(args.get(0), ExpressionUtils.regexToLike((Expression) args.get(1))));

        }else if(NUMERIC.contains(operator)){
            super.visitOperation(type, operator, normalizeNumericArgs(args));

        } else {
            super.visitOperation(type, operator, args);
        }
        //
        wrapElements = old;
    }

    @SuppressWarnings("unchecked")
    private List> normalizeNumericArgs(List> args) {
        boolean hasConstants = false;
        Class numType = null;
        for (Expression arg : args){
            if (Number.class.isAssignableFrom(arg.getType())){
                if (arg instanceof Constant){
                    hasConstants = true;
                }else{
                    numType = (Class) arg.getType();
                }
            }
        }
        if (hasConstants && numType != null){
            List> newArgs = new ArrayList>(args.size());
            for (Expression arg : args){
                if (arg instanceof Constant && Number.class.isAssignableFrom(arg.getType())
                        && !arg.getType().equals(numType)){
                    Number number = (Number) ((Constant)arg).getConstant();
                    newArgs.add(new ConstantImpl(MathUtils.cast(number, (Class)numType)));
                }else{
                    newArgs.add(arg);
                }
            }
            return newArgs;
        }else{
            return args;
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy