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@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@106: import info.globalcode.sql.dk.configuration.Properties; franta-hg@107: import info.globalcode.sql.dk.configuration.Property; franta-hg@34: import info.globalcode.sql.dk.formatting.ColumnsHeader; franta-hg@29: import info.globalcode.sql.dk.formatting.Formatter; franta-hg@28: import java.sql.Connection; franta-hg@28: import java.sql.DriverManager; 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@108: private static final String JDBC_PROPERTY_USER = "user"; franta-hg@108: public static final String JDBC_PROPERTY_PASSWORD = "password"; franta-hg@26: private DatabaseDefinition databaseDefinition; franta-hg@28: private Connection connection; franta-hg@106: private Properties properties; franta-hg@26: franta-hg@106: public DatabaseConnection(DatabaseDefinition databaseDefinition, Properties properties) throws SQLException { franta-hg@26: this.databaseDefinition = databaseDefinition; franta-hg@106: this.properties = properties; franta-hg@28: franta-hg@108: if (properties.hasProperty(JDBC_PROPERTY_PASSWORD)) { franta-hg@108: log.log(Level.WARNING, "Passing DB password as CLI parameter is insecure!"); franta-hg@108: } franta-hg@108: franta-hg@107: Properties credentials = new Properties(); franta-hg@108: credentials.add(new Property(JDBC_PROPERTY_USER, databaseDefinition.getUserName())); franta-hg@108: credentials.add(new Property(JDBC_PROPERTY_PASSWORD, databaseDefinition.getPassword())); franta-hg@107: credentials.setDefaults(databaseDefinition.getProperties()); franta-hg@107: properties.setDefaults(credentials); franta-hg@106: java.util.Properties javaProperties = properties.getJavaProperties(); franta-hg@106: franta-hg@106: connection = DriverManager.getConnection(databaseDefinition.getUrl(), javaProperties); 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@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@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@42: @Override franta-hg@42: public void close() throws SQLException { franta-hg@42: connection.close(); franta-hg@42: } franta-hg@26: }