org.openstreetmap.osmosis.pgsimple.common.DatabaseContext Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of osmosis-pgsimple Show documentation
Show all versions of osmosis-pgsimple Show documentation
Osmosis is a Java application and library for processing OSM data.
// This software is released into the Public Domain. See copying.txt for details.
package org.openstreetmap.osmosis.pgsimple.common;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
import org.openstreetmap.osmosis.core.database.DatabaseLoginCredentials;
/**
* This class manages the lifecycle of JDBC objects to minimise the risk of
* connection leaks and to support a consistent approach to database access.
*
* @author Brett Henderson
*/
public class DatabaseContext implements AutoCloseable {
private static final Logger LOG = Logger.getLogger(DatabaseContext.class.getName());
static {
// Register the database driver.
try {
Class.forName("org.postgresql.Driver");
} catch (ClassNotFoundException e) {
throw new OsmosisRuntimeException("Unable to find database driver.", e);
}
}
private DatabaseLoginCredentials loginCredentials;
private Connection connection;
private boolean autoCommit;
private Statement statement;
/**
* Creates a new instance.
*
* @param loginCredentials
* Contains all information required to connect to the database.
*/
public DatabaseContext(DatabaseLoginCredentials loginCredentials) {
this.loginCredentials = loginCredentials;
autoCommit = false;
}
private Connection getConnectionFromDriverManager() {
try {
return DriverManager.getConnection(
"jdbc:postgresql://" + loginCredentials.getHost() + "/"
+ loginCredentials.getDatabase(),
// + "?logLevel=2"
loginCredentials.getUser(),
loginCredentials.getPassword()
);
} catch (SQLException e) {
throw new OsmosisRuntimeException("Unable to establish a new database connection.", e);
}
}
private Connection getConnectionFromDatasource() {
InitialContext cxt;
DataSource ds;
String jndiLocation;
jndiLocation = loginCredentials.getDatasourceJndiLocation();
try {
cxt = new InitialContext();
} catch (NamingException e) {
throw new OsmosisRuntimeException("Unable to create an initial JNDI context.", e);
}
try {
ds = (DataSource) cxt.lookup(jndiLocation);
} catch (NamingException e) {
throw new OsmosisRuntimeException("Unable to locate the datasource (" + jndiLocation + ")", e);
}
try {
return ds.getConnection();
} catch (SQLException e) {
throw new OsmosisRuntimeException("Unable to obtain a connection from the datasource.", e);
}
}
/**
* If no database connection is open, a new connection is opened. The
* database connection is then returned.
*
* @return The database connection.
*/
public Connection getConnection() {
if (connection == null) {
String jndiLocation;
jndiLocation = loginCredentials.getDatasourceJndiLocation();
if (jndiLocation != null) {
LOG.finer("Creating a new database connection from JNDI.");
connection = getConnectionFromDatasource();
} else {
LOG.finer("Creating a new database connection using DriverManager.");
connection = getConnectionFromDriverManager();
}
try {
connection.setAutoCommit(autoCommit);
} catch (SQLException e) {
throw new OsmosisRuntimeException("Unable to set auto commit to " + autoCommit + ".", e);
}
}
return connection;
}
/**
* Executes a sql statement against the database.
*
* @param sql
* The sql statement to be invoked.
*/
public void executeStatement(String sql) {
try {
LOG.finest("Executing statement {" + sql + "}");
if (statement != null) {
statement.close();
}
statement = getConnection().createStatement();
statement.execute(sql);
} catch (SQLException e) {
throw new OsmosisRuntimeException("Unable to execute statement.", e);
}
}
/**
* Creates a new database prepared statement.
*
* @param sql
* The statement to be created.
* @return The newly created statement.
*/
public PreparedStatement prepareStatement(String sql) {
try {
PreparedStatement preparedStatement;
LOG.finest("Creating prepared statement {" + sql + "}");
preparedStatement = getConnection().prepareStatement(sql);
return preparedStatement;
} catch (SQLException e) {
throw new OsmosisRuntimeException("Unable to create database prepared statement.", e);
}
}
/**
* Creates a new database callable statement.
*
* @param sql
* The statement to be created.
* @return The newly created statement.
*/
public CallableStatement prepareCall(String sql) {
try {
CallableStatement callableStatement;
LOG.finest("Creating callable statement {" + sql + "}");
callableStatement = getConnection().prepareCall(sql);
return callableStatement;
} catch (SQLException e) {
throw new OsmosisRuntimeException("Unable to create database callable statement.", e);
}
}
/**
* Creates a new database statement.
*
* @return The newly created statement.
*/
public Statement createStatement() {
try {
Statement resultStatement;
LOG.finest("Creating a new statement.");
resultStatement = getConnection().createStatement();
return resultStatement;
} catch (SQLException e) {
throw new OsmosisRuntimeException("Unable to create database statement.", e);
}
}
/**
* Executes a query and returns a result set. The returned result set must
* be closed by the caller.
*
* @param sql
* The query to execute.
* @return The newly created result set.
*/
public ResultSet executeQuery(String sql) {
try {
ResultSet resultSet;
LOG.finest("Executing query {" + sql + "}");
if (statement != null) {
statement.close();
}
statement = getConnection().createStatement(
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY
);
statement.setFetchSize(10000);
resultSet = statement.executeQuery(sql);
return resultSet;
} catch (SQLException e) {
throw new OsmosisRuntimeException("Unable to create resultset.", e);
}
}
/**
* Indicates if the specified column exists in the database.
*
* @param tableName The table to check for.
* @param columnName The column to check for.
* @return True if the column exists, false otherwise.
*/
public boolean doesColumnExist(String tableName, String columnName) {
LOG.finest("Checking if column {" + columnName + "} in table {" + tableName + "} exists.");
try (ResultSet resultSet = getConnection().getMetaData().getColumns(null, null, tableName, columnName)) {
return resultSet.next();
} catch (SQLException e) {
throw new OsmosisRuntimeException(
"Unable to check for the existence of column " + tableName + "." + columnName + ".", e
);
}
}
/**
* Indicates if the specified table exists in the database.
*
* @param tableName The table to check for.
* @return True if the table exists, false otherwise.
*/
public boolean doesTableExist(String tableName) {
LOG.finest("Checking if table {" + tableName + "} exists.");
try (ResultSet resultSet =
getConnection().getMetaData().getTables(null, null, tableName, new String[]{"TABLE"})) {
return resultSet.next();
} catch (SQLException e) {
throw new OsmosisRuntimeException(
"Unable to check for the existence of table " + tableName + ".",
e
);
}
}
/**
* Sets the auto-commit property on the underlying connection.
*
* @param autoCommit
* The new auto commit value.
*/
public void setAutoCommit(boolean autoCommit) {
if (connection != null) {
try {
LOG.finest("Setting auto commit to " + autoCommit + ".");
connection.setAutoCommit(autoCommit);
} catch (SQLException e) {
throw new OsmosisRuntimeException("Unable to commit changes.", e);
}
}
this.autoCommit = autoCommit;
}
/**
* Commits any outstanding transaction.
*/
public void commit() {
if (connection != null) {
try {
LOG.finest("Committing changes.");
connection.commit();
} catch (SQLException e) {
throw new OsmosisRuntimeException("Unable to commit changes.", e);
}
}
}
/**
* Releases all database resources. This method is guaranteed not to throw transactions
* and should always be called in a finally or try-with-resources block whenever this
* class is used.
*/
public void close() {
if (connection != null) {
try {
LOG.finest("Closing the database connection.");
connection.close();
} catch (SQLException e) {
// We cannot throw an exception within a release statement.
LOG.log(Level.WARNING, "Unable to close result set.", e);
}
connection = null;
}
}
/**
* Enforces cleanup of any remaining resources during garbage collection.
* This is a safeguard and should not be required if release is called
* appropriately.
*
* @throws Throwable
* if an unexpected problem occurs during finalization.
*/
@Override
protected void finalize() throws Throwable {
close();
super.finalize();
}
}