com.taobao.tddl.jdbc.group.TGroupConnection.java Source code

Java tutorial

Introduction

Here is the source code for com.taobao.tddl.jdbc.group.TGroupConnection.java

Source

/*(C) 2007-2012 Alibaba Group Holding Limited.   
 *This program is free software; you can redistribute it and/or modify   
*it under the terms of the GNU General Public License version 2 as   
* published by the Free Software Foundation.   
* Authors:   
*   junyu <junyu@taobao.com> , shenxun <shenxun@taobao.com>,   
*   linxuan <linxuan@taobao.com> ,qihao <qihao@taobao.com>    
*/
package com.taobao.tddl.jdbc.group;

import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.ResultSet;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.taobao.tddl.jdbc.group.dbselector.DBSelector;
import com.taobao.tddl.jdbc.group.dbselector.DBSelector.AbstractDataSourceTryer;
import com.taobao.tddl.jdbc.group.dbselector.DBSelector.DataSourceTryer;
import com.taobao.tddl.jdbc.group.util.ExceptionUtils;
import com.taobao.tddl.jdbc.group.util.GroupHintParser;

/**
 *JDBC
 * 
 * 1. ConnectionstatementConnectionstatement
 * 2. 
 * 
 *1statementdb1statementdb1
 * stmt1 = TGroupConnection.createStatement
 * rs1 = stmt1.executeQuery --create connection on db1 and execute success
 * stmt2 = conn..createStatement
 * rs2 = stmt2..executeQuery --db1 failed then...
 * db2db1connection
 * astmtrsdb2
 *    exceptionstms1rs1
 * b: TGroupConnectionbaseConnection
 * 
 * 
 * 
 *
 * TGroupConnectiondbDBGroup
 * TGroupConnection
 * baseConnectionbaseConnection
 * 
 * 
 * @author linxuan
 * @author yangzhu
 *
 */
public class TGroupConnection implements Connection {
    private static final Log log = LogFactory.getLog(TGroupConnection.class);

    private TGroupDataSource tGroupDataSource;

    // DataSource.getConnection(String username, String password)
    // jdbc
    private String username;
    private String password;

    public TGroupConnection(TGroupDataSource tGroupDataSource) {
        this.tGroupDataSource = tGroupDataSource;
    }

    public TGroupConnection(TGroupDataSource tGroupDataSource, String username, String password) {
        this(tGroupDataSource);
        this.username = username;
        this.password = password;
    }

    /* ========================================================================
     * connectiongetter/setter
     * ======================================================================*/
    private Connection rBaseConnection;
    private Connection wBaseConnection;
    //private String rBaseDsKey; // rBaseConnectionkey
    //private String wBaseDsKey; // wBaseConnectionkey
    //private int rBaseDataSourceIndex = -2; // rBaseConnectionIndex
    //private int wBaseDataSourceIndex = -2; // wBaseConnectionIndex
    private DataSourceWrapper rBaseDsWrapper;
    private DataSourceWrapper wBaseDsWrapper;

