com.evolveum.midpoint.repo.sql.query2.hqm.HibernateQuery.java Source code

Java tutorial

Introduction

Here is the source code for com.evolveum.midpoint.repo.sql.query2.hqm.HibernateQuery.java

Source

/*
 * Copyright (c) 2010-2015 Evolveum
 *
 * 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.evolveum.midpoint.repo.sql.query2.hqm;

import com.evolveum.midpoint.prism.query.OrderDirection;
import com.evolveum.midpoint.repo.sql.data.common.RObject;
import com.evolveum.midpoint.repo.sql.query.QueryException;
import com.evolveum.midpoint.repo.sql.query2.definition.JpaEntityDefinition;
import com.evolveum.midpoint.repo.sql.query2.definition.JpaLinkDefinition;
import com.evolveum.midpoint.repo.sql.query2.hqm.condition.Condition;
import com.evolveum.midpoint.repo.sql.util.ClassMapper;
import org.apache.commons.lang.Validate;

import java.util.ArrayList;
import java.util.List;

/**
 * Query in HQL that is being created.
 *
 * @author mederly
 */
public abstract class HibernateQuery {

    private static final String INDENT_STRING = "  ";

    // projection elements - i.e. select projectionElement1, projectionElement2, ..., projectionElementN from ...
    private List<ProjectionElement> projectionElements = new ArrayList<>();

    /**
     * Primary entity for this query, along with joined entities.
     * For example,
     *   RUser u
     * Or,
     *   RUser u
     *     left join u.assignments a with ...
     *
     * (originally, we thought about cross-joins with other entities, hence "primary entity")
     */
    private EntityReference primaryEntity; // not null

    /**
     * List of conditions in the "where" clause. They are to be interpreted as a conjunction.
     */
    private List<Condition> conditions = new ArrayList<>();

    class Ordering {
        String byProperty;
        OrderDirection direction;

        public Ordering(String byProperty, OrderDirection direction) {
            this.byProperty = byProperty;
            this.direction = direction;
        }
    }

    private List<Ordering> orderingList = new ArrayList<>();

    public HibernateQuery(JpaEntityDefinition primaryEntityDef) {
        Validate.notNull(primaryEntityDef, "primaryEntityDef");
        primaryEntity = createItemSpecification(primaryEntityDef);
    }

    public List<ProjectionElement> getProjectionElements() {
        return projectionElements;
    }

    public void addProjectionElement(ProjectionElement element) {
        projectionElements.add(element);
    }

    public EntityReference getPrimaryEntity() {
        return primaryEntity;
    }

    public void setPrimaryEntity(EntityReference primaryEntity) {
        this.primaryEntity = primaryEntity;
    }

    public List<Condition> getConditions() {
        return conditions;
    }

    public void addCondition(Condition condition) {
        conditions.add(condition);
    }

    public String getAsHqlText(int indent) {
        StringBuilder sb = new StringBuilder();

        indent(sb, indent);
        sb.append("select\n");
        ProjectionElement.dumpToHql(sb, projectionElements, indent + 1); // we finish at the end of the last line (not at the new line)
        sb.append("\n");

        indent(sb, indent);
        sb.append("from\n");
        primaryEntity.dumpToHql(sb, indent + 1);

        if (!conditions.isEmpty()) {
            sb.append("\n");
            indent(sb, indent);
            sb.append("where\n");
            Condition.dumpToHql(sb, conditions, indent + 1);
        }
        if (!orderingList.isEmpty()) {
            sb.append("\n");
            indent(sb, indent);
            sb.append("order by ");
            boolean first = true;
            for (Ordering ordering : orderingList) {
                if (first) {
                    first = false;
                } else {
                    sb.append(", ");
                }
                sb.append(ordering.byProperty);
                if (ordering.direction != null) {
                    switch (ordering.direction) {
                    case DESCENDING:
                        sb.append(" desc");
                        break;
                    case ASCENDING:
                        sb.append(" asc");
                        break;
                    default:
                        throw new IllegalStateException("Unknown ordering: " + ordering.direction);
                    }
                }
            }
        }
        return sb.toString();
    }

    public static void indent(StringBuilder sb, int indent) {
        while (indent-- > 0) {
            sb.append(INDENT_STRING);
        }
    }

    public EntityReference createItemSpecification(JpaEntityDefinition entityDef) {
        String alias = createAlias(entityDef);
        return new EntityReference(alias, entityDef.getJpaClassName());
    }

    public String createAlias(JpaEntityDefinition def) {
        return createAlias(def.getJpaClassName(), true);
    }

    public String createAlias(JpaLinkDefinition linkDefinition) {
        Validate.notNull(linkDefinition.getJpaName(), "Got unnamed transition");
        return createAlias(linkDefinition.getJpaName(), false);
    }

    private static final int LIMIT = 100;

    public String createAlias(String name, boolean entity) {
        String prefix;

        //we want to skip 'R' prefix for entity definition names (a bit of hack)
        int prefixIndex = entity ? 1 : 0;
        prefix = Character.toString(name.charAt(prefixIndex)).toLowerCase();

        int index = 2;
        String alias = prefix;
        while (hasAlias(alias)) {
            alias = prefix + Integer.toString(index);
            index++;

            if (index > LIMIT) {
                throw new IllegalStateException("Alias index for '" + name + "' is more than " + LIMIT
                        + "? This probably should not happen.");
            }
        }

        return alias;
    }

    private boolean hasAlias(String alias) {
        if (primaryEntity != null && primaryEntity.containsAlias(alias)) {
            return true;
        }
        return false;
    }

    public String getPrimaryEntityAlias() {
        return getPrimaryEntity().getAlias();
    }

    public void addOrdering(String propertyPath, OrderDirection direction) {
        orderingList.add(new Ordering(propertyPath, direction));
    }

    public abstract RootHibernateQuery getRootQuery();

    // used to narrow the primary entity e.g. from RObject to RUser (e.g. during ItemValueRestriction processing)
    public void narrowPrimaryEntity(JpaEntityDefinition newDefinition) throws QueryException {
        String oldEntityName = getPrimaryEntity().getName();
        Class<? extends RObject> oldEntityClass = ClassMapper.getHqlClassForHqlName(oldEntityName);
        Class<? extends RObject> newEntityClass = newDefinition.getJpaClass();
        if (!(oldEntityClass.isAssignableFrom(newEntityClass))) {
            throw new QueryException(
                    "Cannot narrow primary entity definition from " + oldEntityClass + " to " + newEntityClass);
        }
        getPrimaryEntity().setName(newDefinition.getJpaClassName()); // alias stays the same
    }
}