jef.database.query.QueryImpl.java Source code

Java tutorial

Introduction

Here is the source code for jef.database.query.QueryImpl.java

Source

/*
 * JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com)
 *
 * 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 jef.database.query;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jef.database.Condition;
import jef.database.Condition.Operator;
import jef.database.DataObject;
import jef.database.DbUtils;
import jef.database.DebugUtil;
import jef.database.Field;
import jef.database.IConditionField;
import jef.database.IQueryableEntity;
import jef.database.ORMConfig;
import jef.database.Session.PopulateStrategy;
import jef.database.annotation.JoinType;
import jef.database.dialect.type.ColumnMapping;
import jef.database.meta.AbstractMetadata;
import jef.database.meta.AbstractRefField;
import jef.database.meta.EntityType;
import jef.database.meta.ITableMetadata;
import jef.database.meta.MetaHolder;
import jef.database.meta.Reference;
import jef.database.meta.TupleField;
import jef.database.wrapper.populator.Transformer;
import jef.tools.Assert;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

public final class QueryImpl<T extends IQueryableEntity> extends AbstractQuery<T> {
    private static final long serialVersionUID = -8921719771049568842L;

    private boolean cascadeViaOuterJoin = ORMConfig.getInstance().isUseOuterJoin();

    final List<Condition> conditions = new ArrayList<Condition>(4);
    /**
     * ??
     */
    private Map<Reference, List<Condition>> cascadeCondition;

    final List<OrderField> orderBy = new ArrayList<OrderField>();

    EntityMappingProvider selected;

    private Map<String, Object> attribute;

    /**
     * ?
     */
    protected final Transformer t;

    public Transformer getResultTransformer() {
        return t;
    }

    /**
     * ref fieldref field??
     */
    private List<Query<?>> otherQueryProvider = null;

    public QueryImpl(T p) {
        this.instance = p;
        type = MetaHolder.getMeta(p);
        t = new Transformer(type);
        t.setLoadVsOne(type.isUseOuterJoin());
        t.setLoadVsMany(true);
        this.cacheable = type.isCacheable();
    }

    public QueryImpl(T p, String key) {
        this.instance = p;
        DebugUtil.bindQuery((DataObject) p, this);
        setAttribute(ConditionQuery.CUSTOM_TABLE_TYPE, key);
        type = MetaHolder.getMeta(p);
        t = new Transformer(type);
        t.setLoadVsOne(type.isUseOuterJoin());
        t.setLoadVsMany(true);
        this.cacheable = type.isCacheable();
    }

    public void setOrderBy(boolean asc, Field... orderbys) {
        orderBy.clear();
        addOrderBy(asc, orderbys);
    }

    public void addOrderBy(boolean flag, Field... orderbys) {
        for (Field f : orderbys) {
            if (!(f instanceof RefField) && !(f instanceof SqlExpression)) {
                ITableMetadata metaOfField = DbUtils.getTableMeta(f);
                if (!this.type.containsMeta(metaOfField)) {
                    throw new IllegalArgumentException("the field [" + f.name() + "] which belongs to "
                            + metaOfField.getName() + " is not current type:" + getType().getName());
                }
            }
            orderBy.add(new OrderField(f, flag));
        }
    }

    public void clearQuery() {
        conditions.clear();
        orderBy.clear();
        if (cascadeCondition != null)
            cascadeCondition.clear();

    }

    @SuppressWarnings("unchecked")
    public Collection<OrderField> getOrderBy() {
        if (orderBy == null)
            return Collections.EMPTY_LIST;
        return orderBy;
    }

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

    public Query<T> addCondition(IConditionField field) {
        return addCondition(field, Condition.Operator.EQUALS, null);
    }

    /*
     * Condition?Field?RefField
     */
    private void wrapRef(IConditionField field) {
        for (Condition c : field.getConditions()) {
            wrapRef(c);
        }
    }

    /*
     * Condition?Field?RefField
     */
    private void wrapRef(Condition c) {
        Field field = c.getField();
        if (field instanceof Enum || field instanceof TupleField) {
            ITableMetadata meta = DbUtils.getTableMeta(field);
            if (meta != type) {
                c.setField(new RefField(field));
            }
        } else if (field instanceof IConditionField) {
            wrapRef((IConditionField) field);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see jef.database.query.Query#addCondition(jef.database.Condition)
     */
    public Query<T> addCondition(Condition condition) {
        if (condition.getOperator() == Operator.EQUALS && condition.getField().getClass().isEnum()) {
            ColumnMapping column = type.getColumnDef(condition.getField());
            if (column != null && DbUtils.isInvalidValue(condition.getValue(), column, true)) {
                //???
                //??FieldColumnnull
                //2016-9-12?WEB?
                return this;
            }
        }
        if (!conditions.contains(condition))
            conditions.add(condition);
        allRecords = false;
        wrapRef(condition);
        return this;
    }

    public Query<T> addCondition(Field field, Object value) {
        return addCondition(Condition.get(field, Condition.Operator.EQUALS, value));
    }

    public Query<T> addCondition(Field field, Operator oper, Object value) {
        return addCondition(Condition.get(field, oper, value));
    }

    public Query<T> addExtendQuery(Query<?> query) {
        if (query != null) {
            if (otherQueryProvider == null) {
                otherQueryProvider = new ArrayList<Query<?>>(4);
            }
            otherQueryProvider.add(query);
        }
        return this;
    }

    public Query<T> addExtendQuery(Class<?> emptyQuery) {
        AbstractMetadata meta = MetaHolder.getMeta(emptyQuery);
        if (meta.getType() == EntityType.POJO) {
            throw new IllegalArgumentException();
        }
        return addExtendQuery(ReadOnlyQuery.getEmptyQuery(meta));
    }

    boolean allRecords;

    public Query<T> setAllRecordsCondition() {
        conditions.clear();
        allRecords = true;
        otherQueryProvider = null;
        return this;
    }

    public boolean isCascadeViaOuterJoin() {
        return cascadeViaOuterJoin;
    }

    public void setCascadeViaOuterJoin(boolean cascadeOuterJoin) {
        this.cascadeViaOuterJoin = cascadeOuterJoin;
    }

    // RefName??
    // 1?????xx.xx.xxref?????
    public Query<T> addCascadeCondition(String refName, Condition... conds) {
        int n = refName.indexOf('.');

        ITableMetadata type = this.type;
        Reference reference;
        while (n > -1) {
            AbstractRefField rField = type.getRefFieldsByName().get(refName.substring(0, n));
            Assert.notNull(rField, "the input field '" + refName + "' is not Cascade field in " + type.getName());
            if (rField.isSingleColumn()) {
                throw new IllegalArgumentException();
            }
            reference = rField.getReference();
            type = reference.getTargetType();
            refName = refName.substring(n + 1);
            n = refName.indexOf('.');
        }
        AbstractRefField rField = type.getRefFieldsByName().get(refName);
        Assert.notNull(rField, "the input field '" + refName + "' is not Cascade field in " + type.getName());
        reference = rField.getReference();
        return addFilterCondition(reference, conds);
    }

    public Query<T> addCascadeCondition(Condition condition) {
        ITableMetadata meta = DbUtils.getTableMeta(condition.getField());
        Reference ref = DbUtils.findDistinctPath(type, meta);
        return addFilterCondition(ref, condition);
    }

    private void checkRefs(Field c) {
        if (c instanceof RefField) {
            RefField f = (RefField) c;
            Reference ref = DbUtils.findPath(type, DbUtils.getTableMeta(f.getField()));
            ensureRef(ref);
        } else if (c instanceof IConditionField) {
            IConditionField ic = (IConditionField) c;
            for (Condition cc : ic.getConditions()) {
                checkRefs(cc.getField());
            }
        }
    }

    private void ensureRef(Reference ref) {
        if (!this.cascadeViaOuterJoin) {
            if (otherQueryProvider != null) {
                for (Query<?> q : otherQueryProvider) {
                    if (q.getMeta() == ref.getTargetType()) {
                        return;
                    }
                }
            }
            this.addExtendQuery(ReadOnlyQuery.getEmptyQuery(ref.getTargetType()));
        }
    }

    private Query<T> addFilterCondition(Reference ref, Condition... cs) {
        if (cascadeCondition == null)
            cascadeCondition = new HashMap<Reference, List<Condition>>(4);
        List<Condition> cond = cascadeCondition.get(ref);
        if (cond == null) {
            cond = new ArrayList<Condition>();
            cascadeCondition.put(ref, cond);
        }
        cond.addAll(Arrays.asList(cs));
        return this;
    }

    public EntityMappingProvider getSelectItems() {
        return selected;
    }

    public void setSelectItems(Selects select) {
        this.selected = select;
    }

    public SqlContext prepare() {
        if (this.selected != null) {
            if (selected instanceof SqlContext)
                return (SqlContext) selected;
            selected = new SqlContext((SelectsImpl) selected);
            ((SqlContext) selected).attribute = this.attribute;
            return (SqlContext) selected;
        } else {
            SqlContext context = new SqlContext("t", this);
            context.attribute = this.attribute;
            this.selected = context;
            return context;
        }
    }

    /**
     * ?????HashCode
     * keyMaporderbyHashMapHashA?
     * ?orderby?HashMapHashB?? ?? for(Query<?>
     * query: map.keySet()){ String leftAlias=sqlContext.getAliasOf(query);
     * boolean flag=map.containsKey(query);//false. Object
     * data=map.get(query); //null. }
     * HashMap?Hash??table?HashMap??Key
     * ?
     * 1????hashCode?HashCode??
     * 2?Map??entrySet()??keySet()?
     */
    @Override
    public int hashCode() {
        HashCodeBuilder hash = new HashCodeBuilder();
        hash.append(type);
        if (conditions != null) {
            int code = 0;
            for (Condition d : conditions) {
                code += d.hashCode();
            }
            hash.append(code);
        }
        return hash.toHashCode();
    }

    @SuppressWarnings("rawtypes")
    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof QueryImpl)) {
            return false;
        }
        QueryImpl rhs = (QueryImpl) obj;
        // conditions
        if ((conditions == null) != (rhs.conditions == null)) {
            return false;
        }
        if (conditions != null) {
            if (conditions.size() != rhs.conditions.size()) {
                return false;
            }
            if (!conditions.containsAll(rhs.conditions)) {
                return false;
            }
        }
        EqualsBuilder eb = new EqualsBuilder();
        eb.append(this.type, rhs.type);
        // eb.append(this.conditions, rhs.conditions);
        eb.append(this.orderBy, rhs.orderBy);
        eb.append(this.selected, rhs.selected);
        eb.append(this.otherQueryProvider, rhs.otherQueryProvider);
        eb.append(this.cascadeCondition, rhs.cascadeCondition);
        return eb.isEquals();
    }

    // ?,??
    public Query<?>[] getOtherQueryProvider() {
        if (!cascadeViaOuterJoin) {
            for (Condition c : this.conditions) {
                checkRefs(c.getField());
            }
        }
        return otherQueryProvider == null ? EMPTY_Q
                : otherQueryProvider.toArray(new Query<?>[otherQueryProvider.size()]);
    }

    public void setAttribute(String key, Object value) {
        if (attribute == null) {
            attribute = new HashMap<String, Object>();
        }
        attribute.put(key, value);
    }

    public Object getAttribute(String key) {
        if (attribute == null) {
            return null;
        }
        return attribute.get(key);
    }

    public Map<Reference, List<Condition>> getFilterCondition() {
        return cascadeCondition;
    }

    @Override
    public String toString() {
        return type.getThisType().getSimpleName() + conditions.toString();
    }

    public void setCustomTableName(String name) {
        setAttribute(CUSTOM_TABLE_NAME, name);
    }

    public Map<String, Object> getAttributes() {
        return attribute;
    }

    /**
     * ?FilterCondition?Filter??? ?????.
     * ?xxxToOne????
     * 
     * @return ?????.
     */
    public static boolean isAllowMergeAsOuterJoin(Reference ref) {
        if (ref == null) {
            throw new IllegalStateException();
        }
        ReferenceType type = ref.getType();
        if (type.isToOne()) {
            return ref.getJoinType() == JoinType.LEFT;
        }
        return false;
    }

    public void setCascade(boolean cascade) {
        t.setLoadVsMany(cascade);
        t.setLoadVsOne(cascade);
        this.cascadeViaOuterJoin = false;
    }

    @Override
    public boolean isAll() {
        return allRecords && conditions.isEmpty();
    }

    @Override
    public Terms terms() {
        return new Terms(this);
    }

    @Override
    public boolean isSelectCustomized() {
        PopulateStrategy[] s = t.getStrategy();
        if (s != null && s.length > 0)
            return true;
        return selected instanceof SelectsImpl;
    }
}