package jef.database.query; import java.util.AbstractList; import java.util.ArrayList; import java.util.List; import jef.database.DbUtils; import jef.database.ORMConfig; import jef.database.QueryAlias; import jef.database.SelectProcessor; import jef.database.SqlProcessor; import jef.database.annotation.JoinType; import jef.database.dialect.DatabaseDialect; import jef.database.meta.AbstractRefField; import jef.database.meta.IReferenceAllTable; import jef.database.meta.IReferenceColumn; import jef.database.meta.ISelectProvider; import jef.database.meta.ITableMetadata; import jef.database.meta.JoinKey; import jef.database.meta.JoinPath; import jef.database.meta.Reference; import jef.database.wrapper.clause.BindSql; import jef.database.wrapper.clause.GroupClause; import jef.database.wrapper.clause.QueryClause; import jef.database.wrapper.clause.QueryClauseImpl; import jef.tools.Assert; import jef.tools.StringUtils; /** * Join Query实现 * * @author jiyi * */ final class JoinImpl2 extends AbstractJoinImpl { private final List<JoinCondition> conditions = new ArrayList<JoinCondition>(); private final List<Query<?>> qlist = new AbstractList<Query<?>>() { public Query<?> get(int index) { return context.queries.get(index).getQuery(); } public int size() { return context.queries.size(); } }; JoinImpl2(Query<?> left, Query<?> right, JoinCondition joinCondition, Reference ref) { List<QueryAlias> qs = new ArrayList<QueryAlias>(6); qs.add(new QueryAlias(null, left)); QueryAlias rightTableDef = new QueryAlias(null, right); rightTableDef.setStaticRef(ref); qs.add(rightTableDef); SqlContext context = new SqlContext(-1, qs, null); this.context = context; this.conditions.add(joinCondition); if (ref != null) { List<AbstractRefField> referenceOfRight = ref.getAllRefFields(); for (ISelectProvider p : referenceOfRight) { if (p.isSingleColumn()) { rightTableDef.addField((IReferenceColumn) p); } else { rightTableDef.addField((IReferenceAllTable) p); } } } } private void add(Query<?> right, JoinCondition joinCondition, Reference ref, QueryAlias refFromLeft) { QueryAlias rightTableDef = new QueryAlias(null, right); rightTableDef.setStaticRef(ref); context.queries.add(rightTableDef); this.conditions.add(joinCondition); if (ref != null) { setReferenceOfRight(rightTableDef, refFromLeft.getReferenceObj(), ref.getAllRefFields()); } } /** * 间接引用的关系描述 * * @param reference * @param p */ private void setReferenceOfRight(QueryAlias rightTableDef, IReferenceAllTable reference, List<AbstractRefField> p) { if (p == null || p.isEmpty()) return; String lastName = reference == null ? null : reference.getName(); if (StringUtils.isEmpty(lastName)) { for (ISelectProvider select : p) { if (select.isSingleColumn()) { rightTableDef.addField((IReferenceColumn) select); } else { rightTableDef.addField((IReferenceAllTable) select); } } } else { // if(p==null || p.size()!=1){ // return; // } for (ISelectProvider select : p) { AbstractRefField ref = (AbstractRefField) select; ISelectProvider result = ref.toNestedDesc(lastName); if (result.isSingleColumn()) { rightTableDef.addField((IReferenceColumn) result); } else { rightTableDef.addField((IReferenceAllTable) result); } } } } static AbstractJoinImpl create(JoinElement parent, Query<?> e, JoinPath pathHint, JoinType forceType, boolean reverse) { if (parent instanceof Query<?>) { Query<?> query = (Query<?>) parent; JoinPath path = pathHint == null ? null : pathHint.accept(query, e); Reference ref = null; if (reverse) { if (path == null) {// 反向查找 ITableMetadata meta = e.getMeta(); ref = DbUtils.findPath(meta, query.getMeta()); if (ref != null) { path = ref.getHint().flip();// 颠倒一下 ref = Reference.createRevse(path, ref); } } } else { if (path == null) {// 正向查找 ITableMetadata meta = query.getMeta(); ref = DbUtils.findPath(meta, e.getMeta()); if (ref != null) path = ref.getHint(); } } if (path != null) { JoinCondition condition = new JoinCondition(query, path); condition.setType(forceType); return new JoinImpl2(query, e, condition, ref); } } else if (parent instanceof JoinImpl2) { JoinImpl2 join = (JoinImpl2) parent; if (pathHint != null) { pathHint = pathHint.accept(parent, e); if (pathHint != null) { JoinCondition con = new JoinCondition(null, pathHint); con.setType(forceType); join.add(e, con, null, null); return join; } } else { return join.findMatchReferenceJoin(e, forceType, reverse); } } else { throw new IllegalArgumentException("parent join invalid:" + parent.getClass().getName()); } return null; } static AbstractJoinImpl create(JoinElement parent, Reference e, Query<?> tQuery) { if (parent instanceof Query<?>) { Query<?> left = (Query<?>) parent; if (e.getThisType() != left.getMeta()) { throw new IllegalArgumentException(left.getMeta().getName() + " ::" + e.toString()); } JoinPath path = e.toJoinPath(); tQuery = checkAndCreateTarget(path, tQuery, e); return new JoinImpl2(left, tQuery, new JoinCondition(left, path), e); } else if (parent instanceof JoinImpl2) { JoinPath path = e.toJoinPath(); tQuery = checkAndCreateTarget(path, tQuery, e); JoinImpl2 join = (JoinImpl2) parent; for (QueryAlias querya : join.allElements()) { Query<?> left = querya.getQuery(); if (e.getThisType() == left.getMeta()) {// 只找第一个符合类型的表就可以了。 JoinCondition joinCondition = new JoinCondition(left, path); join.add(tQuery, joinCondition, e, querya); return join; } } } throw new IllegalArgumentException("No path from " + parent + " to table " + e.getTargetType().getName()); } private static Query<?> checkAndCreateTarget(JoinPath path, Query<?> right, Reference e) { Assert.notNull(path); if (right == null) { return ReadOnlyQuery.getEmptyQuery(e.getTargetType());// QB.create(hint.getTargetType().getThisType());//;//为什么不用空查询? } else { // "输入的连接条件对象和Hint的目标不是同一张表的"; Assert.isTrue(e.getTargetType().getThisType().isAssignableFrom(right.getInstance().getClass()), "The right side is not match to the target type of join key."); return right; } } // 成功返回自身,不成功返回null private AbstractJoinImpl findMatchReferenceJoin(Query<?> e, JoinType forceType, boolean reverse) { List<QueryAlias> ds = allElements(); Reference ref = null; JoinPath path = null; for (QueryAlias querya : ds) { Query<?> query = querya.getQuery(); if (reverse) { ITableMetadata meta = e.getMeta(); ref = DbUtils.findPath(meta, query.getMeta()); if (ref != null) { path = ref.getHint().flip();// 颠倒一下 ref = Reference.createRevse(path, ref); } } else { ITableMetadata meta = query.getMeta(); ref = DbUtils.findPath(meta, e.getMeta()); if (ref != null) path = ref.getHint(); } // 最后在全局配置中查找 if (path != null) { JoinCondition cond = new JoinCondition(query, path); cond.setType(forceType); add(e, cond, ref, querya); return this; } } return null; } /** * 返回SQL的表名部分 select xxx from [.....] where xxx,包括 join ... on ...等部分 * * @return */ public String toTableDefinitionSql(SqlProcessor processor, SqlContext context, DatabaseDialect profile, boolean batch) { StringBuilder sb = new StringBuilder(64); toTableDefSql(sb, context.queries.get(0), processor, context); // sb.append(' '); String wrap = ORMConfig.getInstance().wrap; for (int i = 0; i < conditions.size(); i++) { JoinCondition relations = conditions.get(i); sb.append(wrap); sb.append(' ').append(relations.getType().nameLower()).append(" join "); QueryAlias right = context.queries.get(i + 1); toTableDefSql(sb, right, processor, context); sb.append(" ON "); relations.toOnExpression(sb, context, right, processor, profile, batch); } return sb.toString(); } private void toTableDefSql(StringBuilder sb, QueryAlias obj, SqlProcessor processor, SqlContext context) { String alias = obj.getAlias(); Assert.notNull(alias); Query<?> q = obj.getQuery(); String table; if (q.getAttribute(ConditionQuery.CUSTOM_TABLE_NAME) != null) { table = String.valueOf(q.getAttribute(ConditionQuery.CUSTOM_TABLE_NAME)); } else { table = DbUtils.toTableName(q.getInstance(), null, q, processor.getPartitionSupport()).getAsOneTable(); } sb.append(DbUtils.escapeColumn(processor.getProfile(), table)).append(' ').append(alias); } private void checkInstance(Query<?> right2) { if (this.allElements().contains(right2)) { throw new IllegalArgumentException("The query instanceof " + right2 + " has been added into this join already"); } } public Join leftJoin(Query<?> right, JoinKey... keys) { processAddJoin(JoinType.LEFT, right, keys); return this; } public Join rightJoin(Query<?> right, JoinKey... keys) { processAddJoin(JoinType.RIGHT, right, keys); return this; } public Join innerJoin(Query<?> right, JoinKey... keys) { processAddJoin(JoinType.INNER, right, keys); return this; } public Join fullJoin(Query<?> right, JoinKey... keys) { processAddJoin(JoinType.FULL, right, keys); return this; } private void processAddJoin(JoinType type, Query<?> right, JoinKey[] keys) { checkInstance(right); JoinPath path = null; if (keys.length > 0) { path = new JoinPath(type, keys); } path = path.accept(this, right); if (path != null) { JoinCondition con = new JoinCondition(null, path); add(right, con, null, null); } else { throw new IllegalArgumentException("Can not create join, the join key not match the input table"); } } public List<Query<?>> elements() { return qlist; } public List<QueryAlias> allElements() { return context.queries; } private int prepareSize = 0; public SqlContext prepare() { if (prepareSize != context.queries.size()) { int i = 0; for (QueryAlias q : context.queries) { q.setAlias("T" + (++i)); } prepareSize = i; } return context; } public List<Reference> getIncludedCascadeOuterJoin() { ArrayList<Reference> result = new ArrayList<Reference>(); for (QueryAlias qa : context.queries) { Reference ref = qa.getStaticRef(); if (ref != null) result.add(ref); } return result; } @Override public QueryClause toQuerySql(SelectProcessor processor, SqlContext context, boolean order) { @SuppressWarnings("deprecation") DatabaseDialect profile = processor.getProfile(); GroupClause groupClause = SelectProcessor.toGroupAndHavingClause(this, context, profile); QueryClauseImpl result = new QueryClauseImpl(profile); result.setSelectPart(SelectProcessor.toSelectSql(context, groupClause, profile)); result.setTableDefinition(toTableDefinitionSql(processor.parent, context, profile,false)); BindSql whereResult = processor.parent.toWhereClause(this, context, null, profile, false); result.setWherePart(whereResult.getSql()); result.setBind(whereResult.getBind()); result.setGrouphavingPart(groupClause); if (order) result.setOrderbyPart(SelectProcessor.toOrderClause(this, context, profile)); return result; } }