jef.database.DbUtils.java Source code

Java tutorial

Introduction

Here is the source code for jef.database.DbUtils.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;

import java.io.IOException;
import java.io.Serializable;
import java.io.StringReader;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.sql.SQLTimeoutException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
import java.util.concurrent.TimeUnit;

import javax.persistence.EntityExistsException;
import javax.persistence.FetchType;
import javax.persistence.PersistenceException;
import javax.persistence.QueryTimeoutException;
import javax.sql.DataSource;

import org.apache.commons.lang.ObjectUtils;

import com.google.common.base.Objects;

import jef.accelerator.bean.BeanAccessor;
import jef.accelerator.bean.FastBeanWrapperImpl;
import jef.common.log.LogUtil;
import jef.database.Session.UpdateContext;
import jef.database.annotation.Cascade;
import jef.database.annotation.JoinType;
import jef.database.datasource.DataSourceInfo;
import jef.database.datasource.IRoutingDataSource;
import jef.database.datasource.SimpleDataSource;
import jef.database.dialect.DatabaseDialect;
import jef.database.dialect.type.ColumnMapping;
import jef.database.innerpool.PartitionSupport;
import jef.database.jsqlparser.parser.JpqlParser;
import jef.database.jsqlparser.parser.ParseException;
import jef.database.jsqlparser.parser.StSqlParser;
import jef.database.jsqlparser.parser.TokenMgrError;
import jef.database.jsqlparser.statement.create.ColumnDefinition;
import jef.database.jsqlparser.statement.create.CreateTable;
import jef.database.jsqlparser.statement.select.OrderBy;
import jef.database.jsqlparser.statement.select.Select;
import jef.database.jsqlparser.visitor.Expression;
import jef.database.jsqlparser.visitor.SelectItem;
import jef.database.meta.AbstractMetadata;
import jef.database.meta.AbstractRefField;
import jef.database.meta.DbProperty;
import jef.database.meta.ITableMetadata;
import jef.database.meta.JoinKey;
import jef.database.meta.JoinPath;
import jef.database.meta.MetaHolder;
import jef.database.meta.Reference;
import jef.database.query.AbstractEntityMappingProvider;
import jef.database.query.AbstractJoinImpl;
import jef.database.query.ConditionQuery;
import jef.database.query.DefaultPartitionCalculator;
import jef.database.query.EntityMappingProvider;
import jef.database.query.JoinElement;
import jef.database.query.JoinUtil;
import jef.database.query.JpqlExpression;
import jef.database.query.OrderField;
import jef.database.query.PartitionCalculator;
import jef.database.query.Query;
import jef.database.query.RefField;
import jef.database.query.ReferenceType;
import jef.database.query.SelectsImpl;
import jef.database.query.SqlContext;
import jef.database.query.SqlExpression;
import jef.database.routing.PartitionResult;
import jef.database.wrapper.executor.DbTask;
import jef.tools.ArrayUtils;
import jef.tools.Assert;
import jef.tools.JefConfiguration;
import jef.tools.StringUtils;
import jef.tools.reflect.BeanWrapper;
import jef.tools.reflect.GenericUtils;
import jef.tools.security.cplus.TripleDES;
import jef.tools.string.CharUtils;

public final class DbUtils {

    // db rac????dbKeyracIdorm????racId
    // private static Map<String, String> dbKey2RacIds = new HashMap<String,
    // String>();
    // private static Map<String, List<String>> racId2DbKeys = new
    // HashMap<String, List<String>>();
    // private static ThreadLocal<Boolean> isRouted = new
    // ThreadLocal<Boolean>();
    public static final int NO_RAC_ID = -1;

    public static PartitionCalculator partitionUtil = new DefaultPartitionCalculator();

    /**
     *  1? 2?JTA????JTADDL???
     * 
     *  1?CPU2. 2?256
     * 3?256??128???
     */
    public static ExecutorService es;

    static {
        int processorCount = Runtime.getRuntime().availableProcessors();
        es = new ThreadPoolExecutor(processorCount * 2, processorCount * 4, 60L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(processorCount * 4), Executors.defaultThreadFactory(),
                new CallerRunsPolicy());
    }

    /**
     * ??,?
     * 
     * @return
     * @throws IOException
     */
    private static byte[] getEncryptKey() {
        String s = JefConfiguration.get(DbCfg.DB_ENCRYPTION_KEY);
        if (StringUtils.isEmpty(s)) {
            s = "781296-5e32-89122";
        }
        return s.getBytes();
    }

    /**
     * ?
     * 
     * @param tasks
     * @throws SQLException
     */
    public static void parallelExecute(List<DbTask> tasks) throws SQLException {
        CountDownLatch latch = new CountDownLatch(tasks.size());
        Queue<SQLException> exceptions = new ConcurrentLinkedQueue<SQLException>();
        Queue<Throwable> throwables = new ConcurrentLinkedQueue<Throwable>();
        for (DbTask task : tasks) {
            task.prepare(latch, exceptions, throwables);
            DbUtils.es.execute(task);
        }
        try {
            latch.await();
        } catch (InterruptedException e) {
            throw new SQLException(e);
        }
        if (!exceptions.isEmpty()) {
            throw DbUtils.wrapExceptions(exceptions);
        }
        if (!throwables.isEmpty()) {
            throw DbUtils.toRuntimeException(throwables.peek());
        }
    }

    /*
     * ?SQL <strong>???</strong>
     * 
     * @param e
     * 
     * @param tablename
     * 
     * @param conn
     */
    public static void processError(SQLException e, String tablename, OperateTarget conn) {
        if (conn.getProfile().isIOError(e)) {
            conn.notifyDisconnect(e);
        }
        DebugUtil.setSqlState(e, tablename);
    }

