All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scriptella.jdbc.JdbcConnection Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2006-2012 The Scriptella Project Team.
 *
 * 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 scriptella.jdbc;

import scriptella.configuration.ConfigurationException;
import scriptella.spi.AbstractConnection;
import scriptella.spi.ConnectionParameters;
import scriptella.spi.DialectIdentifier;
import scriptella.spi.ParametersCallback;
import scriptella.spi.ProviderException;
import scriptella.spi.QueryCallback;
import scriptella.spi.Resource;
import scriptella.util.StringUtils;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Represents a JDBC connection.
 * TODO Extract JDBCConnectionParameters class and JDBCStatementFactory as described in statement cache
 *
 * @author Fyodor Kupolov
 * @version 1.0
 */
public class JdbcConnection extends AbstractConnection {
    public static final String STATEMENT_CACHE_KEY = "statement.cache";
    public static final String STATEMENT_SEPARATOR_KEY = "statement.separator";
    public static final String STATEMENT_SEPARATOR_SINGLELINE_KEY = "statement.separator.singleline";
    public static final String STATEMENT_BATCH_SIZE = "statement.batchSize";
    public static final String STATEMENT_FETCH_SIZE = "statement.fetchSize";
    public static final String KEEPFORMAT_KEY = "keepformat";
    public static final String AUTOCOMMIT_KEY = "autocommit";
    public static final String AUTOCOMMIT_SIZE_KEY = "autocommit.size";
    public static final String FLUSH_BEFORE_QUERY = "flushBeforeQuery";
    public static final String TRANSACTION_ISOLATION_KEY = "transaction.isolation";
    public static final String TRANSACTION_ISOLATION_READ_UNCOMMITTED = "READ_UNCOMMITTED";
    public static final String TRANSACTION_ISOLATION_READ_COMMITTED = "READ_COMMITTED";
    public static final String TRANSACTION_ISOLATION_REPEATABLE_READ = "REPEATABLE_READ";
    public static final String TRANSACTION_ISOLATION_SERIALIZABLE = "SERIALIZABLE";
    private Connection con;
    private static final Logger LOG = Logger.getLogger(JdbcConnection.class.getName());
    private boolean transactable;
    private boolean autocommit;
    private ParametersParser parametersParser;
    protected int statementCacheSize;
    protected int statementBatchSize;
    protected int statementFetchSize;
    protected boolean flushBeforeQuery;
    protected String separator = ";";
    protected boolean separatorSingleLine;
    protected boolean keepformat;
    protected int autocommitSize;

    private Integer txIsolation;
    private final Map resourcesMap = new IdentityHashMap();

    public JdbcConnection(Connection con, ConnectionParameters parameters) {
        super(parameters);
        if (con == null) {
            throw new IllegalArgumentException("Connection cannot be null");
        }
        this.con = con;
        init(parameters);
        if (txIsolation != null) {
            try {
                con.setTransactionIsolation(txIsolation);
            } catch (SQLException e) {
                throw new JdbcException("Unable to set transaction isolation level for " + toString(), e);
            }
        }

        try {
            //Several drivers return -1 which is illegal, but means no TX
            transactable = con.getTransactionIsolation() > Connection.TRANSACTION_NONE;
        } catch (SQLException e) {
            LOG.log(Level.WARNING, "Unable to determine transaction isolation level for connection " + toString(), e);
        }
        if (transactable) { //only effective for transactable connections
            try {
                con.setAutoCommit(autocommit);
            } catch (Exception e) {
                throw new JdbcException("Unable to set autocommit=false for " + toString(), e);
            }
        }
    }

