franta-hg@26: /** franta-hg@26: * SQL-DK franta-hg@26: * Copyright © 2013 František Kučera (frantovo.cz) franta-hg@26: * franta-hg@26: * This program is free software: you can redistribute it and/or modify franta-hg@26: * it under the terms of the GNU General Public License as published by franta-hg@26: * the Free Software Foundation, either version 3 of the License, or franta-hg@26: * (at your option) any later version. franta-hg@26: * franta-hg@26: * This program is distributed in the hope that it will be useful, franta-hg@26: * but WITHOUT ANY WARRANTY; without even the implied warranty of franta-hg@26: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the franta-hg@26: * GNU General Public License for more details. franta-hg@26: * franta-hg@26: * You should have received a copy of the GNU General Public License franta-hg@26: * along with this program. If not, see . franta-hg@26: */ franta-hg@27: package info.globalcode.sql.dk; franta-hg@27: franta-hg@179: import static info.globalcode.sql.dk.jmx.ConnectionManagement.incrementCounter; franta-hg@179: import static info.globalcode.sql.dk.jmx.ConnectionManagement.resetCounter; franta-hg@29: import info.globalcode.sql.dk.batch.Batch; franta-hg@146: import info.globalcode.sql.dk.batch.BatchException; franta-hg@27: import info.globalcode.sql.dk.configuration.DatabaseDefinition; franta-hg@192: import info.globalcode.sql.dk.configuration.Loader; franta-hg@106: import info.globalcode.sql.dk.configuration.Properties; franta-hg@34: import info.globalcode.sql.dk.formatting.ColumnsHeader; franta-hg@29: import info.globalcode.sql.dk.formatting.Formatter; franta-hg@179: import info.globalcode.sql.dk.jmx.ConnectionManagement; franta-hg@179: import info.globalcode.sql.dk.jmx.ConnectionManagement.COUNTER; franta-hg@28: import java.sql.Connection; franta-hg@29: import java.sql.PreparedStatement; franta-hg@29: import java.sql.ResultSet; franta-hg@28: import java.sql.SQLException; franta-hg@86: import java.sql.SQLWarning; franta-hg@55: import java.util.logging.Level; franta-hg@55: import java.util.logging.Logger; franta-hg@26: franta-hg@26: /** franta-hg@155: * Represents connected database. Is derived from {@linkplain DatabaseDefinition}. franta-hg@155: * Wraps {@linkplain Connection}. franta-hg@155: * franta-hg@155: * Is responsible for executing {@linkplain SQLCommand} and passing results to the franta-hg@155: * {@linkplain Formatter}. franta-hg@26: * franta-hg@26: * @author Ing. František Kučera (frantovo.cz) franta-hg@26: */ franta-hg@42: public class DatabaseConnection implements AutoCloseable { franta-hg@26: franta-hg@55: private static final Logger log = Logger.getLogger(DatabaseConnection.class.getName()); franta-hg@192: public static final String JDBC_PROPERTY_USER = "user"; franta-hg@108: public static final String JDBC_PROPERTY_PASSWORD = "password"; franta-hg@178: private final DatabaseDefinition databaseDefinition; franta-hg@178: private final Connection connection; franta-hg@178: private final Properties properties; franta-hg@179: /** franta-hg@179: * Could be null = JMX is disabled → must check, see functions in franta-hg@179: * {@linkplain ConnectionManagement} franta-hg@179: */ franta-hg@179: private final ConnectionManagement connectionMBean; franta-hg@26: franta-hg@179: /** franta-hg@179: * franta-hg@179: * @param databaseDefinition DB url, name, password etc. franta-hg@179: * @param properties additional properties from CLI franta-hg@179: * @param connectionMBean JMX management bean | null = disabled JMX reporting franta-hg@179: * @throws SQLException franta-hg@179: */ franta-hg@179: public DatabaseConnection(DatabaseDefinition databaseDefinition, Properties properties, ConnectionManagement connectionMBean) throws SQLException { franta-hg@26: this.databaseDefinition = databaseDefinition; franta-hg@106: this.properties = properties; franta-hg@179: this.connectionMBean = connectionMBean; franta-hg@192: this.connection = Loader.jdbcConnect(databaseDefinition, properties); franta-hg@26: } franta-hg@29: franta-hg@29: public void executeQuery(SQLCommand sqlCommand, Formatter formatter) throws SQLException { franta-hg@91: formatter.writeStartBatch(); franta-hg@29: formatter.writeStartDatabase(databaseDefinition); franta-hg@142: formatter.writeStartStatement(); franta-hg@142: formatter.writeQuery(sqlCommand.getQuery()); franta-hg@142: formatter.writeParameters(sqlCommand.getParameters()); franta-hg@29: processCommand(sqlCommand, formatter); franta-hg@142: formatter.writeEndStatement(); franta-hg@29: formatter.writeEndDatabase(); franta-hg@91: formatter.writeEndBatch(); franta-hg@29: } franta-hg@29: franta-hg@146: public void executeBatch(Batch batch, Formatter formatter) throws SQLException, BatchException { franta-hg@91: formatter.writeStartBatch(); franta-hg@29: formatter.writeStartDatabase(databaseDefinition); franta-hg@29: while (batch.hasNext()) { franta-hg@142: SQLCommand sqlCommand = batch.next(); franta-hg@142: formatter.writeStartStatement(); franta-hg@142: formatter.writeQuery(sqlCommand.getQuery()); franta-hg@142: formatter.writeParameters(sqlCommand.getParameters()); franta-hg@142: processCommand(sqlCommand, formatter); franta-hg@142: formatter.writeEndStatement(); franta-hg@29: } franta-hg@29: formatter.writeEndDatabase(); franta-hg@91: formatter.writeEndBatch(); franta-hg@29: } franta-hg@29: franta-hg@29: private void processCommand(SQLCommand sqlCommand, Formatter formatter) throws SQLException { franta-hg@179: incrementCounter(connectionMBean, COUNTER.COMMAND); franta-hg@179: resetCounter(connectionMBean, COUNTER.RECORD_CURRENT); franta-hg@192: franta-hg@29: try (PreparedStatement ps = sqlCommand.prepareStatement(connection)) { franta-hg@55: log.log(Level.FINE, "Statement prepared"); franta-hg@29: sqlCommand.parametrize(ps); franta-hg@35: franta-hg@35: boolean isRS = ps.execute(); franta-hg@55: log.log(Level.FINE, "Statement executed"); franta-hg@35: if (isRS) { franta-hg@35: try (ResultSet rs = ps.getResultSet()) { franta-hg@142: processResultSet(rs, formatter); franta-hg@35: } franta-hg@35: } else { franta-hg@142: processUpdateResult(ps, formatter); franta-hg@35: } franta-hg@86: logWarnings(ps); franta-hg@35: franta-hg@35: while (ps.getMoreResults() || ps.getUpdateCount() > -1) { franta-hg@37: ResultSet rs = ps.getResultSet(); franta-hg@37: if (rs == null) { franta-hg@142: processUpdateResult(ps, formatter); franta-hg@37: } else { franta-hg@142: processResultSet(rs, formatter); franta-hg@37: rs.close(); franta-hg@37: } franta-hg@86: logWarnings(ps); franta-hg@29: } franta-hg@29: } franta-hg@37: } franta-hg@37: franta-hg@142: private void processUpdateResult(PreparedStatement ps, Formatter formatter) throws SQLException { franta-hg@142: formatter.writeUpdatesResult(ps.getUpdateCount()); franta-hg@37: } franta-hg@37: franta-hg@142: private void processResultSet(ResultSet rs, Formatter formatter) throws SQLException { franta-hg@142: formatter.writeStartResultSet(new ColumnsHeader(rs.getMetaData())); franta-hg@37: franta-hg@34: int columnCount = rs.getMetaData().getColumnCount(); franta-hg@34: franta-hg@29: while (rs.next()) { franta-hg@179: incrementCounter(connectionMBean, COUNTER.RECORD_CURRENT); franta-hg@179: incrementCounter(connectionMBean, COUNTER.RECORD_TOTAL); franta-hg@192: franta-hg@29: formatter.writeStartRow(); franta-hg@29: franta-hg@34: for (int i = 1; i <= columnCount; i++) { franta-hg@34: formatter.writeColumnValue(rs.getObject(i)); franta-hg@34: } franta-hg@34: franta-hg@29: formatter.writeEndRow(); franta-hg@29: } franta-hg@34: franta-hg@142: formatter.writeEndResultSet(); franta-hg@29: } franta-hg@42: franta-hg@86: private void logWarnings(PreparedStatement ps) throws SQLException { franta-hg@86: SQLWarning w = ps.getWarnings(); franta-hg@86: while (w != null) { franta-hg@86: log.log(Level.WARNING, "SQL: {0}", w.getLocalizedMessage()); franta-hg@86: w = w.getNextWarning(); franta-hg@86: } franta-hg@86: ps.clearWarnings(); franta-hg@86: } franta-hg@86: franta-hg@65: /** franta-hg@65: * Tests if this connection is live. franta-hg@65: * franta-hg@65: * @return true if test was successful franta-hg@65: * @throws SQLException if test fails franta-hg@65: */ franta-hg@65: public boolean test() throws SQLException { franta-hg@65: connection.getAutoCommit(); franta-hg@65: return true; franta-hg@65: } franta-hg@65: franta-hg@229: public String getProductName() throws SQLException { franta-hg@229: return connection.getMetaData().getDatabaseProductName(); franta-hg@229: } franta-hg@229: franta-hg@229: public String getProductVersion() throws SQLException { franta-hg@229: return connection.getMetaData().getDatabaseProductVersion(); franta-hg@229: } franta-hg@229: franta-hg@42: @Override franta-hg@42: public void close() throws SQLException { franta-hg@42: connection.close(); franta-hg@42: } franta-hg@26: }