    /**
     * 
     * 
     * @param class1
     * @return 
     */
    public static Reference findPath(ITableMetadata from, ITableMetadata target) {
        for (Reference r : from.getRefFieldsByRef().keySet()) {
            if (r.getTargetType() == target) {
                return r;
            }
        }
        return null;
    }

    /**
     * ??0
     * 
     * @param class1
     * @return 
     * @throws IllegalArgumentException
     */
    public static Reference findDistinctPath(ITableMetadata from, ITableMetadata target) {
        Reference ref = null;
        for (Reference reference : from.getRefFieldsByRef().keySet()) {
            if (reference.getTargetType() == target) {
                if (ref != null) {
                    throw new IllegalArgumentException(
                            "There's more than one reference to [" + target.getSimpleName() + "] in type ["
                                    + from.getSimpleName() + "],please assign the reference field name.");
                }
                ref = reference;
            }
        }
        if (ref == null) {
            throw new IllegalArgumentException("Target class " + target.getSimpleName()
                    + "of fileter-condition is not referenced by " + from.getSimpleName());
        }
        return ref;
    }

    /**
     * ???????
     * 
     * @param profile
     * @param name
     * @return
     */
    public static final String escapeColumn(DatabaseDialect profile, String name) {
        if (name == null)
            return name;
        String w = profile.getProperty(DbProperty.WRAP_FOR_KEYWORD);
        if (w != null && profile.containKeyword(name)) {
            StringBuilder sb = new StringBuilder(name.length() + 2);
            sb.append(w.charAt(0)).append(name).append(w.charAt(1));
            return sb.toString();
        }
        return name;
    }

    /**
     * ?datasource??
     * 
     * @param ds
     * @param updateDataSourceProperties
     *            ?ds?datasource
     * @return
     * @throws SQLException
     */
    public static ConnectInfo tryAnalyzeInfo(DataSource ds, boolean updateDataSourceProperties) {
        if (ds instanceof IRoutingDataSource) {
            IRoutingDataSource rds = (IRoutingDataSource) ds;
            Entry<String, DataSource> e = rds.getDefaultDatasource();
            if (e == null) {// ??DataSource
                Collection<String> names = rds.getDataSourceNames();
                if (!names.isEmpty()) {
                    String name = names.iterator().next();
                    LogUtil.warn("Can not determine default datasource name. choose [" + name
                            + "] as default datasource.");
                    return tryAnalyzeInfo(rds.getDataSource(name), updateDataSourceProperties);
                }
            } else {
                return tryAnalyzeInfo(e.getValue(), updateDataSourceProperties);
            }
        }
        if (ds instanceof DataSourceInfo) {
            DataSourceInfo dsw = (DataSourceInfo) ds;
            ConnectInfo info = new ConnectInfo();
            DbUtils.processDataSourceOfEnCrypted(dsw);

            info.url = dsw.getUrl();
            info.user = dsw.getUser();
            info.password = dsw.getPassword();
            DatabaseDialect profile = info.parse();// ?profile, ?????
            if (updateDataSourceProperties)
                profile.processConnectProperties(dsw);
            return info;// ?
        }
        return null;
    }

    /**
     * ???
     * 
     * @param conn
     * @return
     * @throws SQLException
     */
    public static ConnectInfo tryAnalyzeInfo(Connection conn) throws SQLException {
        DatabaseMetaData meta = conn.getMetaData();
        ConnectInfo info = new ConnectInfo();
        info.user = meta.getUserName();
        info.url = meta.getURL();
        info.parse();// ?profile, ?????
        return info;
    }

    /**
     * Close the given JDBC Connection and ignore any thrown exception. This is
     * useful for typical finally blocks in manual JDBC code.
     * 
     * @param con
     *            the JDBC Connection to close (may be {@code null})
     */
    public static void closeConnection(Connection con) {
        if (con != null) {
            try {
                con.close();
            } catch (SQLException ex) {
                LogUtil.exception("Could not close JDBC Connection", ex);
            } catch (Throwable ex) {
                LogUtil.exception("Unexpected exception on closing JDBC Connection", ex);
            }
        }
    }

    /**
     * SQL?
     * 
     * @param errors
     * @return
     */
    public static final SQLException wrapExceptions(Collection<SQLException> errors) {
        if (errors == null || errors.isEmpty())
            return null;
        Iterator<SQLException> iter = errors.iterator();
        SQLException root = iter.next();
        SQLException last = root;
        while (iter.hasNext()) {
            SQLException current = iter.next();
            last.setNextException(current);
            last = current;
        }
        return root;
    }

    /**
     * ??
     */
    public static String decrypt(String pass) {
        TripleDES t = new TripleDES();
        String text = t.decipher2(getEncryptKey(), pass);
        return text;
    }

    /**
     * ??
     */
    public static String ecrypt(String pass) throws IOException {
        TripleDES t = new TripleDES();
        String text = t.cipher2(getEncryptKey(), pass);
        return text;
    }

    /**
     * ?DataSource?
     * 
     * @param ds
     */
    public static void processDataSourceOfEnCrypted(DataSourceInfo ds) {
        boolean flag = JefConfiguration.getBoolean(DbCfg.DB_PASSWORD_ENCRYPTED, false);
        if (!flag) {
            return;
        }
        String old = ds.getPassword();
        if (old != null && old.matches("[a-fA-F0-9]{16,}")) {
            ds.setPassword(decrypt(old));
        }
    }

    /**
     * ?select??
     * 
     * @param sql
     * @return
     * @throws ParseException
     */
    @SuppressWarnings("unchecked")
    public static List<SelectItem> parseSelectItems(String sql) throws ParseException {
        JpqlParser parser = new JpqlParser(new StringReader(sql));
        return parser.SelectItemsList();
    }