    /**
     * Called in constructor
     *
     * @param parameters connection parameters.
     */
    protected void init(ConnectionParameters parameters) {

        StringBuilder statusMsg = new StringBuilder();
        if (!StringUtils.isAsciiWhitespacesOnly(parameters.getUrl())) {
            statusMsg.append(parameters.getUrl()).append(": ");
        }
        statementCacheSize = parameters.getIntegerProperty(STATEMENT_CACHE_KEY, 64);
        if (statementCacheSize > 0) {
            statusMsg.append("Statement cache is enabled (cache size ").append(statementCacheSize).append("). ");
        }
        statementBatchSize = parameters.getIntegerProperty(STATEMENT_BATCH_SIZE, 0);
        if (statementBatchSize > 0) {
            statusMsg.append("Statement batching is enabled (batch size ").append(statementBatchSize).append("). ");
        }
        statementFetchSize = parameters.getIntegerProperty(STATEMENT_FETCH_SIZE, 0);
        if (statementFetchSize != 0) {
            statusMsg.append("Query statement fetching is enabled (fetch size ").append(statementFetchSize).append("). ");
        }
        String separatorStr = parameters.getStringProperty(STATEMENT_SEPARATOR_KEY);
        if (!StringUtils.isEmpty(separatorStr)) {
            separator = separatorStr.trim();
        }
        statusMsg.append("Statement separator '").append(separator).append('\'');
        separatorSingleLine = parameters.getBooleanProperty(STATEMENT_SEPARATOR_SINGLELINE_KEY, false);
        statusMsg.append(separatorSingleLine ? " on a single line. " : ". ");

        keepformat = parameters.getBooleanProperty(KEEPFORMAT_KEY, false);
        String isolationStr = parameters.getStringProperty(TRANSACTION_ISOLATION_KEY);
        if (isolationStr != null) {
            isolationStr = isolationStr.trim();
            if (TRANSACTION_ISOLATION_READ_COMMITTED.equalsIgnoreCase(isolationStr)) {
                txIsolation = Connection.TRANSACTION_READ_COMMITTED;
            } else if (TRANSACTION_ISOLATION_READ_UNCOMMITTED.equalsIgnoreCase(isolationStr)) {
                txIsolation = Connection.TRANSACTION_READ_UNCOMMITTED;
            } else if (TRANSACTION_ISOLATION_REPEATABLE_READ.equalsIgnoreCase(isolationStr)) {
                txIsolation = Connection.TRANSACTION_REPEATABLE_READ;
            } else if (TRANSACTION_ISOLATION_SERIALIZABLE.equalsIgnoreCase(isolationStr)) {
                txIsolation = Connection.TRANSACTION_SERIALIZABLE;
            } else if (StringUtils.isDecimalInt(isolationStr)) {
                txIsolation = parameters.getIntegerProperty(TRANSACTION_ISOLATION_KEY);
            } else {
                throw new ConfigurationException(
                        "Invalid " + TRANSACTION_ISOLATION_KEY + " connection property value: " + isolationStr +
                                ". Valid values are: " + TRANSACTION_ISOLATION_READ_COMMITTED + ", " +
                                TRANSACTION_ISOLATION_READ_UNCOMMITTED + ", " + TRANSACTION_ISOLATION_REPEATABLE_READ +
                                ", " + TRANSACTION_ISOLATION_SERIALIZABLE +
                                " or a numeric value according to java.sql.Connection transaction isolation constants");
            }

        }
        if (isolationStr != null) {
            statusMsg.append("Transaction isolation level: ").append(txIsolation).
                    append('(').append(isolationStr).append("). ");
        }
        autocommit = parameters.getBooleanProperty(AUTOCOMMIT_KEY);
        autocommitSize = parameters.getIntegerProperty(AUTOCOMMIT_SIZE_KEY, 0);
        statusMsg.append("Autocommit: ").append(autocommit);
        if (autocommitSize > 0) {
            statusMsg.append("(size ").append(autocommitSize).append(")");
        }
        statusMsg.append(".");
        flushBeforeQuery = parameters.getBooleanProperty(FLUSH_BEFORE_QUERY, false);
        if (flushBeforeQuery) {
            statusMsg.append("Flushing before query execution is enabled.");
        }

        LOG.fine(statusMsg.toString());


        parametersParser = new ParametersParser(parameters.getContext());
        initDialectIdentifier();
    }

    StatementCounter getStatementCounter() {
        return counter;
    }

    /**
     * Initializes dialect identifier for connection.
     * If driver doesn't support DatabaseMetaData or other problem occurs,
     * {@link DialectIdentifier#NULL_DIALECT} is used.
     * 

May be overriden by subclasses. */ protected void initDialectIdentifier() { try { final DatabaseMetaData metaData = con.getMetaData(); if (metaData != null) { //Several drivers violate spec and return null setDialectIdentifier(new DialectIdentifier(metaData.getDatabaseProductName(), metaData.getDatabaseProductVersion())); } } catch (Exception e) { setDialectIdentifier(DialectIdentifier.NULL_DIALECT); LOG.log(Level.WARNING, "Failed to obtain meta data for connection. No dialect checking for " + con, e); } } public void executeScript(Resource scriptContent, ParametersCallback parametersCallback) { SqlExecutor s = resourcesMap.get(scriptContent); if (s == null) { resourcesMap.put(scriptContent, s = new SqlExecutor(scriptContent, this)); } s.execute(parametersCallback); } public void executeQuery(Resource queryContent, ParametersCallback parametersCallback, QueryCallback queryCallback) { SqlExecutor q = resourcesMap.get(queryContent); if (q == null) { resourcesMap.put(queryContent, q = new SqlExecutor(queryContent, this)); } if (flushBeforeQuery) { flush(); } q.execute(parametersCallback, queryCallback); if (q.getUpdateCount() < 0) { throw new JdbcException("SQL query cannot make updates"); } } /** * Creates an instance of statement cache. * * @return new instance of statement cache. */ protected StatementCache newStatementCache() { return new StatementCache(getNativeConnection(), statementCacheSize, statementBatchSize, statementFetchSize); } ParametersParser getParametersParser() { return parametersParser; } public void commit() { if (con == null) { throw new IllegalStateException("Attempt to commit a transaction on a closed connection"); } flush(); if (!transactable) { LOG.log(Level.INFO, "Connection " + toString() + " doesn't support transactions. Commit ignored."); } else { try { con.commit(); } catch (Exception e) { throw new JdbcException("Unable to commit transaction", e); } } } public void rollback() { if (con == null) { throw new IllegalStateException("Attempt to roll back a transaction on a closed connection"); } if (!transactable) { LOG.log(Level.INFO, "Connection " + toString() + " doesn't support transactions. Rollback ignored."); } else { try { con.rollback(); } catch (Exception e) { throw new JdbcException("Unable to roll back transaction", e); } } } public void flush() throws ProviderException { //Caches for ETL element executors are flushed if (resourcesMap != null) { for (SqlExecutor executor : resourcesMap.values()) { try { executor.cache.flush(); } catch (SQLException e) { throw new JdbcException("Unable to commit transaction - cannot flush cache", e); } } } } public void close() { if (con != null) { //Closing resources for (SqlExecutor element : resourcesMap.values()) { element.close(); } resourcesMap.clear(); try { con.close(); con = null; } catch (SQLException e) { throw new JdbcException("Unable to close a connection", e); } } } public Connection getNativeConnection() { return con; } public String toString() { return "JdbcConnection{" + (con == null ? "" : con.getClass().getName()) + '}'; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy