/*
* 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.meta;
import java.util.ArrayList;
import java.util.List;
import com.google.common.base.Objects;
import jef.database.annotation.JoinType;
import jef.database.query.ReferenceType;
import jef.tools.Assert;
/**
* 关于多表操作的三个概念设计
* jef.database.meta.JoinKey类:描述两个实体间的一组关系。两个实体间的关系可能由多个jef.database.meta.JoinKey构成
*
* Reference:描述两个实体的关系,这个关系除了包含多个JoinKey意外,还包含了A或者B中引用对方的那个字段名
* @author Jiyi
*
*/
public class Reference{
/**
* 链接目标关系,定义了关联的操作方式,
* 详细参见{@link ReferenceType}
*/
private ReferenceType type;
/**
* 发起表
*/
private ITableMetadata fromType;
/**
* 要链接到一个目标的表(类)。
*/
private ITableMetadata targetType;
/**
* 连接路径
*/
private JoinPath hint;
/**
* 产生一个反向的Reference关系
* @param path
* @param r
* @return
*/
public static Reference createRevse(JoinPath path,Reference r){
Reference ref=new Reference(r.getTargetType(),r.getType(),r.getThisType());
ref.setHint(path);
return ref;
}
/**
* 构造
* @param target 引用表
* @param refType 引用类型
* @param source 被引用表
*/
public Reference(ITableMetadata target, ReferenceType refType, ITableMetadata source) {
this.type=refType;
this.fromType=source;
this.targetType=target;
Assert.notNull(type);
Assert.notNull(fromType);
Assert.notNull(targetType);
}
/**
* 空构造
* 必须有。会通过反射调用
*/
@SuppressWarnings("unused")
private Reference(){};
/**
* 返回引用类型
* @return 引用类型
*/
public ReferenceType getType() {
return type;
}
/**
* 设置引用类型
* @param type 引用类型
*/
public void setType(ReferenceType type) {
this.type = type;
}
/**
* 返回被引用表
* @return
*/
public ITableMetadata getTargetType() {
return targetType;
}
/**
* 设置被引用表
* @param targetType
*/
public void setTargetType(ITableMetadata targetType) {
this.targetType = targetType;
}
/**
* 返回引用表
* @return
*/
public ITableMetadata getThisType() {
return fromType;
}
public String toString(){
StringBuilder sb=new StringBuilder();
if(fromType!=null)
sb.append("Join:").append(fromType.getThisType().getSimpleName()).append("->");
if(targetType!=null)
sb.append(targetType.getThisType().getSimpleName());
if(this.hint!=null){
sb.append(", CustomPath: "+hint);
sb.append("\n");
}
return sb.toString();
}
//当没有配置Join路径时,通过自动查找的方式来寻找Join路径。
//这是一种为了减少配置工作量而不得不做的容错处理。(不太推荐依赖这种容错机制)
private JoinPath findPath(ITableMetadata thisType, ITableMetadata targetType) {
//先在当前表中查询已定义的可用引用关系。
for(Reference ref:thisType.getRefFieldsByRef().keySet()){
if(ref==this){
continue;
}
if(ref.getThisType()==thisType && ref.getTargetType()==targetType && ref.getHint()!=null){
return ref.getHint();
}
}
//再到目标表中查询可用的反向引用关系。
for(Reference ref:targetType.getRefFieldsByRef().keySet()){
if(ref.getThisType()==targetType && ref.getTargetType()==thisType && ref.getHint()!=null){
return ref.getHint().flip();
}
}
throw new IllegalArgumentException("Can not find the join path from "+thisType.getSimpleName()+" to "+ targetType.getSimpleName());
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Reference))return false;
Reference o=(Reference)obj;
if(!Objects.equal(this.fromType, o.fromType))return false;
if(!Objects.equal(this.targetType, o.targetType))return false;
if(this.type!=o.type)return false;
if(!Objects.equal(this.hint, o.hint))return false;
return true;
}
@Override
public int hashCode() {
int hashCode=this.fromType.hashCode()+this.targetType.hashCode()+this.type.hashCode();
return hashCode;
}
public JoinPath getHint() {
return hint;
}
/**
* 返回到目标实体表的路径
*/
public JoinPath toJoinPath(){
Assert.notNull(this.targetType,"No join target found.");
if(this.hint!=null )return hint;
this.hint=findPath(fromType, targetType);
return hint;
}
public void setHint(JoinPath hint) {
// Query<?> leftq=ReadOnlyQuery.getEmptyQuery(this.fromType);
// Query<?> rightq=ReadOnlyQuery.getEmptyQuery(this.targetType);
// JoinPath path=hint.accept(leftq, rightq);
JoinPath path=hint.accept(this.fromType, this.targetType);
if(path==null){
throw new IllegalArgumentException("The join key is invalid." + hint.toString());
}
this.hint = hint;
}
/**
* 返回关联配置的Join类型。
* 默认类型为左连接
* @return Join类型。
* @see JoinType
*/
public JoinType getJoinType(){
JoinPath path=toJoinPath();
if(path==null){
return JoinType.LEFT;
}
return path.getType();
}
/**
* 得到使用这个引用的全部引用字段
* @return
*/
public List<AbstractRefField> getAllRefFields(){
return fromType.getRefFieldsByRef().get(this);
}
private List<Reference> reverse;
/**
* 得到目前对象对当前对象的反向引用关系。这个方法必须在所有级联关系初始化后调用,一般在第一次查询操作的时候才能调用。
* @return
*/
public List<Reference> getExistReverseReference(){
if(null==reverse){
reverse=new ArrayList<Reference>();
for(Reference rr:getTargetType().getRefFieldsByRef().keySet()){
if(isReverse(rr)){
//出现多个反向关联,由于JoinDesc的限定条件存在,正向关联被分化,当反向关联查找时,会出现重复的关联。
//该问题发生了,看看有没有什么办法更准确的判断反响关联
if(!reverse.isEmpty()){
throw new IllegalArgumentException();
}
reverse.add(rr);
}
}
}
return reverse;
}
private boolean isReverse(Reference ref){
if(this.fromType!=ref.targetType || this.targetType!=ref.fromType || type.reverse()!=ref.type){
return false;
}
if(hint.getJoinKeys().length!=ref.hint.getJoinKeys().length){
return false;
}
for(JoinKey key:hint.getJoinKeys()){
if(!hasReverse(key,ref.hint.getJoinKeys())){
return false;
}
}
return true;
}
private boolean hasReverse(JoinKey key, JoinKey[] joinKeys) {
String left=key.getField().name();
String right=key.getRightAsField().name();
for(JoinKey k:joinKeys){
if(right.equals(k.getField().name()) && left.equals(k.getRightAsField().name())){
return true;
}
}
return false;
}
}