    public static ColumnDefinition parseColumnDef(String def) {
        String sql = StringUtils.concat("create table A (B ", def, ")");
        StSqlParser parser = new StSqlParser(new StringReader(sql));
        try {
            CreateTable ct = parser.CreateTable();
            return ct.getColumnDefinitions().get(0);
        } catch (ParseException e) {
            throw new IllegalArgumentException("Parse sql error:" + sql, e);
        }
    }

    /**
     * ?select?
     * 
     * @param sql
     * @return
     * @throws ParseException
     */
    public static Select parseSelect(String sql) throws ParseException {
        JpqlParser parser = new JpqlParser(new StringReader(sql));
        return parser.Select();
    }

    /**
     * ?Select?(SQL)
     * 
     * @param sql
     * @return
     * @throws ParseException
     */
    public static Select parseNativeSelect(String sql) throws ParseException {
        StSqlParser parser = new StSqlParser(new StringReader(sql));
        return parser.Select();
    }

    /**
     * ??
     * 
     * @param sql
     * @return
     * @throws ParseException
     */
    public static Expression parseExpression(String sql) throws ParseException {
        JpqlParser parser = new JpqlParser(new StringReader(sql));
        return parser.SimpleExpression();
    }

    /**
     * ?OrderBy
     * 
     * @param sql
     * @return
     * @throws ParseException
     */
    public static OrderBy parseOrderBy(String sql) {
        StSqlParser parser = new StSqlParser(new StringReader("ORDER BY " + sql));
        try {
            return parser.OrderByElements();
        } catch (ParseException e) {
            throw new PersistenceException(sql, e);
        }
    }

    /**
     * ??
     * 
     * @param sql
     * @return
     * @throws ParseException
     */
    public static Expression parseBinaryExpression(String sql) throws ParseException {
        JpqlParser parser = new JpqlParser(new StringReader(sql));
        return parser.Expression();
    }

    /**
     * ??
     * 
     * @param sql
     * @return
     * @throws ParseException
     */
    public static jef.database.jsqlparser.visitor.Statement parseStatement(String sql) throws ParseException {
        JpqlParser parser = new JpqlParser(new StringReader(sql));
        try {
            return parser.Statement();
        } catch (ParseException e) {
            LogUtil.error("ErrorSQL:" + sql);
            throw e;
        } catch (TokenMgrError e) {
            LogUtil.error("ErrorSQL:" + sql);
            throw e;
        }
    }

    /**
     * ????
     * 
     * @param <T>
     * @param queryObj
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T extends IQueryableEntity> JoinElement toReferenceJoinQuery(Query<T> queryObj,
            List<Reference> excludeRef) {
        // ??
        Map<Reference, List<AbstractRefField>> map = queryObj.isCascadeViaOuterJoin()
                ? DbUtils.getMergeAsOuterJoinRef(queryObj)
                : Collections.EMPTY_MAP;
        Query<?>[] otherQuery = queryObj.getOtherQueryProvider();

        if (otherQuery.length == 0 && map.isEmpty()) {
            return queryObj;
        }
        // 
        AbstractJoinImpl j = DbUtils.getJoin(queryObj, map, ArrayUtils.asList(otherQuery), excludeRef);
        if (j != null) {
            j.setFetchSize(queryObj.getFetchSize());
            j.setMaxResult(queryObj.getMaxResult());
            j.setQueryTimeout(queryObj.getQueryTimeout());
            if (queryObj.getSelectItems() != null) {
                List<QueryAlias> qs = j.allElements();
                for (int i = 0; i < qs.size(); i++) {
                    qs.get(i).setAlias("T" + (i + 1));
                }
                SelectsImpl select = new jef.database.query.SelectsImpl(qs);
                select.merge((AbstractEntityMappingProvider) queryObj.getSelectItems());
                j.setSelectItems(select);
            }
            // TODO cacheable, Transformer,
            // attributeQuery????????
            j.setResultTransformer(queryObj.getResultTransformer());
            j.setCacheable(queryObj.isCacheable());
            // FilterCondition?
            if (queryObj.getFilterCondition() != null) {
                for (QueryAlias qa : j.allElements()) {
                    if (qa.getStaticRef() != null) {
                        List<Condition> con = queryObj.getFilterCondition().get(qa.getStaticRef());
                        if (con != null) {
                            j.addRefConditions(qa.getQuery(), con);
                        }
                    }
                }
            }
            return j;
        } else {
            return queryObj;
        }
    }

    /**
     * ?????<br>
     *  you_are_boy -> YouAreBoy
     * 
     * @param name
     *            ??
     * @param capitalize
     *            ???
     * @return ????
     */
    public static String underlineToUpper(String name, boolean capitalize) {
        char[] r = new char[name.length()];
        int n = 0;
        boolean nextUpper = capitalize;
        for (char c : name.toCharArray()) {
            if (c == '_') {
                nextUpper = true;
            } else {
                if (nextUpper) {
                    r[n] = Character.toUpperCase(c);
                    nextUpper = false;
                } else {
                    r[n] = Character.toLowerCase(c);
                }

                n++;
            }
        }
        return new String(r, 0, n);
    }

    /**
     * ??????? : iLoveYou -> i_love_you
     * 
     * @param name
     *            ???
     * @return ????
     */
    public static String upperToUnderline(String name) {
        if (name == null)
            return null;
        boolean skipUpper = true;
        StringBuilder sb = new StringBuilder();
        char[] chars = name.toCharArray();
        sb.append(chars[0]);
        for (int i = 1; i < chars.length; i++) {
            if (CharUtils.isUpperAlpha(chars[i])) {
                if (!skipUpper) {
                    sb.append('_').append(chars[i]);
                    skipUpper = true;
                } else {
                    if (i + 2 < chars.length && CharUtils.isLowerAlpha(chars[i + 1])) {
                        sb.append('_').append(chars[i]);
                    } else {
                        sb.append(chars[i]);
                    }
                }
            } else {
                sb.append(chars[i]);
                skipUpper = false;
            }
        }
        return sb.toString().toUpperCase();
    }

