package jef.database.query; import jef.database.DbUtils; import jef.database.Field; import jef.database.QueryAlias; import jef.database.dialect.type.ColumnMapping; import jef.database.meta.AbstractMetadata; import jef.database.meta.ITableMetadata; import jef.database.meta.Reference; import jef.tools.Assert; import org.apache.commons.lang.builder.HashCodeBuilder; import com.google.common.base.Objects; /** * 能绑定到一个SQL中查询表上的Field描述。 * 正常情况下Field只能对应到一张表(静态上)上。不能对应到查询中的一个表实例(动态)上。 * 因此,在多表查询时—— * 1、Refield描述当前Field所对应的表,并不是其所存在的Query对象。 * 2、当一个Condition中同时涉及两个表的Field时,需要显式的明确每个Field所属的Query。 * 这个对象就专门为了描述这种情况。 * <p> * * RefField可以迟绑定(在查询时根据类型自动匹配对应Query,仅限查询中该类型的Query唯一时), * 早绑定(在查询前指定对用Query),但无论是哪种绑定方式,RefField所在的Condition仅限于单次查询使用。(区别于CascadeCondition)。 * <p> * RefField的一个灵活之处是,其可以作用于级联引起的Join查询中,甚至如果加上了RefField,会强行开启单次Join查询。(即便是在对多关系中。 * 此时主表的记录会因为和子表Join而变为多份。不作为BUG,因为RefField为仅允许显式添加的场景下,我们认为这种Join是用户期望的而予以支持). * * @author jiyi * */ public class RefField implements Field,LazyQueryBindField{ private static final long serialVersionUID = 1925450637038779L; //下面两个字段信息构成一个绑定: private Query<?> instance; //构建时不再绑定 private AbstractEntityMappingProvider dynamicBindContext;//如果当前的绑定是属于动态绑定,需要存储此值 private ColumnMapping field; Reference ref;//默认为null,用于迟邦定的情况下匹配Query /** * 绑定构造,显式的指定field所绑定的查询表 * @param ins * @param field */ public RefField(Query<?> ins,Field field){ ColumnMapping mapping=ins.getMeta().getColumnDef(field); Assert.notNull(mapping,"The field in refField must be a metamodel field."); this.field=mapping; this.instance=ins; } /** * 绑定构造,显式的指定field所绑定的查询表 * @param p * @param field */ public RefField(Query<?> p, String field) { this.instance=p; this.field=p.getMeta().findField(field); Assert.notNull(field); } /** * 非绑定构造,会在后期自动匹配一个Query实例。 * 如果该查询中,相同的表出现多次(如自表关联),那么会抛出异常。 * @param fld */ public RefField(Field fld) { ColumnMapping mapping=DbUtils.toColumnMapping(fld); Assert.notNull(mapping,"The field in refField must be a metamodel field."); this.field=mapping; } /** * <tt>框架内部使用</tt> * 传入context,获取绑定的Query。 */ //如果传入null,且没有绑定,则使用Emptyinstanceo public Query<?> getInstanceQuery(AbstractEntityMappingProvider context) { //这个逻辑有问题,在分页场合下reffield存在重用现象,因此绑定一次以后的动态查询实例在第二次查询中将不再使用。 //所以不能再用第一次查询时的查询实例,而是要在第二次查询的上下文中重新查找 if(instance!=null){ if(dynamicBindContext==null || context==dynamicBindContext){ return instance; } } AbstractMetadata type=DbUtils.getTableMeta(getField()); if(context!=null){ QueryAlias al=context.findQuery(type,this.ref); if(al!=null){ rebind(al.getQuery(),context); } } if(instance!=null){ return instance; }else{ return ReadOnlyQuery.getEmptyQuery(type); } } /** * <tt>框架内部使用</tt> * 检查当前引用是否已经通过给定的上下文得到绑定 * @param context * @return */ public boolean isBindOn(AbstractEntityMappingProvider context) { //这个逻辑有问题,在分页场合下reffield存在重用现象,因此绑定一次以后的动态查询实例在第二次查询中将不再使用。 //所以不能再用第一次查询时的查询实例,而是要在第二次查询的上下文中重新查找 if(instance!=null){ if(dynamicBindContext==null || context==dynamicBindContext){ return true; } } ITableMetadata type=DbUtils.getTableMeta(getField()); if(context!=null){ QueryAlias al=context.findQuery(type,this.ref); if(al!=null){ return true; } } return false; } public ColumnMapping getFieldDef(){ return field; } /** * 返回field * @return field */ public Field getField() { return field.field(); } /** * 返回名称 * @return name */ public String name() { return field.fieldName(); } @Override public String toString() { return (instance==null?field.getClass().getName():instance.getType())+"."+field.fieldName(); } //重新绑定查询实体 void rebind(Query<?> extQuery,AbstractEntityMappingProvider dynamicContext) { this.instance=extQuery; this.dynamicBindContext=dynamicContext; } public boolean isBind() { return instance!=null; } @Override public int hashCode() { HashCodeBuilder hash=new HashCodeBuilder(); hash.append(field); if(instance!=null){ hash.append(instance.getType()); } return hash.toHashCode(); } @Override public boolean equals(Object obj) { if(!(obj instanceof RefField)){ return false; } RefField rhs=(RefField)obj; if(!Objects.equal(field, rhs.field)){ return false; } if(instance==null && rhs.instance==null){ return true; }else if(instance==null || rhs.instance==null){ return false; } if(!Objects.equal(instance.getType(), rhs.instance.getType())){ return false; } return true; } public ITableMetadata getMeta() { return field.getMeta(); } public Query<?> getBind() { return instance; } public void setBind(Query<?> query) { this.instance=query; } }