/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.jena.sdb.sql; import static java.sql.ResultSet.CONCUR_READ_ONLY; import static java.sql.ResultSet.TYPE_FORWARD_ONLY; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import java.util.List; import java.util.function.Supplier ; import javax.sql.DataSource; import org.apache.jena.graph.TransactionHandler ; import org.apache.jena.sdb.core.Generator ; import org.apache.jena.sdb.core.Gensym ; import org.apache.jena.sdb.core.SDBConstants ; import org.apache.jena.sdb.graph.TransactionHandlerSDB ; import org.apache.jena.shared.Command ; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /* * An SDBConnection is the abstraction of the link between client * application and the database. * There can be many Store's per connection. */ public class SDBConnection { static private Logger log = LoggerFactory.getLogger(SDBConnection.class) ; static private Generator gen = Gensym.create("connection-") ; private Connection sqlConnection = null ; TransactionHandler transactionHandler = null ; String label = gen.next() ; String jdbcURL = "unset" ; // Defaults public static boolean logSQLExceptions = true ; public static boolean logSQLStatements = false ; public static boolean logSQLQueries = false ; private boolean thisLogSQLExceptions = logSQLExceptions ; private boolean thisLogSQLStatements = logSQLStatements ; private boolean thisLogSQLQueries = logSQLQueries ; public SDBConnection(DataSource ds) throws SQLException { this(ds.getConnection()) ; } public SDBConnection(String url, String user, String password) { this(SDBConnectionFactory.createSqlConnection(url, user, password)) ; setLabel(url) ; setJdbcURL(url) ; } public SDBConnection(Connection jdbcConnection) { this(jdbcConnection, null) ; } public SDBConnection(Connection jdbcConnection, String url) { sqlConnection = jdbcConnection ; transactionHandler = new TransactionHandlerSDB(this) ; if ( url != null ) setJdbcURL(url) ; } public static SDBConnection none() { return new SDBConnection(JDBC.jdbcNone, null, null) ; } public boolean hasSQLConnection() { return sqlConnection != null ; } public TransactionHandler getTransactionHandler() { return transactionHandler ; } public ResultSetJDBC execQuery(String sqlString) throws SQLException { return execQuery(sqlString, SDBConstants.jdbcFetchSizeOff) ; } public ResultSetJDBC execQuery(String sqlString, int fetchSize) throws SQLException { if ( loggingSQLStatements() || loggingSQLQueries() ) writeLog("execQuery", sqlString) ; Connection conn = getSqlConnection() ; try { //Statement s = conn.createStatement() ; // Managed by ResultSetJDBC // These are needed for MySQL when trying for row-by-row fetching // and they are gemnerally true so set them always. Statement s = conn.createStatement(TYPE_FORWARD_ONLY, CONCUR_READ_ONLY) ; // Managed by ResultSetJDBC // MySQL : Integer.MIN_VALUE if ( fetchSize != SDBConstants.jdbcFetchSizeOff ) { /* MySQL: streaming if: * stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE); */ s.setFetchSize(fetchSize) ; } return new ResultSetJDBC(s, s.executeQuery(sqlString)) ; } catch (SQLException ex) { exception("execQuery", ex, sqlString) ; throw ex ; } catch (RuntimeException ex) { throw ex ; } } @Deprecated public Object executeInTransaction(Command c) { return getTransactionHandler().executeInTransaction(c) ; } public void execute(Runnable action) { getTransactionHandler().execute(action) ; } public <T> T calculate(Supplier<T> action) { return getTransactionHandler().calculate(action) ; } public Object executeSQL(final SQLCommand c) { try { return c.execute(getSqlConnection()) ; } catch (SQLException ex) { exception("SQL", ex) ; throw new SDBExceptionSQL(ex) ; } } public int execUpdate(String sqlString) throws SQLException { if ( loggingSQLStatements() ) writeLog("execUpdate", sqlString) ; Connection conn = getSqlConnection() ; try ( Statement s = conn.createStatement() ) { return s.executeUpdate(sqlString) ; } catch (SQLException ex) { exception("execUpdate", ex, sqlString) ; throw ex ; } } /** Execute a statement, return the result set if there was one, else null */ public ResultSetJDBC exec(String sqlString) throws SQLException { if ( loggingSQLStatements() ) writeLog("exec", sqlString) ; Connection conn = getSqlConnection() ; Statement s = null ; try { s = conn.createStatement() ; boolean r = s.execute(sqlString) ; if ( r ) return new ResultSetJDBC(s, s.getResultSet()) ; RS.close(s) ; return null ; } // catch (SQLSyntaxErrorException ex) // Java 6 // { // exception("exec", ex, sqlString) ; // throw ex ; // } catch (SQLException ex) { RS.close(s) ; exception("exec", ex, sqlString) ; throw ex ; } } /** Execute a statement, return the result set if there was one, else null. */ public ResultSetJDBC execSilent(String sqlString) { if ( loggingSQLStatements() ) writeLog("execSilent", sqlString) ; Connection conn = getSqlConnection() ; Statement s = null ; try { s = conn.createStatement() ; boolean r = s.execute(sqlString) ; if ( r ) return new ResultSetJDBC(s, s.getResultSet()) ; } catch (SQLException ex) {} // Close if did not return a ResultSetJDBC RS.close(s) ; return null ; } /** Prepare a statement **/ public PreparedStatement prepareStatement(String sqlString) throws SQLException { if ( loggingSQLStatements() ) writeLog("prepareStatement", sqlString) ; Connection conn = getSqlConnection() ; try { PreparedStatement ps = conn.prepareStatement(sqlString); return ps; } catch (SQLException ex) { exception("prepareStatement", ex, sqlString) ; throw ex; } } /** Close a prepared statement **/ public void closePreparedStatement(PreparedStatement ps) throws SQLException { if ( loggingSQLStatements() ) writeLog("closePrepareStatement", ps.toString()) ; if ( ps == null ) return ; try { ps.close() ; } catch (SQLException ex) { exception("closePrepareStatement", ex, ps.toString()) ; throw ex; } } /** Get the names of the application tables */ public List<String> getTableNames() { return TableUtils.getTableNames(getSqlConnection()) ; } // public ResultSet metaData(String sqlString) throws SQLException // { // try { // Connection conn = getSqlConnection() ; // DatabaseMetaData dbmd = conn.getMetaData() ; // ResultSet rsMD = dbmd.getTables(null, null, null, null) ; // return rsMD ; // } catch (SQLException e) // { // exception("metaData", e) ; // throw e ; // } // } public Connection getSqlConnection() { // Potential pool point. return sqlConnection ; } public void close() { Connection connection = getSqlConnection() ; try { if ( connection != null && ! connection.isClosed() ) connection.close() ; } catch (SQLException ex){ log.warn("Problems closing SQL connection", ex) ; } } @Override public String toString() { return getLabel() ; } public boolean loggingSQLExceptions() { return thisLogSQLExceptions ; } public void setLogSQLExceptions(boolean thisLogSQLExceptions) { this.thisLogSQLExceptions = thisLogSQLExceptions ; } public boolean loggingSQLQueries() { return thisLogSQLQueries ; } public void setLogSQLQueries(boolean thisLogSQLQueries) { this.thisLogSQLQueries = thisLogSQLQueries ; } public boolean loggingSQLStatements() { return thisLogSQLStatements ; } public void setLogSQLStatements(boolean thisLogSQLStatements) { this.thisLogSQLStatements = thisLogSQLStatements ; } public String getLabel() { return label ; } public void setLabel(String label) { this.label = label ; } public String getJdbcURL() { return jdbcURL ; } public void setJdbcURL(String jdbcURL) { this.jdbcURL = jdbcURL ; } static Logger sqlLog = log ; // LoggerFactory.getLogger("SQL") ; // Remember to turn on in log4j.properties. private void exception(String who, SQLException ex, String sqlString) { if ( this.loggingSQLExceptions() ) sqlLog.warn(who+": SQLException\n"+ex.getMessage()+"\n"+sqlString+"\n") ; } private void exception(String who, SQLException ex) { if ( this.loggingSQLExceptions() ) sqlLog.warn(who+": SQLException\n"+ex.getMessage()) ; } private void writeLog(String who, String sqlString) { if ( sqlLog.isInfoEnabled() ) sqlLog.info(who+"\n\n"+sqlString+"\n") ; } }