    /**
     * 
     * 
     * @param data
     *            
     * @param pk
     *            ?Map?
     */
    public static void setPrimaryKeyValue(IQueryableEntity data, Object pk) throws PersistenceException {
        List<ColumnMapping> fields = MetaHolder.getMeta(data).getPKFields();
        if (fields.isEmpty())
            return;
        Assert.notNull(pk);
        // if (pk instanceof Map) {
        // Map<String, Object> pkMap = (Map<String, Object>) pk;
        // BeanWrapper wrapper = BeanWrapper.wrap(data, BeanWrapper.FAST);
        // for (Field f : fields) {
        // if (wrapper.isWritableProperty(f.name())) {
        // wrapper.setPropertyValue(f.name(), pkMap.get(f.name()));
        // }
        // }
        // } else
        if (pk.getClass().isArray()) {
            int length = Array.getLength(pk);
            int n = 0;
            Assert.isTrue(length == fields.size());
            for (ColumnMapping f : fields) {
                f.getFieldAccessor().set(data, Array.get(pk, n++));
            }
        } else {
            if (fields.size() != 1) {
                throw new PersistenceException("No Proper PK fields!");
            }
            fields.get(0).getFieldAccessor().set(data, pk);
        }
    }

    /**
     * ??
     */
    public static Map<String, Object> getPrimaryKeyValueMap(IQueryableEntity data) {
        ITableMetadata meta = MetaHolder.getMeta(data);
        int len = meta.getPKFields().size();
        if (len == 0)
            return null;
        Map<String, Object> keyValMap = new HashMap<String, Object>();
        for (int i = 0; i < len; i++) {
            ColumnMapping field = meta.getPKFields().get(i);
            if (!isValidPKValue(data, meta, field)) {
                return null;
            }
            keyValMap.put(field.fieldName(), field.getFieldAccessor().get(data));
        }
        return keyValMap;
    }

    /**
     * ??
     */
    public static List<Object> getPrimaryKeyValue(IQueryableEntity data) {
        ITableMetadata meta = MetaHolder.getMeta(data);
        if (meta.getPKFields().isEmpty())
            return null;

        int len = meta.getPKFields().size();
        Object[] result = new Object[len];
        for (int i = 0; i < len; i++) {
            ColumnMapping field = meta.getPKFields().get(i);
            if (!isValidPKValue(data, meta, field)) {
                return null;
            }
            result[i] = field.getFieldAccessor().get(data);
        }
        return Arrays.asList(result);
    }

    // ??????
    public static List<Serializable> getPKValueSafe(IQueryableEntity data) {
        ITableMetadata meta = MetaHolder.getMeta(data);
        if (meta.getPKFields().isEmpty())
            return null;
        int len = meta.getPKFields().size();
        Serializable[] result = new Serializable[len];
        for (int i = 0; i < len; i++) {
            ColumnMapping field = meta.getPKFields().get(i);
            result[i] = (Serializable) field.getFieldAccessor().get(data);
        }
        return Arrays.asList(result);
    }

    /**
     * field??????
     * 
     * @param field
     *            
     * @param feature
     *            ?
     * @param tableAlias
     *            ??
     * @return ?SQL??
     * @deprecated use
     *             {@linkplain #toColumnName(ColumnMapping, DatabaseDialect, String)}
     *             instead
     */
    public static String toColumnName(Field field, DatabaseDialect feature, String tableAlias) {
        if (field instanceof MetadataContainer) {
            ITableMetadata meta = ((MetadataContainer) field).getMeta();
            return getColumnName(meta, field, tableAlias, feature);
        }
        ITableMetadata meta = getTableMeta(field);
        if (field instanceof JpqlExpression) {
            return ((JpqlExpression) field).toSqlAndBindAttribs(null, feature);
        } else {
            return getColumnName(meta, field, tableAlias, feature);
        }
    }

    /**
     * ?,
     * 
     * @param column
     *            
     * @param profile
     *            ?
     * @param tableAlias
     *            ??
     * @return SQL??
     */
    public static String toColumnName(ColumnMapping column, DatabaseDialect profile, String alias) {
        if (alias != null) {
            StringBuilder sb = new StringBuilder();
            sb.append(alias).append('.').append(column.getColumnName(profile, true));
            return sb.toString();
        } else {
            return column.getColumnName(profile, true);
        }
    }

    /**
     * ?field???
     * 
     * @param field
     *            field
     * @param alias
     *            ???
     * @param profile
     *            ??
     * @return ???
     * 
     * @deprecated ?
     */
    public static String getColumnName(ITableMetadata meta, Field fld, String alias, DatabaseDialect profile) {
        if (alias == null) {
            return meta.getColumnName(fld, profile, true);
        } else {
            if (fld instanceof JpqlExpression) {
                throw new UnsupportedOperationException();
            } else {
                StringBuilder sb = new StringBuilder();
                sb.append(alias).append('.').append(meta.getColumnName(fld, profile, true));
                return sb.toString();
            }
        }
    }

