1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/java/sql-dk/src/main/java/info/globalcode/sql/dk/DatabaseConnection.java Mon Mar 04 20:15:24 2019 +0100
1.3 @@ -0,0 +1,192 @@
1.4 +/**
1.5 + * SQL-DK
1.6 + * Copyright © 2013 František Kučera (frantovo.cz)
1.7 + *
1.8 + * This program is free software: you can redistribute it and/or modify
1.9 + * it under the terms of the GNU General Public License as published by
1.10 + * the Free Software Foundation, either version 3 of the License, or
1.11 + * (at your option) any later version.
1.12 + *
1.13 + * This program is distributed in the hope that it will be useful,
1.14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.16 + * GNU General Public License for more details.
1.17 + *
1.18 + * You should have received a copy of the GNU General Public License
1.19 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
1.20 + */
1.21 +package info.globalcode.sql.dk;
1.22 +
1.23 +import static info.globalcode.sql.dk.jmx.ConnectionManagement.incrementCounter;
1.24 +import static info.globalcode.sql.dk.jmx.ConnectionManagement.resetCounter;
1.25 +import info.globalcode.sql.dk.batch.Batch;
1.26 +import info.globalcode.sql.dk.batch.BatchException;
1.27 +import info.globalcode.sql.dk.configuration.DatabaseDefinition;
1.28 +import info.globalcode.sql.dk.configuration.Loader;
1.29 +import info.globalcode.sql.dk.configuration.Properties;
1.30 +import info.globalcode.sql.dk.formatting.ColumnsHeader;
1.31 +import info.globalcode.sql.dk.formatting.Formatter;
1.32 +import info.globalcode.sql.dk.jmx.ConnectionManagement;
1.33 +import info.globalcode.sql.dk.jmx.ConnectionManagement.COUNTER;
1.34 +import java.sql.Connection;
1.35 +import java.sql.PreparedStatement;
1.36 +import java.sql.ResultSet;
1.37 +import java.sql.SQLException;
1.38 +import java.sql.SQLWarning;
1.39 +import java.util.logging.Level;
1.40 +import java.util.logging.Logger;
1.41 +
1.42 +/**
1.43 + * Represents connected database. Is derived from {@linkplain DatabaseDefinition}.
1.44 + * Wraps {@linkplain Connection}.
1.45 + *
1.46 + * Is responsible for executing {@linkplain SQLCommand} and passing results to the
1.47 + * {@linkplain Formatter}.
1.48 + *
1.49 + * @author Ing. František Kučera (frantovo.cz)
1.50 + */
1.51 +public class DatabaseConnection implements AutoCloseable {
1.52 +
1.53 + private static final Logger log = Logger.getLogger(DatabaseConnection.class.getName());
1.54 + public static final String JDBC_PROPERTY_USER = "user";
1.55 + public static final String JDBC_PROPERTY_PASSWORD = "password";
1.56 + private final DatabaseDefinition databaseDefinition;
1.57 + private final Connection connection;
1.58 + private final Properties properties;
1.59 + /**
1.60 + * Could be null = JMX is disabled → must check, see functions in
1.61 + * {@linkplain ConnectionManagement}
1.62 + */
1.63 + private final ConnectionManagement connectionMBean;
1.64 +
1.65 + /**
1.66 + *
1.67 + * @param databaseDefinition DB url, name, password etc.
1.68 + * @param properties additional properties from CLI
1.69 + * @param connectionMBean JMX management bean | null = disabled JMX reporting
1.70 + * @throws SQLException
1.71 + */
1.72 + public DatabaseConnection(DatabaseDefinition databaseDefinition, Properties properties, ConnectionManagement connectionMBean) throws SQLException {
1.73 + this.databaseDefinition = databaseDefinition;
1.74 + this.properties = properties;
1.75 + this.connectionMBean = connectionMBean;
1.76 + this.connection = Loader.jdbcConnect(databaseDefinition, properties);
1.77 + }
1.78 +
1.79 + public void executeQuery(SQLCommand sqlCommand, Formatter formatter) throws SQLException {
1.80 + formatter.writeStartBatch();
1.81 + formatter.writeStartDatabase(databaseDefinition);
1.82 + formatter.writeStartStatement();
1.83 + formatter.writeQuery(sqlCommand.getQuery());
1.84 + formatter.writeParameters(sqlCommand.getParameters());
1.85 + processCommand(sqlCommand, formatter);
1.86 + formatter.writeEndStatement();
1.87 + formatter.writeEndDatabase();
1.88 + formatter.writeEndBatch();
1.89 + }
1.90 +
1.91 + public void executeBatch(Batch batch, Formatter formatter) throws SQLException, BatchException {
1.92 + formatter.writeStartBatch();
1.93 + formatter.writeStartDatabase(databaseDefinition);
1.94 + while (batch.hasNext()) {
1.95 + SQLCommand sqlCommand = batch.next();
1.96 + formatter.writeStartStatement();
1.97 + formatter.writeQuery(sqlCommand.getQuery());
1.98 + formatter.writeParameters(sqlCommand.getParameters());
1.99 + processCommand(sqlCommand, formatter);
1.100 + formatter.writeEndStatement();
1.101 + }
1.102 + formatter.writeEndDatabase();
1.103 + formatter.writeEndBatch();
1.104 + }
1.105 +
1.106 + private void processCommand(SQLCommand sqlCommand, Formatter formatter) throws SQLException {
1.107 + incrementCounter(connectionMBean, COUNTER.COMMAND);
1.108 + resetCounter(connectionMBean, COUNTER.RECORD_CURRENT);
1.109 +
1.110 + try (PreparedStatement ps = sqlCommand.prepareStatement(connection)) {
1.111 + log.log(Level.FINE, "Statement prepared");
1.112 + sqlCommand.parametrize(ps);
1.113 +
1.114 + boolean isRS = ps.execute();
1.115 + log.log(Level.FINE, "Statement executed");
1.116 + if (isRS) {
1.117 + try (ResultSet rs = ps.getResultSet()) {
1.118 + processResultSet(rs, formatter);
1.119 + }
1.120 + } else {
1.121 + processUpdateResult(ps, formatter);
1.122 + }
1.123 + logWarnings(ps);
1.124 +
1.125 + while (ps.getMoreResults() || ps.getUpdateCount() > -1) {
1.126 + ResultSet rs = ps.getResultSet();
1.127 + if (rs == null) {
1.128 + processUpdateResult(ps, formatter);
1.129 + } else {
1.130 + processResultSet(rs, formatter);
1.131 + rs.close();
1.132 + }
1.133 + logWarnings(ps);
1.134 + }
1.135 + }
1.136 + }
1.137 +
1.138 + private void processUpdateResult(PreparedStatement ps, Formatter formatter) throws SQLException {
1.139 + formatter.writeUpdatesResult(ps.getUpdateCount());
1.140 + }
1.141 +
1.142 + private void processResultSet(ResultSet rs, Formatter formatter) throws SQLException {
1.143 + formatter.writeStartResultSet(new ColumnsHeader(rs.getMetaData()));
1.144 +
1.145 + int columnCount = rs.getMetaData().getColumnCount();
1.146 +
1.147 + while (rs.next()) {
1.148 + incrementCounter(connectionMBean, COUNTER.RECORD_CURRENT);
1.149 + incrementCounter(connectionMBean, COUNTER.RECORD_TOTAL);
1.150 +
1.151 + formatter.writeStartRow();
1.152 +
1.153 + for (int i = 1; i <= columnCount; i++) {
1.154 + formatter.writeColumnValue(rs.getObject(i));
1.155 + }
1.156 +
1.157 + formatter.writeEndRow();
1.158 + }
1.159 +
1.160 + formatter.writeEndResultSet();
1.161 + }
1.162 +
1.163 + private void logWarnings(PreparedStatement ps) throws SQLException {
1.164 + SQLWarning w = ps.getWarnings();
1.165 + while (w != null) {
1.166 + log.log(Level.WARNING, "SQL: {0}", w.getLocalizedMessage());
1.167 + w = w.getNextWarning();
1.168 + }
1.169 + ps.clearWarnings();
1.170 + }
1.171 +
1.172 + /**
1.173 + * Tests if this connection is live.
1.174 + *
1.175 + * @return true if test was successful
1.176 + * @throws SQLException if test fails
1.177 + */
1.178 + public boolean test() throws SQLException {
1.179 + connection.getAutoCommit();
1.180 + return true;
1.181 + }
1.182 +
1.183 + public String getProductName() throws SQLException {
1.184 + return connection.getMetaData().getDatabaseProductName();
1.185 + }
1.186 +
1.187 + public String getProductVersion() throws SQLException {
1.188 + return connection.getMetaData().getDatabaseProductVersion();
1.189 + }
1.190 +
1.191 + @Override
1.192 + public void close() throws SQLException {
1.193 + connection.close();
1.194 + }
1.195 +}