    Connection getBaseConnection(String sql, boolean isRead) throws SQLException {
        int dataSourceIndex = DBSelector.NOT_EXIST_USER_SPECIFIED_INDEX;
        if (sql == null) {
            //Connection
            dataSourceIndex = ThreadLocalDataSourceIndex.getIndex();
        } else {
            dataSourceIndex = GroupHintParser.convertHint2Index(sql);
            if (dataSourceIndex < 0) {
                dataSourceIndex = ThreadLocalDataSourceIndex.getIndex();
            }
        }

        if (dataSourceIndex != DBSelector.NOT_EXIST_USER_SPECIFIED_INDEX) {
            if (log.isDebugEnabled()) {
                log.debug("dataSourceIndex=" + dataSourceIndex);
            }
            //
            if (!isAutoCommit) {
                if (wBaseDsWrapper != null && !wBaseDsWrapper.isMatchDataSourceIndex(dataSourceIndex))
                    throw new SQLException("Transaction in another dataSourceIndex: " + dataSourceIndex);
            }
            if (isRead) {
                if (rBaseDsWrapper != null && !rBaseDsWrapper.isMatchDataSourceIndex(dataSourceIndex))
                    closeReadConnection();
            } else {
                if (wBaseDsWrapper != null && !wBaseDsWrapper.isMatchDataSourceIndex(dataSourceIndex))
                    closeWriteConnection();
            }
        }

        //
        if (isRead && isAutoCommit) {
            //
            return wBaseConnection != null && wBaseDsWrapper.hasReadWeight() ? wBaseConnection : rBaseConnection;
            //rBaseConnectionnull
        } else {
            if (wBaseConnection != null) {
                this.tGroupDataSource.setWriteTarget(wBaseDsWrapper);
                return wBaseConnection;
            } else if (rBaseConnection != null && rBaseDsWrapper.hasWriteWeight()) {
                //null
                wBaseConnection = rBaseConnection; //wBaseConnection
                //
                if (wBaseConnection.getAutoCommit() != isAutoCommit)
                    wBaseConnection.setAutoCommit(isAutoCommit);
                //wBaseDsKey = rBaseDsKey;
                wBaseDsWrapper = rBaseDsWrapper;
                this.tGroupDataSource.setWriteTarget(wBaseDsWrapper);
                return wBaseConnection;
            } else {
                return null;
            }
        }
    }

    /**
     * DataSourceConnection
     * TGroupStatementTGroupPreparedStatement
     */
    Connection createNewConnection(DataSourceWrapper dsw, boolean isRead) throws SQLException {
        ///
        Connection conn;
        if (username != null)
            conn = dsw.getConnection(username, password);
        else
            conn = dsw.getConnection();

        //
        setBaseConnection(conn, dsw, isRead && isAutoCommit);

        //  setAutoCommit,   TGroupConnection#setAutoCommit 
        if (!isRead || !isAutoCommit)
            conn.setAutoCommit(isAutoCommit); //AutoCommitisAutoCommit

        return conn;
    }

    private void setBaseConnection(Connection baseConnection, DataSourceWrapper dsw, boolean isRead) {
        if (baseConnection == null) {
            log.warn("setBaseConnection to null !!");
        }

        if (isRead)
            closeReadConnection();
        else
            closeWriteConnection();

        if (isRead) {
            rBaseConnection = baseConnection;
            //this.rBaseDsKey = dsw.getDataSourceKey();
            //this.rBaseDataSourceIndex = dsw.getDataSourceIndex();
            this.rBaseDsWrapper = dsw;
        } else {
            wBaseConnection = baseConnection;
            //this.wBaseDsKey = dsw.getDataSourceKey();
            //this.wBaseDataSourceIndex = dsw.getDataSourceIndex();
            this.wBaseDsWrapper = dsw;
            this.tGroupDataSource.setWriteTarget(dsw);
        }
    }

    private void closeReadConnection() {
        //r|wBaseConnection
        if (rBaseConnection != null && rBaseConnection != wBaseConnection) {
            try {
                rBaseConnection.close(); // baseConnection
            } catch (SQLException e) {
                log.error("close rBaseConnection failed.", e);
            }
            rBaseDsWrapper = null;
            rBaseConnection = null;
        }
    }

    private void closeWriteConnection() {
        //r|wBaseConnection
        if (wBaseConnection != null && rBaseConnection != wBaseConnection) {
            try {
                wBaseConnection.close(); // baseConnection
            } catch (SQLException e) {
                log.error("close wBaseConnection failed.", e);
            }
            wBaseDsWrapper = null;
            wBaseConnection = null;
        }
    }

    private Set<TGroupStatement> openedStatements = new HashSet<TGroupStatement>(2);

    void removeOpenedStatements(Statement statement) {
        if (!openedStatements.remove(statement)) {
            log.warn("current statmenet " + statement + " doesn't exist!");
        }
    }

    /* ========================================================================
     * 
     * ======================================================================*/
    private boolean closed;

    private void checkClosed() throws SQLException {
        if (closed) {
            throw new SQLException("No operations allowed after connection closed.");
        }
    }

    public boolean isClosed() throws SQLException {
        return closed;
    }