    /*
     * ????????
     * 
     * @param d 
     * 
     * @param map ???
     * 
     * @param queryProvider : ??
     * 
     * @return
     */
    protected static AbstractJoinImpl getJoin(Query<?> d, Map<Reference, List<AbstractRefField>> map,
            List<Query<?>> queryProvider, List<Reference> exclude) {
        AbstractJoinImpl join = null;
        // ????????
        for (Reference r : map.keySet()) {
            if (exclude != null && exclude.contains(r))
                continue;
            Query<?> tQuery = null;
            for (Query<?> t : queryProvider) {
                if (t.getInstance().getClass() == r.getTargetType().getThisType()) {
                    queryProvider.remove(t);
                    tQuery = t;
                    break;
                }
            }
            if (join == null) {
                join = JoinUtil.create(d, r, tQuery);
                Assert.notNull(join, "Invalid Reference:" + r.toString());
            } else {
                join = JoinUtil.create(join, r, tQuery);
                Assert.notNull(join, "Invalid Reference:" + r.toString());
            }
        }

        // REF???????Join
        List<QueryAlias> qs = join == null ? Arrays.asList(new QueryAlias(null, d)) : join.allElements();
        AbstractEntityMappingProvider tmpContext = new SqlContext(-1, qs, null);
        for (Condition c : d.getConditions()) {
            checkIfThereIsExQueryInRefField(c.getField(), tmpContext, queryProvider);
        }
        for (OrderField c : d.getOrderBy()) {
            checkIfThereIsExQueryInRefField(c.getField(), tmpContext, queryProvider);
        }

        // ??Query??
        while (queryProvider.size() > 0) {
            int left = queryProvider.size();
            for (Iterator<Query<?>> iter = queryProvider.iterator(); iter.hasNext();) {
                Query<?> tq = iter.next();
                AbstractJoinImpl ng = JoinUtil.create(join == null ? d : join, tq, null, null, false);
                if (ng != null) {
                    iter.remove();
                    join = ng;
                }
            }
            if (left == queryProvider.size()) {// reverse look for...
                for (Iterator<Query<?>> iter = queryProvider.iterator(); iter.hasNext();) {
                    Query<?> tq = iter.next();
                    AbstractJoinImpl ng = JoinUtil.create(join == null ? d : join, tq, null, null, true);
                    if (ng != null) {
                        iter.remove();
                        join = ng;
                    }
                }
            }

            if (left == queryProvider.size()) {// ???
                LogUtil.error("There 's still " + queryProvider.size() + " query not added into join.");
                break;
            }
        }
        if (join != null)
            join.fillAttribute((Query<?>) d);
        return join;
    }

    /*
     * field(fieldRefField)
     */
    private static void checkIfThereIsExQueryInRefField(Field field, AbstractEntityMappingProvider tmpContext,
            List<Query<?>> qt) {
        // RefField???refFieldQuery
        if (field instanceof RefField) {
            rebindRefField((RefField) field, tmpContext, qt);
        } else if (field instanceof IConditionField) {
            IConditionField condf = (IConditionField) field;
            processConditionField(condf, tmpContext, qt);
        }
    }

    // ?REFField()
    private static void rebindRefField(RefField ref, AbstractEntityMappingProvider tmpContext, List<Query<?>> qt) {
        if (!ref.isBindOn(tmpContext)) {
            Query<?> refQuery = ref.getInstanceQuery(null);
            for (Query<?> extQuery : qt) {
                if (refQuery == extQuery) {
                    return;
                } else if (refQuery.getType() == extQuery.getType()
                        && refQuery.getConditions().equals(extQuery.getConditions())) {
                    // ref.rebind(extQuery,null);
                    return;
                }
            }
            qt.add(refQuery);
        }
    }

    // ?REFField()
    private static void processConditionField(IConditionField container, AbstractEntityMappingProvider tmpContext,
            List<Query<?>> qt) {
        for (Condition c : container.getConditions()) {
            Field field = c.getField();
            if (field instanceof IConditionField) {
                processConditionField((IConditionField) field, tmpContext, qt);
            } else if (field instanceof RefField) {
                rebindRefField((RefField) field, tmpContext, qt);
            }
        }
    }

    /**
     * ????
     * 
     * @param subs
     * @param container
     * @return
     * @throws SQLException
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    protected static Object toProperContainerType(Collection<? extends IQueryableEntity> subs, Class<?> container,
            Class<?> bean, AbstractRefField config) throws SQLException {
        if (container.isAssignableFrom(subs.getClass())) {
            return subs;
        }
        if (container == Set.class) {
            HashSet set = new HashSet();
            set.addAll(subs);
            return set;
        } else if (container == List.class) {
            ArrayList list = new ArrayList();
            list.addAll(subs);
            return list;
        } else if (container == Array.class) {
            return subs.toArray();
        } else if (container == Map.class) {
            Cascade cascade = config.getCascadeInfo();
            if (cascade == null) {
                throw new SQLException("@Cascade annotation is required for Map mapping " + config.toString());
            }
            Map map = new HashMap();
            String key = cascade.keyOfMap();
            BeanAccessor ba = FastBeanWrapperImpl.getAccessorFor(bean);
            if (StringUtils.isEmpty(cascade.valueOfMap())) {
                for (IQueryableEntity e : subs) {
                    map.put(ba.getProperty(e, key), e);
                }
            } else {
                String vField = cascade.valueOfMap();
                for (IQueryableEntity e : subs) {
                    map.put(ba.getProperty(e, key), ba.getProperty(e, vField));
                }
            }
            return map;
        }
        throw new SQLException("the type " + container.getName() + " is not supported as a collection container.");
    }

    /**
     * 
     * 
     * @param data
     * @return
     */
    protected static Map<Reference, List<AbstractRefField>> getMergeAsOuterJoinRef(Query<?> q) {
        Map<Reference, List<AbstractRefField>> result = new HashMap<Reference, List<AbstractRefField>>(5);
        // ?
        for (Map.Entry<Reference, List<AbstractRefField>> entry : q.getMeta().getRefFieldsByRef().entrySet()) {
            Reference key = entry.getKey();
            if (key.getType().isToOne()) {
                List<AbstractRefField> value = entry.getValue();
                AbstractRefField first = value.get(0);
                if (first.getFetch() == FetchType.LAZY) {
                    continue;
                }
                result.put(key, value);
            }
        }
        // ???
        if (q.getFilterCondition() != null) {
            for (Reference ref : q.getFilterCondition().keySet()) {
                if (ref.getType().isToOne() && ref.getJoinType() != JoinType.LEFT) {
                    result.remove(ref);
                }
            }
        }
        return result;
    }

