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