    @SuppressWarnings("unchecked")
    public void close() throws SQLException {
        if (closed) {
            return;
        }
        closed = true;

        List<SQLException> exceptions = new LinkedList<SQLException>();
        try {
            // statement
            for (TGroupStatement stmt : openedStatements) {
                try {
                    stmt.close(false);
                } catch (SQLException e) {
                    exceptions.add(e);
                }
            }

            try {
                if (rBaseConnection != null && !rBaseConnection.isClosed()) {
                    rBaseConnection.close();
                }
            } catch (SQLException e) {
                exceptions.add(e);
            }
            try {
                if (wBaseConnection != null && !wBaseConnection.isClosed()) {
                    wBaseConnection.close();
                }
            } catch (SQLException e) {
                exceptions.add(e);
            }
        } finally {
            openedStatements.clear();
            // openedStatements = null; //
            rBaseConnection = null;
            wBaseConnection = null;

            ThreadLocalDataSourceIndex.clearIndex();
        }
        ExceptionUtils.throwSQLException(exceptions, "close tconnection", Collections.EMPTY_LIST);
    }

    /* ========================================================================
     * Statement
     * ======================================================================*/
    public TGroupStatement createStatement() throws SQLException {
        checkClosed();
        TGroupStatement stmt = new TGroupStatement(tGroupDataSource, this);
        openedStatements.add(stmt);
        return stmt;
    }