    /**
     * Ref
     * 
     * @param data
     *            
     * @param excludeReference
     *            ??null?
     * @return
     */
    protected static Map<Reference, List<AbstractRefField>> getLazyLoadRef(ITableMetadata data,
            Collection<Reference> excludeReference) {
        // ==null???,?
        // ???
        if (excludeReference == null) {
            Map<Reference, List<AbstractRefField>> result = new HashMap<Reference, List<AbstractRefField>>(5);
            for (Map.Entry<Reference, List<AbstractRefField>> entry : data.getRefFieldsByRef().entrySet()) {
                Reference key = entry.getKey();
                ReferenceType type = key.getType();
                List<AbstractRefField> value = entry.getValue();
                if (type.isToOne()) {
                    if (value.get(0).getFetch() == FetchType.LAZY) {
                        result.put(key, value);
                    }
                } else {
                    result.put(key, value);
                }
            }
            return result;
            // ????
        } else if (excludeReference.isEmpty()) {
            return data.getRefFieldsByRef();
            // !!???,??ref
        } else {
            Map<Reference, List<AbstractRefField>> result = new HashMap<Reference, List<AbstractRefField>>(
                    data.getRefFieldsByRef());
            for (Reference ref : excludeReference) {
                result.remove(ref);
            }
            return result;
        }
    }

    /**
     * class
     * 
     * @param field
     * @return
     */
    public static AbstractMetadata getTableMeta(Field field) {
        Assert.notNull(field);
        if (field instanceof MetadataContainer) {
            return (AbstractMetadata) ((MetadataContainer) field).getMeta();
        }
        if (field instanceof Enum) {
            // FIXME ??
            Class<?> c = field.getClass().getDeclaringClass();
            Assert.isTrue(IQueryableEntity.class.isAssignableFrom(c),
                    field + " is not a defined in a IQueryableEntity's meta-model.");
            return MetaHolder.getMeta(c.asSubclass(IQueryableEntity.class));
        } else {
            throw new IllegalArgumentException(
                    "method 'getTableMeta' doesn't support field type of " + field.getClass());
        }
    }

    /**
     * 
     * 
     * @param field
     *            
     * @return ??
     */
    public static ColumnMapping toColumnMapping(Field field) {
        if (field instanceof ColumnMapping) {
            return (ColumnMapping) field;
        } else if (field instanceof MetadataContainer) {
            return ((MetadataContainer) field).getMeta().getColumnDef(field);
        } else if (field instanceof Enum) {
            Class<?> c = field.getClass().getDeclaringClass();
            Assert.isTrue(IQueryableEntity.class.isAssignableFrom(c),
                    field + " is not a defined in a IQueryableEntity's meta-model.");
            ITableMetadata meta = MetaHolder.getMeta(c);
            return meta.getColumnDef(field);
        }
        throw new IllegalArgumentException(
                "method 'getTableMeta' doesn't support field type of " + field.getClass());
    }

    /**
     * ??
     * 
     * @param bean
     * @param rs
     * @param query
     * @return
     */
    protected static boolean appendRefCondition(BeanWrapper bean, JoinPath rs, Query<?> query,
            List<Condition> filters) {
        query.clearQuery();
        boolean hasValue = false;
        for (JoinKey r : rs.getJoinKeys()) {
            Object value = bean.getPropertyValue(r.getLeft().name());
            query.addCondition(r.getRightAsField(), value);
            if (value != null)
                hasValue = true;
        }
        // ??hasValue
        for (JoinKey condition : rs.getJoinExpression()) {
            Field f = condition.getLeft();
            if (f == null) {
                continue;
            }
            if (f instanceof SqlExpression) {
                query.addCondition(condition);
                continue;
            }
            if (f instanceof JpqlExpression) {
                query.addCondition(condition);
                continue;
            }
            ITableMetadata meta = DbUtils.getTableMeta(f);
            if (meta == query.getMeta()) {
                query.addCondition(condition);
            }
        }
        if (filters != null) {
            Query<?> bq = query;
            bq.getConditions().addAll(filters);
        }
        return hasValue;
    }

    /**
     * 
     * 
     * @param obj
     * @param query
     * @param isUpdate
     *            isUpdatetrue?updateMap
     *            ??whereset?
     * @param force
     * @return
     */
    protected static void fillConditionFromField(IQueryableEntity obj, Query<?> query, UpdateContext update,
            boolean force) {
        Assert.isTrue(query.getConditions().isEmpty());
        ITableMetadata meta = query.getMeta();
        boolean isUpdate = update != null;
        if (fillPKConditions(obj, meta, query, isUpdate, force)) {
            if (isUpdate)
                update.setIsPkQuery(true);
            return;
        }
        populateExampleConditions(obj);
    }

    /*
     * (nojava doc)
     */
    private static boolean isValidPKValue(IQueryableEntity obj, ITableMetadata meta, ColumnMapping field) {
        Class<?> type = field.getFieldAccessor().getType();
        Object value = field.getFieldAccessor().get(obj);
        if (field.isUnsavedValueDeclared()) {
            return !field.isUnsavedValue(value);
        } else if (type.isPrimitive()) {
            if (field.isUnsavedValue(value)) {
                if (!obj.isUsed(field.field()))
                    return false;
            }
            return true;
        } else {
            return value != null;
        }
    }

