/*
* 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 field时,存储ref 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)){
//无效条件,不接受
//注意有可能传入别的表的Field,此时Column为null
//实验性功能,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.xx这样的ref名称来描述
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计算问题。
* 当此对象作为key放入到Map中去时,orderby还没设过值。此时HashMap按照HashA值存放数据。
* 当我循环获取对象时,orderby已经设置过值。此时HashMap按照HashB值去定位数据。 非常不幸的…… 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;
}
}