    public TGroupStatement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        TGroupStatement stmt = createStatement();
        stmt.setResultSetType(resultSetType);
        stmt.setResultSetConcurrency(resultSetConcurrency);
        return stmt;
    }

    public TGroupStatement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
            throws SQLException {
        TGroupStatement stmt = createStatement(resultSetType, resultSetConcurrency);
        stmt.setResultSetHoldability(resultSetHoldability);
        return stmt;
    }

    /* ========================================================================
     * PreparedStatement
     * ======================================================================*/
    public TGroupPreparedStatement prepareStatement(String sql) throws SQLException {
        checkClosed();
        TGroupPreparedStatement stmt = new TGroupPreparedStatement(tGroupDataSource, this, sql);
        openedStatements.add(stmt);
        return stmt;
    }

    public TGroupPreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
            throws SQLException {
        TGroupPreparedStatement stmt = prepareStatement(sql);
        stmt.setResultSetType(resultSetType);
        stmt.setResultSetConcurrency(resultSetConcurrency);
        return stmt;
    }

    public TGroupPreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
            int resultSetHoldability) throws SQLException {
        TGroupPreparedStatement stmt = prepareStatement(sql, resultSetType, resultSetConcurrency);
        stmt.setResultSetHoldability(resultSetHoldability);
        return stmt;
    }

    public TGroupPreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
        TGroupPreparedStatement stmt = prepareStatement(sql);
        stmt.setAutoGeneratedKeys(autoGeneratedKeys);
        return stmt;
    }

    public TGroupPreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
        TGroupPreparedStatement stmt = prepareStatement(sql);
        stmt.setColumnIndexes(columnIndexes);
        return stmt;
    }

    public TGroupPreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
        TGroupPreparedStatement stmt = prepareStatement(sql);
        stmt.setColumnNames(columnNames);
        return stmt;
    }

    /* ========================================================================
     * CallableStatementCallableStatement
     * ======================================================================*/
    private DataSourceTryer<CallableStatement> getCallableStatementTryer = new AbstractDataSourceTryer<CallableStatement>() {
        public CallableStatement tryOnDataSource(DataSourceWrapper dsw, Object... args) throws SQLException {
            String sql = (String) args[0];
            int resultSetType = (Integer) args[1];
            int resultSetConcurrency = (Integer) args[2];
            int resultSetHoldability = (Integer) args[3];
            Connection conn = TGroupConnection.this.createNewConnection(dsw, false);
            return getCallableStatement(conn, sql, resultSetType, resultSetConcurrency, resultSetHoldability);
        }
    };

    private CallableStatement getCallableStatement(Connection conn, String sql, int resultSetType,
            int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        if (resultSetType == Integer.MIN_VALUE) {
            return conn.prepareCall(sql);
        } else if (resultSetHoldability == Integer.MIN_VALUE) {
            return conn.prepareCall(sql, resultSetType, resultSetConcurrency);
        } else {
            return conn.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
        }
    }

    public TGroupCallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
            int resultSetHoldability) throws SQLException {
        checkClosed();
        CallableStatement target;

        Connection conn = this.getBaseConnection(sql, false); //
        if (conn != null) {
            sql = GroupHintParser.removeTddlGroupHint(sql);
            target = getCallableStatement(conn, sql, resultSetType, resultSetConcurrency, resultSetHoldability);
        } else {
            // hint
            Integer dataSourceIndex = GroupHintParser.convertHint2Index(sql);
            sql = GroupHintParser.removeTddlGroupHint(sql);
            if (dataSourceIndex < 0) {
                dataSourceIndex = ThreadLocalDataSourceIndex.getIndex();
            }
            target = tGroupDataSource.getDBSelector(false).tryExecute(null, getCallableStatementTryer,
                    this.tGroupDataSource.getRetryingTimes(), sql, resultSetType, resultSetConcurrency,
                    resultSetHoldability, dataSourceIndex);
        }
        TGroupCallableStatement stmt = new TGroupCallableStatement(tGroupDataSource, this, target, sql);
        if (resultSetType != Integer.MIN_VALUE) {
            stmt.setResultSetType(resultSetType);
            stmt.setResultSetConcurrency(resultSetConcurrency);
        }
        if (resultSetHoldability != Integer.MIN_VALUE) {
            stmt.setResultSetHoldability(resultSetHoldability);
        }
        openedStatements.add(stmt);
        return stmt;
    }

    public TGroupCallableStatement prepareCall(String sql) throws SQLException {
        return prepareCall(sql, Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
    }

    public TGroupCallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)
            throws SQLException {
        return prepareCall(sql, resultSetType, resultSetConcurrency, Integer.MIN_VALUE);
    }

    /* ========================================================================
     * JDBCautoCommitcommit/rollbackTransactionIsolation
     * ======================================================================*/
    private boolean isAutoCommit = true; // jdbctrue

    public void setAutoCommit(boolean autoCommit0) throws SQLException {
        checkClosed();
        if (this.isAutoCommit == autoCommit0) {
            // ,true==true false == false: 
            return;
        }
        this.isAutoCommit = autoCommit0;
        /*/////////////////////////////////////
        if (this.rBaseConnection != null) {
           this.rBaseConnection.setAutoCommit(autoCommit0);
        }
        */
        if (this.wBaseConnection != null) {
            this.wBaseConnection.setAutoCommit(autoCommit0);
        }
    }

    public boolean getAutoCommit() throws SQLException {
        checkClosed();
        return isAutoCommit;
    }

    public void commit() throws SQLException {
        checkClosed();
        if (isAutoCommit) {
            return;
        }

        /*/////////////////////////////////////
        if (rBaseConnection != null) {
           try {
        rBaseConnection.commit();
           } catch (SQLException e) {
        log.error("Commit failed on " + this.rBaseDsKey + ":" + e.getMessage());
        throw e;
           }
        }
        */
        if (wBaseConnection != null) {
            try {
                wBaseConnection.commit();
            } catch (SQLException e) {
                log.error("Commit failed on " + this.wBaseDsWrapper.getDataSourceKey() + ":" + e.getMessage());
                throw e;
            }
        }
    }

    public void rollback() throws SQLException {
        checkClosed();
        if (isAutoCommit) {
            return;
        }

        /*/////////////////////////////////////
        if (rBaseConnection != null) {
           try {
        rBaseConnection.rollback();
           } catch (SQLException e) {
        log.error("Rollback failed on " + this.rBaseDsKey + ":" + e.getMessage());
        throw e;
           }
        }
        */

        if (wBaseConnection != null) {
            try {
                wBaseConnection.rollback();
            } catch (SQLException e) {
                log.error("Rollback failed on " + this.wBaseDsWrapper.getDataSourceKey() + ":" + e.getMessage());
                throw e;
            }
        }
    }

    // TODO: 
    private int transactionIsolation = -1;

    public int getTransactionIsolation() throws SQLException {
        checkClosed();
        return transactionIsolation;
    }

    public void setTransactionIsolation(int transactionIsolation) throws SQLException {
        checkClosed();
        this.transactionIsolation = transactionIsolation;
    }

    /* ========================================================================
     * SQLWarning  DatabaseMetaData
     * ======================================================================*/
    public SQLWarning getWarnings() throws SQLException {
        checkClosed();
        if (rBaseConnection != null)
            return rBaseConnection.getWarnings();
        else if (wBaseConnection != null)
            return wBaseConnection.getWarnings();
        else
            return null;
    }

    public void clearWarnings() throws SQLException {
        checkClosed();
        if (rBaseConnection != null)
            rBaseConnection.clearWarnings();
        if (wBaseConnection != null)
            wBaseConnection.clearWarnings();
    }

    public DatabaseMetaData getMetaData() throws SQLException {
        checkClosed();
        if (rBaseConnection != null)
            return rBaseConnection.getMetaData();
        else if (wBaseConnection != null)
            return wBaseConnection.getMetaData();
        else
            return new TGroupDatabaseMetaData();
    }

    /* ========================================================================
     * 
     * ======================================================================*/
    public void rollback(Savepoint savepoint) throws SQLException {
        throw new UnsupportedOperationException("rollback");
    }

    public Savepoint setSavepoint() throws SQLException {
        throw new UnsupportedOperationException("setSavepoint");
    }

    public Savepoint setSavepoint(String name) throws SQLException {
        throw new UnsupportedOperationException("setSavepoint");
    }

    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
        throw new UnsupportedOperationException("releaseSavepoint");
    }

    public String getCatalog() throws SQLException {
        throw new UnsupportedOperationException("getCatalog");
    }

    public void setCatalog(String catalog) throws SQLException {
        throw new UnsupportedOperationException("setCatalog");
    }

    public int getHoldability() throws SQLException {
        return ResultSet.CLOSE_CURSORS_AT_COMMIT;
    }

    public void setHoldability(int holdability) throws SQLException {
        /*
         *  mysql5.xjdbc driverholdability 
         * .CLOSE_CURSORS_AT_COMMIT close
         */

        // mysql 5.xjdbc driverResultSet.HOLD_CURSORS_OVER_COMMIT
        throw new UnsupportedOperationException("setHoldability");
    }

    public Map<String, Class<?>> getTypeMap() throws SQLException {
        throw new UnsupportedOperationException("getTypeMap");
    }

    public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
        throw new UnsupportedOperationException("setTypeMap");
    }

    public String nativeSQL(String sql) throws SQLException {
        throw new UnsupportedOperationException("nativeSQL");
    }

    /**
     * 
     * @author junyu
     */
    public boolean isReadOnly() throws SQLException {
        return false;
    }

    /**
     * 
     * @author junyu
     */
    public void setReadOnly(boolean readOnly) throws SQLException {

    }

    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return this.getClass().isAssignableFrom(iface);
    }

    @SuppressWarnings("unchecked")
    public <T> T unwrap(Class<T> iface) throws SQLException {
        try {
            return (T) this;
        } catch (Exception e) {
            throw new SQLException(e);
        }
    }

    public Clob createClob() throws SQLException {
        throw new SQLException("not support exception");
    }

    public Blob createBlob() throws SQLException {
        throw new SQLException("not support exception");
    }

    public NClob createNClob() throws SQLException {
        throw new SQLException("not support exception");
    }

    public SQLXML createSQLXML() throws SQLException {
        throw new SQLException("not support exception");
    }

    public boolean isValid(int timeout) throws SQLException {
        throw new SQLException("not support exception");
    }

    public void setClientInfo(String name, String value) throws SQLClientInfoException {
        throw new RuntimeException("not support exception");
    }

    public void setClientInfo(Properties properties) throws SQLClientInfoException {
        throw new RuntimeException("not support exception");
    }

    public String getClientInfo(String name) throws SQLException {
        throw new SQLException("not support exception");
    }

    public Properties getClientInfo() throws SQLException {
        throw new SQLException("not support exception");
    }

    public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
        throw new SQLException("not support exception");
    }

    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
        throw new SQLException("not support exception");
    }

}