    /**
     * ?? ? 1???? 2???
     * 
     * @param value
     * @param field
     * @param isUsed
     * @return  true
     */
    public static boolean isInvalidValue(Object value, ColumnMapping field, boolean isUsed) {
        if (field.isUnsavedValueDeclared()) {
            return field.isUnsavedValue(value);
        }
        // ?????
        // primitive
        if (!isUsed && field.getFieldAccessor().getType().isPrimitive()) {
            return field.isUnsavedValue(value);
        }
        return false;
    }

    /*
     * @param obj 
     * 
     * @param meta ?
     * 
     * @param wrapper 
     * 
     * @param query 
     * 
     * @param removePkUpdate
     * 
     * @param force
     * 
     * @return
     */
    protected static boolean fillPKConditions(IQueryableEntity obj, ITableMetadata meta, Query<?> query,
            boolean isUpdate, boolean force) {
        if (meta.getPKFields().isEmpty())
            return false;
        if (!force) {
            for (ColumnMapping field : meta.getPKFields()) {
                if (!isValidPKValue(obj, meta, field))
                    return false;
            }
        }
        Map<Field, Object> map = obj.getUpdateValueMap();
        for (ColumnMapping mapping : meta.getPKFields()) {
            Object value = mapping.getFieldAccessor().get(obj);
            Field field = mapping.field();
            query.addCondition(field, value);
            if (isUpdate && map.containsKey(field)) {//
                Object v = map.get(field);
                if (Objects.equal(value, v)) {
                    map.remove(field);
                }
            }
        }
        return true;
    }

    /**
     * ?update'' <br>
     * ??updateMap
     * 
     * @param <T>
     * @param prepareObj
     */
    public static <T extends IQueryableEntity> void fillUpdateMap(T... obj) {
        if (obj == null || obj.length == 0)
            return;
        ITableMetadata m = MetaHolder.getMeta(obj[0]);
        for (T o : obj) {
            BeanWrapper bean = BeanWrapper.wrap(o);
            for (ColumnMapping mType : m.getColumns()) {
                if (mType.isPk()) {
                    continue;
                }
                Field field = mType.field();
                o.prepareUpdate(field, bean.getPropertyValue(field.name()), true);
            }
        }
    }

    /**
     * ??Example?
     * 
     * @param obj
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T extends IQueryableEntity> Query<T> populateExampleConditions(T obj, String... properties) {
        Query<T> query = (Query<T>) obj.getQuery();
        ITableMetadata meta = query.getMeta();
        BeanWrapper bw = BeanWrapper.wrap(obj, BeanWrapper.FAST);
        if (properties.length == 0) {
            for (ColumnMapping mType : meta.getColumns()) {
                Field field = mType.field();
                if (obj.isUsed(field)) {
                    Object value = bw.getPropertyValue(field.name());
                    query.addCondition(field, value);
                }
            }
        } else {
            for (String s : properties) {
                Field field = meta.getField(s);
                if (field == null) {
                    throw new IllegalArgumentException("field [" + s + "] not found in object " + meta.getName());
                }
                Object value = bw.getPropertyValue(field.name());
                query.addCondition(field, value);
            }
        }
        return query;
    }

    /**
     * ??
     * 
     * @param queryObj
     * @return
     */
    protected static EntityMappingProvider getMappingProvider(ConditionQuery queryObj) {
        if (queryObj instanceof JoinElement) {
            return ((JoinElement) queryObj).getSelectItems();
        }
        return null;
    }

    /**
     * ??????
     * 
     * @param name
     * @param needTranslate
     * @return
     */
    public static PartitionResult[] toTableNames(IQueryableEntity obj, String customName, Query<?> q,
            PartitionSupport processor) {
        AbstractMetadata meta = q == null ? MetaHolder.getMeta(obj) : (AbstractMetadata) q.getMeta();
        if (StringUtils.isNotEmpty(customName))
            return new PartitionResult[] { new PartitionResult(customName).setDatabase(meta.getBindDsName()) };
        PartitionResult[] result = partitionUtil.toTableNames(meta, obj, q, processor,
                ORMConfig.getInstance().isFilterAbsentTables());
        // if(ORMConfig.getInstance().isDebugMode()){
        // LogUtil.show("Partitions:"+Arrays.toString(result));
        // }
        return result;
    }

    /**
     * ????
     * 
     * 
     * @param meta
     *            ???
     * @param processor
     * @param operateType
     *            ???0 1 ?? 2 + 3 ??? 4
     *            ?
     * 
     * 
     * @return
     */
    public static PartitionResult[] toTableNames(ITableMetadata meta, PartitionSupport processor, int operateType) {
        Assert.notNull(meta);
        // long start=System.nanoTime();
        // try{
        return partitionUtil.toTableNames((AbstractMetadata) meta, processor, operateType);
        // }finally{
        // System.out.println((System.nanoTime()-start)/1000+"us");
        // }
    }

    /**
     * ???????
     * 
     * @param obj
     * @param customName
     * @param q
     * @param profile
     * @return
     */
    public static PartitionResult toTableName(IQueryableEntity obj, String customName, Query<?> q,
            PartitionSupport profile) {
        AbstractMetadata meta = obj == null ? (AbstractMetadata) q.getMeta() : MetaHolder.getMeta(obj);
        if (StringUtils.isNotEmpty(customName))
            return new PartitionResult(customName).setDatabase(meta.getBindDsName());
        PartitionResult result = partitionUtil.toTableName(meta, obj, q, profile);
        Assert.notNull(result);
        return result;
    }

    // /**
    // * dbkey?????
    // * ?rac??
    // * @param dbkey,anotherDbKeytrue
    // * @return
    // */
    // public static boolean isSameDb(String dbkey,String anotherDbKey){
    // if(StringUtils.isEmpty(dbkey)&&StringUtils.isEmpty(anotherDbKey)){
    // return true;
    // }
    // if(StringUtils.isEmpty(dbkey))
    // return false;
    // if(dbkey.equalsIgnoreCase(anotherDbKey))
    // return true;
    // String racId = getRacId(dbkey);
    // String anotherRacId = getRacId(anotherDbKey);
    //
    // return
    // racId!=null&&!String.valueOf(NO_RAC_ID).equals(racId)&&racId.equalsIgnoreCase(anotherRacId);
    // }

    /**
     * ?
     * 
     * @param rs
     */
    public static void close(ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
            }
        }
    }

    /**
     * Statement
     * 
     * @param st
     */
    public static void close(Statement st) {
        try {
            if (st != null)
                st.close();
        } catch (SQLException e) {
        }
    }

    /**
     * RuntimeException
     * 
     * @param e
     * @return
     */
    public static PersistenceException toRuntimeException(SQLException e) {
        String s = e.getSQLState();
        if (e instanceof SQLIntegrityConstraintViolationException) {
            return new EntityExistsException(e);
        } else if (e instanceof SQLTimeoutException) {
            return new QueryTimeoutException(s, e);
        }
        return new PersistenceException(s, e);
    }

    /**
     * Runtime
     * 
     * @param e
     * @return
     */
    public static RuntimeException toRuntimeException(Throwable e) {
        while (true) {
            if (e instanceof RuntimeException) {
                return (RuntimeException) e;
            }
            if (e instanceof InvocationTargetException) {
                e = e.getCause();
                continue;
            }
            if (e instanceof Error) {
                throw (Error) e;
            }
            if (e instanceof SQLException) {
                return toRuntimeException((SQLException) e);
            }
            return new IllegalStateException(e);
        }
    }

    private static final String DEFAULT_SEQUENCE_PATTERN = "S_%s";
    private static final int TABLE_NAME_MAX_LENGTH = 26;

    public static String calcSeqNameByTable(String schema, String tableName, String columnName) {
        String pattern = JefConfiguration.get(DbCfg.SEQUENCE_NAME_PATTERN);
        if (StringUtils.isBlank(pattern))
            pattern = DEFAULT_SEQUENCE_PATTERN;
        String tblName = tableName;
        if (tblName.length() > TABLE_NAME_MAX_LENGTH) {
            tblName = tblName.substring(0, TABLE_NAME_MAX_LENGTH);
        }
        if (schema == null) {
            return StringUtils.upperCase(String.format(pattern, tblName));
        } else {
            String name = String.format(pattern, tblName);
            return new StringBuilder(schema.length() + name.length() + 1).append(schema).append('.').append(name)
                    .toString().toUpperCase();
        }
    }

    // TODO Oracle RAC?URL
    // public static String getSimpleUrl(String url) {
    // if (url.toLowerCase().indexOf("service_name") > -1) {
    // StringBuilder sb=new StringBuilder();
    // StringTokenizer st=new StringTokenizer(url,"()");
    // while(st.hasMoreTokens()){
    // String str=st.nextToken();
    // String x=str.toUpperCase();
    // if(x.startsWith("SERVICE_NAME") || x.startsWith("HOST")){
    // sb.append('(').append(str).append(')');
    // }
    // }
    // url = sb.toString();
    // }
    // return url;
    // }

    /**
     * JDBC URLdatasource???
     * 
     * @param url
     * @param user
     * @param password
     * @return
     */
    public static SimpleDataSource createSimpleDataSource(String url, String user, String password) {
        SimpleDataSource s = new SimpleDataSource();
        s.setUsername(user);
        s.setUrl(url);
        s.setPassword(password);
        return s;
    }

    /**
     * 
     * 
     * @param subclass
     * @param superclass
     * @return
     */
    public static Type[] getTypeParameters(Class<?> subclass, Class<?> superclass) {
        if (superclass == null) {// ?
            if (subclass.getSuperclass() == Object.class && subclass.getInterfaces().length > 0) {
                superclass = subclass.getInterfaces()[0];
            } else {
                superclass = subclass.getSuperclass();
            }
        }
        Type type = GenericUtils.getSuperType(null, subclass, superclass);
        if (type instanceof ParameterizedType) {
            return ((ParameterizedType) type).getActualTypeArguments();
        }
        throw new RuntimeException("Can not get the generic param type for class:" + subclass.getName());
    }

    /**
     * Map
     * 
     * @param <T>
     * @param changedObj
     * @param oldObj
     * @throws SQLException
     * @return the object who is able to update.
     */
    public static <T extends IQueryableEntity> T compareToUpdateMap(T changedObj, T oldObj) {
        // Assert.isTrue(Objects.equal(DbUtils.getPrimaryKeyValue(changedObj),
        // DbUtils.getPKValueSafe(oldObj)),
        // "For consistence, the two parameter must hava equally primary
        // keys.");
        ITableMetadata m = MetaHolder.getMeta(oldObj);
        boolean safeMerge = ORMConfig.getInstance().isSafeMerge();

        for (ColumnMapping mType : m.getColumns()) {
            if (mType.isPk())
                continue;
            Field field = mType.field();
            Object value = mType.getFieldAccessor().get(changedObj);

            boolean used = changedObj.isUsed(field);
            if (mType.isGenerated() && !used) {
                continue;
            }
            // ?
            if (safeMerge && DbUtils.isInvalidValue(value, mType, used)) {
                continue;
            }
            Object oldValue = mType.getFieldAccessor().get(oldObj);
            if (!ObjectUtils.equals(value, oldValue)) {
                oldObj.prepareUpdate(field, value);
            }
        }
        return oldObj;
    }
}