All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nz.co.gregs.dbvolution.databases.DBDatabase Maven / Gradle / Ivy

/*
 * Copyright 2013 Gregory Graham.
 *
 * Licensed 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 nz.co.gregs.dbvolution.databases;

import nz.co.gregs.dbvolution.exceptions.UnableToDropDatabaseException;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.PrintStream;
import java.io.Serializable;
import java.sql.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sql.DataSource;
import nz.co.gregs.dbvolution.DBMigration;
import nz.co.gregs.dbvolution.DBQuery;
import nz.co.gregs.dbvolution.DBQueryInsert;
import nz.co.gregs.dbvolution.DBQueryRow;
import nz.co.gregs.dbvolution.DBReport;
import nz.co.gregs.dbvolution.DBRow;
import nz.co.gregs.dbvolution.DBScript;
import nz.co.gregs.dbvolution.DBTable;
import nz.co.gregs.dbvolution.actions.DBAction;
import nz.co.gregs.dbvolution.actions.DBActionList;
import nz.co.gregs.dbvolution.actions.DBQueryable;
import nz.co.gregs.dbvolution.databases.definitions.DBDefinition;
import nz.co.gregs.dbvolution.datatypes.DBLargeObject;
import nz.co.gregs.dbvolution.datatypes.QueryableDatatype;
import nz.co.gregs.dbvolution.exceptions.*;
import nz.co.gregs.dbvolution.transactions.*;
import nz.co.gregs.dbvolution.internal.properties.PropertyWrapper;
import nz.co.gregs.dbvolution.reflection.DataModel;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * DBDatabase is the repository of all knowledge about your database.
 *
 * 

* All DBvolution projects need a DBDatabase object to provide the database * connection, login details, and to generate the correct syntax for the * database. * *

* It also provides quick methods to get and print database values and perform * transactions. * *

* There should be a subclass for your database already in * {@code nz.co.gregs.dbvolution.databases} * *

* Very few programmers will need to construct an actual DBDatabase as the * subclasses provide most of the required details for connecting to databases. * *

Support DBvolution at * Patreon

* * @author Gregory Graham */ public abstract class DBDatabase implements Serializable, Cloneable { private static final long serialVersionUID = 1l; static final Log LOG = LogFactory.getLog(DBDatabase.class); private String driverName = ""; private String jdbcURL = ""; private String username = ""; private String password = null; private DataSource dataSource = null; private boolean printSQLBeforeExecuting = false; boolean isInATransaction = false; DBTransactionStatement transactionStatement; private DBDefinition definition = null; private String databaseName; private boolean batchIfPossible = true; private boolean preventAccidentalDroppingOfTables = true; private boolean preventAccidentalDroppingDatabase = true; private final Object getStatementSynchronizeObject = new Object(); private final Object getConnectionSynchronizeObject = new Object(); Connection transactionConnection; private static final transient Map> BUSY_CONNECTION = new HashMap<>(); private static final transient HashMap> FREE_CONNECTIONS = new HashMap<>(); private Boolean needToAddDatabaseSpecificFeatures = true; boolean explicitCommitActionRequired = false; @Override public String toString() { if (jdbcURL != null && !jdbcURL.isEmpty()) { return this.getClass().getSimpleName() + "{" + (databaseName == null ? "UNNAMED" : databaseName + "=") + jdbcURL + ":" + username + "}"; } else if (dataSource != null) { return this.getClass().getSimpleName() + ": " + dataSource.toString(); } else { return super.toString(); } } /** * Clones the DBDatabase. * *

Support DBvolution at * Patreon

* * @return a clone of the DBDatabase. * @throws CloneNotSupportedException not likely */ @Override public DBDatabase clone() throws CloneNotSupportedException { Object clone = super.clone(); DBDatabase newInstance = (DBDatabase) clone; return newInstance; } @Override public synchronized int hashCode() { int hash = 7; hash = 29 * hash + (this.driverName != null ? this.driverName.hashCode() : 0); hash = 29 * hash + (this.jdbcURL != null ? this.jdbcURL.hashCode() : 0); hash = 29 * hash + (this.username != null ? this.username.hashCode() : 0); hash = 29 * hash + (this.password != null ? this.password.hashCode() : 0); hash = 29 * hash + (this.dataSource != null ? this.dataSource.hashCode() : 0); return hash; } @Override public synchronized boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final DBDatabase other = (DBDatabase) obj; if ((this.getDriverName() == null) ? (other.getDriverName() != null) : !this.getDriverName().equals(other.getDriverName())) { return false; } if ((this.getJdbcURL() == null) ? (other.getJdbcURL() != null) : !this.getJdbcURL().equals(other.getJdbcURL())) { return false; } if ((this.getUsername() == null) ? (other.getUsername() != null) : !this.getUsername().equals(other.getUsername())) { return false; } if ((this.getPassword() == null) ? (other.getPassword() != null) : !this.getPassword().equals(other.getPassword())) { return false; } return !(this.dataSource != other.dataSource && (this.dataSource == null || !this.dataSource.equals(other.dataSource))); } /** * * Provides a convenient constructor for DBDatabases that have configuration * details hardwired or are able to automatically retrieve the details. * *

* This constructor creates an empty DBDatabase with only the default * settings, in particular with no driver, URL, username, password, or * {@link DBDefinition} * *

* Most programmers should not call this constructor directly. Check the * subclasses in {@code nz.co.gregs.dbvolution.databases} for your particular * database. * *

* DBDatabase encapsulates the knowledge of the database, in particular the * syntax of the database in the DBDefinition and the connection details from * a DataSource. * * @see DBDefinition * @see Oracle12DB * @see Oracle11XEDB * @see OracleAWS11DB * @see MySQLDB * @see MSSQLServerDB * @see H2DB * @see H2MemoryDB * @see InformixDB * @see MariaDB * @see MariaClusterDB * @see NuoDB */ protected DBDatabase() { SLEEP_BETWEEN_CONNECTION_RETRIES_MILLIS = 10; MAX_CONNECTION_RETRIES = 6; } /** * Define a new DBDatabase. * *

* Most programmers should not call this constructor directly. Check the * subclasses in {@code nz.co.gregs.dbvolution} for your particular database. * *

* DBDatabase encapsulates the knowledge of the database, in particular the * syntax of the database in the DBDefinition and the connection details from * a DataSource. * * @param definition - the subclass of DBDefinition that provides the syntax * for your database. * @param ds - a DataSource for the required database. * @see DBDefinition * @see OracleDB * @see MSSQLServerDB * @see MySQLDB * @see PostgresDB * @see H2DB * @see H2MemoryDB * @see InformixDB * @see MariaDB * @see MariaClusterDB * @see NuoDB */ public DBDatabase(DBDefinition definition, DataSource ds) { SLEEP_BETWEEN_CONNECTION_RETRIES_MILLIS = 10; MAX_CONNECTION_RETRIES = 6; this.definition = definition; this.dataSource = ds; createRequiredTables(); } /** * Define a new DBDatabase. * *

* Most programmers should not call this constructor directly. Check the * subclasses in {@code nz.co.gregs.dbvolution} for your particular database. * *

* Create a new DBDatabase by providing the connection details * * @param definition - the subclass of DBDefinition that provides the syntax * for your database. * @param driverName - The name of the JDBC class that is the Driver for this * database. * @param jdbcURL - The JDBC URL to connect to the database. * @param username - The username to login to the database as. * @param password - The users password for the database. * @see DBDefinition * @see OracleDB * @see MySQLDB * @see MSSQLServerDB * @see H2DB * @see H2MemoryDB * @see InformixDB * @see PostgresDB */ public DBDatabase(DBDefinition definition, String driverName, String jdbcURL, String username, String password) { SLEEP_BETWEEN_CONNECTION_RETRIES_MILLIS = 1; MAX_CONNECTION_RETRIES = 6; this.definition = definition; this.driverName = driverName; this.jdbcURL = jdbcURL; this.password = password; this.username = username; createRequiredTables(); } DBTransactionStatement getDBTransactionStatement() throws SQLException { final DBStatement dbStatement = getDBStatement(); if (dbStatement instanceof DBTransactionStatement) { return (DBTransactionStatement) dbStatement; } else { return new DBTransactionStatement(this, dbStatement); } } /** * Retrieve the DBStatement used internally. * *

* DBStatement is the internal version of {@code java.sql.Statement} * *

* However you will not need a DBStatement to use DBvolution. Your path lies * elsewhere. * *

Support DBvolution at * Patreon

* * @return the DBStatement to be used: either a new one, or the current * transaction statement. * @throws java.sql.SQLException interacts with the database layer. */ public final DBStatement getDBStatement() throws SQLException { DBStatement statement; synchronized (getStatementSynchronizeObject) { if (isInATransaction) { statement = this.transactionStatement; if (statement.isClosed()) { this.transactionStatement = new DBTransactionStatement(this, getLowLevelStatement()); } } else { statement = getLowLevelStatement(); } } return statement; } protected DBStatement getLowLevelStatement() throws UnableToCreateDatabaseConnectionException, UnableToFindJDBCDriver, SQLException { Connection connection = getConnection(); try { while (connection.isClosed()) { discardConnection(connection); connection = getConnection(); } return new DBStatement(this, connection); } catch (SQLException cantCreateStatement) { discardConnection(connection); throw new UnableToCreateDatabaseConnectionException(getJdbcURL(), getUsername(), cantCreateStatement); } } /** * Retrieve the Connection used internally. * *

* However you will not need a Connection to use DBvolution. Your path lies * elsewhere. * *

Support DBvolution at * Patreon

* * @return the Connection to be used. * @throws java.sql.SQLException interacts with the database layer * @throws UnableToCreateDatabaseConnectionException thrown when there is an * issue connecting * @throws UnableToFindJDBCDriver may be thrown if the JDBCDriver is not on * the class path. DBvolution includes several JDBCDrivers already but Oracle * and MS SQLserver, in particular, need to be added to the path if you wish * to work with those databases. */ public synchronized Connection getConnection() throws UnableToCreateDatabaseConnectionException, UnableToFindJDBCDriver, SQLException { if (isInATransaction && !this.transactionConnection.isClosed()) { return this.transactionConnection; } Connection conn = null; while (conn == null) { if (supportsPooledConnections()) { //synchronized (FREE_CONNECTIONS) { if (FREE_CONNECTIONS.isEmpty() || getConnectionList(FREE_CONNECTIONS).isEmpty()) { conn = getRawConnection(); } else { conn = getConnectionList(FREE_CONNECTIONS).get(0); } //} } else { conn = getRawConnection(); } try { if (conn.isClosed()) { discardConnection(conn); conn = null; } } catch (SQLException ex) { Logger.getLogger(DBDatabase.class.getName()).log(Level.FINEST, null, ex); } if (connectionUsedForPersistentConnection(conn)) { conn = null; } } usedConnection(conn); return conn; } @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value = {"OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE", "ODR_OPEN_DATABASE_RESOURCE"}, justification = "Raw connections are pooled and closed in discardConnection()") private Connection getRawConnection() throws UnableToFindJDBCDriver, UnableToCreateDatabaseConnectionException, SQLException { Connection connection = null; int retries = 0; synchronized (getConnectionSynchronizeObject) { if (this.dataSource == null) { try { // load the driver Class.forName(getDriverName()); } catch (ClassNotFoundException noDriver) { throw new UnableToFindJDBCDriver(getDriverName(), noDriver); } while (connection == null) { try { connection = getConnectionFromDriverManager(); } catch (SQLException noConnection) { if (retries < MAX_CONNECTION_RETRIES) { retries++; try { getConnectionSynchronizeObject.wait(SLEEP_BETWEEN_CONNECTION_RETRIES_MILLIS); } catch (InterruptedException ex) { Logger.getLogger(DBDatabase.class.getName()).log(Level.SEVERE, null, ex); } } else { throw noConnection; } } } } else { try { connection = dataSource.getConnection(); } catch (SQLException noConnection) { throw new UnableToCreateDatabaseConnectionException(dataSource, noConnection); } } } synchronized (this) { if (needToAddDatabaseSpecificFeatures) { addDatabaseSpecificFeatures(connection.createStatement()); needToAddDatabaseSpecificFeatures = false; } } return connection; } private final int SLEEP_BETWEEN_CONNECTION_RETRIES_MILLIS; private int MAX_CONNECTION_RETRIES = 6; /** * Used to hold the database open if required by the database. * */ protected Connection storedConnection; @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value = {"OBL_UNSATISFIED_OBLIGATION", "ODR_OPEN_DATABASE_RESOURCE"}, justification = "Breaking the obligation is required to keep some databases, mostly memory DBs, from disappearing") private boolean connectionUsedForPersistentConnection(Connection connection) throws DBRuntimeException, SQLException { if (getDefinition().persistentConnectionRequired()) { if (storedConnection == null) { this.storedConnection = connection; this.storedConnection.createStatement(); } if (storedConnection.equals(connection)) { return true; } } return false; } /** * * Inserts DBRows into the correct tables automatically * * @param listOfRowsToInsert a list of DBRows *

Support DBvolution at * Patreon

* @return a DBActionList of all the actions performed * @throws SQLException database exceptions */ public final DBActionList insert(DBRow... listOfRowsToInsert) throws SQLException { DBActionList changes = new DBActionList(); for (DBRow row : listOfRowsToInsert) { changes.addAll(this.getDBTable(row).insert(row)); } return changes; } /** * * Inserts DBRows and Lists of DBRows into the correct tables automatically * * @param listOfRowsToInsert a List of DBRows *

Support DBvolution at * Patreon

* @return a DBActionList of all the actions performed * @throws SQLException database exceptions */ public final DBActionList insert(Collection listOfRowsToInsert) throws SQLException { DBActionList changes = new DBActionList(); if (listOfRowsToInsert.size() > 0) { for (DBRow row : listOfRowsToInsert) { changes.addAll(this.getDBTable(row).insert(row)); } } return changes; } /** * * Deletes DBRows from the correct tables automatically * * @param rows a list of DBRows *

Support DBvolution at * Patreon

* @return a DBActionList of all the actions performed * @throws SQLException database exceptions */ public final DBActionList delete(DBRow... rows) throws SQLException { DBActionList changes = new DBActionList(); for (DBRow row : rows) { changes.addAll(this.getDBTable(row).delete(row)); } return changes; } /** * * Deletes Lists of DBRows from the correct tables automatically * * @param list a list of DBRows *

Support DBvolution at * Patreon

* @return a DBActionList of all the actions performed * @throws SQLException database exceptions */ public final DBActionList delete(Collection list) throws SQLException { DBActionList changes = new DBActionList(); if (list.size() > 0) { for (DBRow row : list) { changes.addAll(this.getDBTable(row).delete(row)); } } return changes; } /** * * Updates DBRows and Lists of DBRows in the correct tables automatically. * * Updated rows are marked as updated, and can be used as though they have * been freshly retrieved from the database. * * @param rows a list of DBRows *

Support DBvolution at * Patreon

* @return a DBActionList of the actions performed on the database * @throws SQLException database exceptions */ public final DBActionList update(DBRow... rows) throws SQLException { DBActionList actions = new DBActionList(); for (DBRow row : rows) { actions.addAll(this.getDBTable(row).update(row)); } return actions; } /** * * Updates Lists of DBRows in the correct tables automatically. * * Updated rows are marked as updated, and can be used as though they have * been freshly retrieved from the database. * * @param listOfRowsToUpdate a List of DBRows *

Support DBvolution at * Patreon

* @return a DBActionList of the actions performed on the database * @throws SQLException database exceptions */ public final DBActionList update(Collection listOfRowsToUpdate) throws SQLException { DBActionList actions = new DBActionList(); if (listOfRowsToUpdate.size() > 0) { for (DBRow row : listOfRowsToUpdate) { actions.addAll(this.getDBTable(row).update(row)); } } return actions; } /** * * Automatically selects the correct table based on the example supplied and * returns the selected rows as a list * *

* See * {@link nz.co.gregs.dbvolution.DBTable#getRowsByExample(nz.co.gregs.dbvolution.DBRow)} * * @param the row affected * @param exampleRow the example *

Support DBvolution at * Patreon

* @return a list of the selected rows * @throws SQLException database exceptions */ public List get(R exampleRow) throws SQLException { DBTable dbTable = getDBTable(exampleRow); return dbTable.getAllRows(); } /** * * Automatically selects the correct table based on the example supplied and * returns the selected rows as a list * *

* See * {@link nz.co.gregs.dbvolution.DBTable#getRowsByExample(nz.co.gregs.dbvolution.DBRow)} * * @param the table affected * @param exampleRow the example *

Support DBvolution at * Patreon

* @return a list of the selected rows * @throws SQLException database exceptions */ public List getByExample(R exampleRow) throws SQLException { return get(exampleRow); } /** * * Automatically selects the correct table based on the example supplied and * returns the selected rows as a list * *

* See {@link DBTable#getRowsByExample(nz.co.gregs.dbvolution.DBRow, long)} * * @param the table affected * @param expectedNumberOfRows throw an exception and abort if this number is * not matched * @param exampleRow the example *

Support DBvolution at * Patreon

* @return a list of the selected rows * @throws SQLException database exceptions * @throws UnexpectedNumberOfRowsException the exception thrown if the number * of rows is wrong */ public List get(Long expectedNumberOfRows, R exampleRow) throws SQLException, UnexpectedNumberOfRowsException { if (expectedNumberOfRows == null) { return get(exampleRow); } else { return getDBTable(exampleRow).getRowsByExample(exampleRow, expectedNumberOfRows); } } /** * * Automatically selects the correct table based on the example supplied and * returns the selected rows as a list * *

* See {@link DBTable#getRowsByExample(nz.co.gregs.dbvolution.DBRow, long)} * * @param the table affected * @param expectedNumberOfRows the number of rows required * @param exampleRow the example *

Support DBvolution at * Patreon

* @return a list of the selected rows * @throws SQLException database exceptions * @throws UnexpectedNumberOfRowsException the exception thrown when the * number of rows is not correct */ public List getByExample(Long expectedNumberOfRows, R exampleRow) throws SQLException, UnexpectedNumberOfRowsException { return get(expectedNumberOfRows, exampleRow); } /** * creates a query and fetches the rows automatically, based on the examples * given * * @param rows the examples of the rows required *

Support DBvolution at * Patreon

* @return a list of DBQueryRows relating to the selected rows * @throws SQLException database exceptions * @see DBQuery * @see DBQuery#getAllRows() */ public List get(DBRow... rows) throws SQLException { DBQuery dbQuery = getDBQuery(rows); return dbQuery.getAllRows(); } /** * creates a query and fetches the rows automatically, based on the examples * given * * @param rows the example rows for the tables required *

Support DBvolution at * Patreon

* @return a list of DBQueryRows relating to the selected rows * @throws SQLException database exceptions * @see DBQuery * @see DBQuery#getAllRows() */ public List getByExamples(DBRow... rows) throws SQLException { return get(rows); } /** * * Convenience method to print the rows from get(DBRow...) * * @param rows lists of DBRows, DBReports, or DBQueryRows */ public void print(List rows) { if (rows != null) { for (Object row : rows) { System.out.println(row.toString()); } } } /** * * creates a query and fetches the rows automatically, based on the examples * given * * Will throw a {@link UnexpectedNumberOfRowsException} if the number of rows * found is different from the number expected. See {@link DBQuery#getAllRows(long) * } for further details. * * @param expectedNumberOfRows the number of rows required * @param rows examples of the tables required *

Support DBvolution at * Patreon

* @return a list of DBQueryRows relating to the selected rows * @throws SQLException database exceptions * @throws UnexpectedNumberOfRowsException thrown when the retrieved row count * is wrong * @see DBQuery * @see DBQuery#getAllRows(long) */ public List get(Long expectedNumberOfRows, DBRow... rows) throws SQLException, UnexpectedNumberOfRowsException { if (expectedNumberOfRows == null) { return get(rows); } else { return getDBQuery(rows).getAllRows(expectedNumberOfRows); } } /** * * Convenience method to simplify switching from READONLY to COMMITTED * transaction * * @param the return type of the transaction, can be anything * @param dbTransaction the transaction to execute * @param commit commit=true or rollback=false. *

Support DBvolution at * Patreon

* @return the object returned by the transaction * @throws SQLException database exceptions * @throws java.lang.CloneNotSupportedException * @see DBTransaction * @see * DBDatabase#doTransaction(nz.co.gregs.dbvolution.transactions.DBTransaction) * @see * DBDatabase#doReadOnlyTransaction(nz.co.gregs.dbvolution.transactions.DBTransaction) */ public synchronized V doTransaction(DBTransaction dbTransaction, Boolean commit) throws SQLException, CloneNotSupportedException, Exception { DBDatabase db; db = this.clone(); V returnValues = null; db.transactionStatement = db.getDBTransactionStatement(); try { db.isInATransaction = true; db.transactionConnection = db.transactionStatement.getConnection(); db.transactionConnection.setAutoCommit(false); try { returnValues = dbTransaction.doTransaction(db); if (commit && !explicitCommitActionRequired) { db.transactionConnection.commit(); } else { try { if (!explicitCommitActionRequired) { db.transactionConnection.rollback(); } } catch (SQLException rollbackFailed) { discardConnection(db.transactionConnection); } } } catch (SQLException ex) { try { if (!explicitCommitActionRequired) { db.transactionConnection.rollback(); } } catch (SQLException excp) { LOG.warn("Exception Occurred During Rollback: " + ex.getLocalizedMessage()); } throw ex; } } finally { db.isInATransaction = false; db.transactionStatement.transactionFinished(); discardConnection(db.transactionConnection); db.transactionConnection = null; db.transactionStatement = null; } return returnValues; } /** * Performs the transaction on this database. * *

* If there is an exception of any kind the transaction is rolled back and no * changes are made. * *

* Otherwise the transaction is committed and changes are made permanent * * @param the return type of the transaction * @param dbTransaction the transaction to execute *

Support DBvolution at * Patreon

* @return the object returned by the transaction * @throws SQLException database exceptions * @throws java.lang.CloneNotSupportedException * @see DBTransaction */ public V doTransaction(DBTransaction dbTransaction) throws SQLException, CloneNotSupportedException, Exception { return doTransaction(dbTransaction, true); } /** * Performs the transaction on this database without making changes. * *

* If there is an exception of any kind the transaction is rolled back and no * changes are made. * *

* If no exception occurs, the transaction is still rolled back and no changes * are made * * @param the return type of the transaction * @param dbTransaction the transaction to execute *

Support DBvolution at * Patreon

* @return the object returned by the transaction * @throws SQLException database exceptions * @throws Exception any other exception * @see DBTransaction */ public V doReadOnlyTransaction(DBTransaction dbTransaction) throws SQLException, Exception { return doTransaction(dbTransaction, false); } /** * Convenience method to implement a DBScript on this database * * equivalent to script.implement(this); * * @param script the script to execute and commit *

Support DBvolution at * Patreon

* @return a DBActionList provided by the script * @throws Exception any exception can be thrown by a DBScript */ public DBActionList implement(DBScript script) throws Exception { return script.implement(this); } /** * Convenience method to test a DBScript on this database * * equivalent to script.test(this); * * @param script the script to executed and rollback *

Support DBvolution at * Patreon

* @return a DBActionList provided by the script * @throws Exception DBScripts can throw any exception at any time */ public DBActionList test(DBScript script) throws Exception { return script.test(this); } /** * Returns the name of the JDBC driver class used by this DBDatabase instance. * *

Support DBvolution at * Patreon

* * @return the driverName */ public synchronized String getDriverName() { return driverName; } /** * Sets the name of the JDBC driver class used by this DBDatabase instance. * * @param driver the name of the JDBC Drive class for this DBDatabase. */ protected synchronized void setDriverName(String driver) { driverName = driver; } /** * Returns the JDBC URL used by this instance, if one has been specified. * *

Support DBvolution at * Patreon

* * @return the jdbcURL */ public synchronized String getJdbcURL() { return jdbcURL; } /** * Returns the username specified for this DBDatabase instance. * *

Support DBvolution at * Patreon

* * @return the username */ public synchronized String getUsername() { return username; } /** * Returns the password specified * *

Support DBvolution at * Patreon

* * @return the password */ public synchronized String getPassword() { return password; } /** * Returns a DBTable instance for the DBRow example. * *

* See {@link DBTable DBTable} for more details. * *

* Please be aware that DBtable doesn't assume the example's criteria are * important. Use * {@link DBTable#getRowsByExample(nz.co.gregs.dbvolution.DBRow) getRowsByExample} * to use the criteria on the DBRow. * * @param the table affected * @param example the example row to use in the query *

Support DBvolution at * Patreon

* @return a DBTable instance for the example provided */ public DBTable getDBTable(R example) { return DBTable.getInstance(this, example); } /** * Creates a new DBQuery object with the examples added as * {@link DBQuery#add(nz.co.gregs.dbvolution.DBRow[]) required} tables. * * This is the easiest way to create DBQueries, and indeed queries. * * @param examples the example rows that are required in the query *

Support DBvolution at * Patreon

* @return a DBQuery with the examples as required tables */ public DBQuery getDBQuery(DBRow... examples) { return DBQuery.getInstance(this, examples); } /** * Creates a new DBQuery object with the examples added as * {@link DBQuery#add(nz.co.gregs.dbvolution.DBRow[]) required} tables. * * This is the easiest way to create DBQueries, and indeed queries. * * @param examples the example rows that are required in the query *

Support DBvolution at * Patreon

* @return a DBQuery with the examples as required tables */ public DBQuery getDBQuery(Collection examples) { DBRow[] toArray = examples.toArray(new DBRow[]{}); // return DBQuery.getInstance(this, toArray); return getDBQuery(toArray); } /** * Enables the printing of all SQL to System.out before the SQL is executed. * * @param b TRUE to print SQL before execution, FALSE otherwise. */ public synchronized void setPrintSQLBeforeExecuting(boolean b) { printSQLBeforeExecuting = b; } /** * Indicates whether SQL will be printed before it is executed. * *

Support DBvolution at * Patreon

* * @return the printSQLBeforeExecuting */ public boolean isPrintSQLBeforeExecuting() { return printSQLBeforeExecuting; } /** * Called by internal methods that are about to execute SQL so the SQL can be * printed. * * @param sqlString the raw SQL to print */ public void printSQLIfRequested(String sqlString) { printSQLIfRequested(sqlString, System.out); } synchronized void printSQLIfRequested(String sqlString, PrintStream out) { if (printSQLBeforeExecuting) { out.println(sqlString); } } /** * Creates tables on the database based on the DBRows. * *

* Implemented to facilitate testing, this method creates actual tables on the * database using the default data types supplied by the fields of the DBRows. * * @param includeForeignKeyClauses * @param newTable the table to create * @throws AutoCommitActionDuringTransactionException thrown if this action is * used during a DBTransaction or DBScript */ public void createTableNoExceptions(boolean includeForeignKeyClauses, DBRow newTable) throws AutoCommitActionDuringTransactionException { try { createTable(newTable, includeForeignKeyClauses); } catch (SQLException ex) { LOG.info(ex.getLocalizedMessage()); } } /** * Creates tables on the database based on the DBRows. * *

* Foreign key constraints are NOT created. * *

* Implemented to facilitate testing, this method creates actual tables on the * database using the default data types supplied by the fields of the DBRows. * * @param newTable the table to create * @throws AutoCommitActionDuringTransactionException thrown if this action is * used during a DBTransaction or DBScript */ public void createTableNoExceptions(DBRow newTable) throws AutoCommitActionDuringTransactionException { try { createTable(newTable, false); } catch (SQLException ex) { LOG.info(ex.getLocalizedMessage()); } } /** * Creates tables on the database based on the DBRows. * *

* Implemented to facilitate testing, this method creates actual tables on the * database using the default data types supplied by the fields of the DBRows. * * @param includeForeignKeyClauses * @param newTables the tables to create * @throws AutoCommitActionDuringTransactionException thrown if this action is * used during a DBTransaction or DBScript */ public void createTablesNoExceptions(boolean includeForeignKeyClauses, DBRow... newTables) { for (DBRow tab : newTables) { createTableNoExceptions(includeForeignKeyClauses, tab); } } /** * Creates tables on the database based on the DBRows. * *

* Foreign key constraints are NOT created. *

* Implemented to facilitate testing, this method creates actual tables on the * database using the default data types supplied by the fields of the DBRows. * * @param newTables the tables to create * @throws AutoCommitActionDuringTransactionException thrown if this action is * used during a DBTransaction or DBScript */ public void createTablesNoExceptions(DBRow... newTables) { for (DBRow tab : newTables) { createTableNoExceptions(false, tab); } } /** * Creates tables on the database based on the DBRows, and creates the * required database foreign key constraints. * *

* Implemented to facilitate testing, this method creates actual tables on the * database using the default data types supplied by the fields of the DBRow. * *

* DBvolution does not require actual foreign keys constraints to exist in the * database but there are some advantages in terms of data integrity and * schema transparency. * *

* Unfortunately there are also problems caused by creating foreign key * constraints: insertion order sensitivity for instance. * *

* Personally I prefer the foreign keys to exist, however database constraints * have been described as the "ambulance at the bottom of the cliff" so you * might be better off without them. * * @param newTables table * */ public void createTablesWithForeignKeysNoExceptions(DBRow... newTables) { for (DBRow tab : newTables) { try { createTable(tab, true); } catch (SQLException | AutoCommitActionDuringTransactionException ex) { } } } /** * Creates a table on the database based on the DBRow. * *

* Implemented to facilitate testing, this method creates an actual table on * the database using the default data types supplied by the fields of the * DBRow. * * @param newTableRow the table to create * @throws SQLException database exceptions * @throws AutoCommitActionDuringTransactionException thrown if this action is * used during a DBTransaction or DBScript */ public void createTable(DBRow newTableRow) throws SQLException, AutoCommitActionDuringTransactionException { createTable(newTableRow, false); } /** * Creates a table on the database based on the DBRow, and creates the * required database foreign key constraints. * *

* Implemented to facilitate testing, this method creates an actual table on * the database using the default data types supplied by the fields of the * DBRow. * *

* DBvolution does not require actual foreign keys constraints to exist in the * database but there are some advantages in terms of data integrity and * schema transparency. * *

* Unfortunately there are also problems caused by creating foreign key * constraints: insertion order sensitivity for instance. * *

* Personally I prefer the foreign keys to exist, however database constraints * have been described as the "ambulance at the bottom of the cliff" so you * might be better off without them. * * @param newTableRow table * @throws SQLException database exceptions * */ public void createTableWithForeignKeys(DBRow newTableRow) throws SQLException, AutoCommitActionDuringTransactionException { createTable(newTableRow, true); } public final synchronized String getSQLForCreateTable(DBRow newTableRow, boolean includeForeignKeyClauses) { return getSQLForCreateTable(newTableRow, includeForeignKeyClauses, new ArrayList(), new ArrayList()); } private final synchronized String getSQLForCreateTable(DBRow newTableRow, boolean includeForeignKeyClauses, List pkFields, List spatial2DFields) { StringBuilder sqlScript = new StringBuilder(); String lineSeparator = System.getProperty("line.separator"); // table name sqlScript.append(definition.getCreateTableStart()).append(definition.formatTableName(newTableRow)).append(definition.getCreateTableColumnsStart()).append(lineSeparator); // columns String sep = ""; String nextSep = definition.getCreateTableColumnsSeparator(); List fields = newTableRow.getColumnPropertyWrappers(); List fkClauses = new ArrayList<>(); for (PropertyWrapper field : fields) { if (field.isColumn() && !field.getQueryableDatatype().hasColumnExpression()) { String colName = field.columnName(); sqlScript .append(sep) .append(definition.formatColumnName(colName)) .append(definition.getCreateTableColumnsNameAndTypeSeparator()) .append(definition.getSQLTypeAndModifiersOfDBDatatype(field)); sep = nextSep + lineSeparator; if (field.isPrimaryKey()) { pkFields.add(field); } if (field.isSpatial2DType()) { spatial2DFields.add(field); } String fkClause = definition.getForeignKeyClauseForCreateTable(field); if (!fkClause.isEmpty()) { fkClauses.add(fkClause); } } } if (includeForeignKeyClauses) { for (String fkClause : fkClauses) { sqlScript.append(sep).append(fkClause); sep = nextSep + lineSeparator; } } // primary keys if (definition.prefersTrailingPrimaryKeyDefinition()) { String pkStart = lineSeparator + definition.getCreateTablePrimaryKeyClauseStart(); String pkMiddle = definition.getCreateTablePrimaryKeyClauseMiddle(); String pkEnd = definition.getCreateTablePrimaryKeyClauseEnd() + lineSeparator; String pkSep = pkStart; for (PropertyWrapper field : pkFields) { sqlScript.append(pkSep).append(definition.formatColumnName(field.columnName())); pkSep = pkMiddle; } if (!pkSep.equalsIgnoreCase(pkStart)) { sqlScript.append(pkEnd); } } //finish sqlScript.append(definition.getCreateTableColumnsEnd()).append(lineSeparator).append(definition.endSQLStatement()); return sqlScript.toString(); } private synchronized void createTable(DBRow newTableRow, boolean includeForeignKeyClauses) throws SQLException, AutoCommitActionDuringTransactionException { preventDDLDuringTransaction("DBDatabase.createTable()"); List pkFields = new ArrayList<>(); List spatial2DFields = new ArrayList<>(); String sqlString = getSQLForCreateTable(newTableRow, includeForeignKeyClauses, pkFields, spatial2DFields); try (DBStatement dbStatement = getDBStatement()) { dbStatement.execute(sqlString); //Oracle style trigger based auto-increment keys if (definition.prefersTriggerBasedIdentities() && pkFields.size() == 1) { List triggerBasedIdentitySQL = definition.getTriggerBasedIdentitySQL(this, definition.formatTableName(newTableRow), definition.formatColumnName(pkFields.get(0).columnName())); for (String sql : triggerBasedIdentitySQL) { try { dbStatement.execute(sql); } catch (SQLException sqlex) { // System.out.println(sqlex.getLocalizedMessage()+": "+sql); // sqlex.printStackTrace(); } } } if (definition.requiresSpatial2DIndexes() && spatial2DFields.size() > 0) { List triggerBasedIdentitySQL = definition.getSpatial2DIndexSQL(this, definition.formatTableName(newTableRow), definition.formatColumnName(spatial2DFields.get(0).columnName())); for (String sql : triggerBasedIdentitySQL) { dbStatement.execute(sql); } } } } /** * Adds actual foreign key constraints to the database table represented by * the supplied DBRow. * *

* While database theory stipulates that foreign keys should be represented by * a constraint on the table, this is not part of the industry standard. * DBvolution allows for the creation of these constraints through this * method. * *

* All databases support FK constraints, and they provide useful checks. * However they are the last possible check, represent an inadequate * protection, and can cause considerable difficulties at surprising times. I * recommend against them. * *

* Note: SQLite does not support adding Foreign Keys to existing tables. * * @param newTableRow the table that needs foreign key constraints * @throws SQLException the database has had an issue. */ public synchronized void createForeignKeyConstraints(DBRow newTableRow) throws SQLException { if (this.definition.supportsAlterTableAddConstraint()) { List fields = newTableRow.getColumnPropertyWrappers(); List fkClauses = new ArrayList<>(); for (PropertyWrapper field : fields) { if (field.isColumn() && !field.getQueryableDatatype().hasColumnExpression()) { final String alterTableAddForeignKeyStatement = definition.getAlterTableAddForeignKeyStatement(newTableRow, field); if (!alterTableAddForeignKeyStatement.isEmpty()) { fkClauses.add(alterTableAddForeignKeyStatement); } } } if (fkClauses.size() > 0) { try (DBStatement statement = getDBStatement()) { for (String fkClause : fkClauses) { statement.execute(fkClause); } } } } } /** * Drops All Foreign Key Constraints From The Supplied Table, does not affect * @DBForeignKey. * *

* Generates and executes the required SQL to remove all foreign key * constraints on this table defined within the database. * *

* This methods is supplied as an inverse to * {@link #createForeignKeyConstraints(nz.co.gregs.dbvolution.DBRow)}. * *

* If a pair of tables have foreign keys constraints to each other it may be * necessary to remove the constraints to successfully insert some rows. * DBvolution cannot to protect you from this situation, however this method * will remove some of the problem. * * @param newTableRow the data models version of the table that needs FKs * removed * @throws SQLException database exceptions */ public synchronized void removeForeignKeyConstraints(DBRow newTableRow) throws SQLException { List fields = newTableRow.getColumnPropertyWrappers(); List fkClauses = new ArrayList<>(); for (PropertyWrapper field : fields) { if (field.isColumn() && !field.getQueryableDatatype().hasColumnExpression()) { final String alterTableDropForeignKeyStatement = definition.getAlterTableDropForeignKeyStatement(newTableRow, field); if (!alterTableDropForeignKeyStatement.isEmpty()) { fkClauses.add(alterTableDropForeignKeyStatement); } } } if (fkClauses.size() > 0) { try (DBStatement statement = getDBStatement()) { for (String fkClause : fkClauses) { statement.execute(fkClause); } } } } /** * Adds Database Indexes To All Fields Of This Table. * *

* Use this method to add indexes to all the columns of the table. This is * only necessary once and should really be performed by a DBA. * *

* Adding indexes can improve response time for queries, but has consequences * for storage and insertion time. However in a small database the query * improvement will far out weigh the down sides and this is a recommend route * to improvements. * *

* As usual, your mileage may vary and consult a DBA if trouble persists. * * @param newTableRow the data model's version of the table that needs indexes * @throws SQLException database exceptions */ public synchronized void createIndexesOnAllFields(DBRow newTableRow) throws SQLException { List fields = newTableRow.getColumnPropertyWrappers(); List indexClauses = new ArrayList<>(); for (PropertyWrapper field : fields) { final QueryableDatatype qdt = field.getQueryableDatatype(); if (field.isColumn() && !qdt.hasColumnExpression() && !(qdt instanceof DBLargeObject)) { String indexClause = definition.getIndexClauseForCreateTable(field); if (!indexClause.isEmpty()) { indexClauses.add(indexClause); } } } //Create indexes if (indexClauses.size() > 0) { try (DBStatement statement = getDBStatement()) { for (String indexClause : indexClauses) { statement.execute(indexClause); } } } } /** * Drops a table from the database. * *

* In General NEVER USE THIS METHOD. * *

* Seriously NEVER USE THIS METHOD. * *

* Your DBA will murder you. * * * 1 Database exceptions may be thrown * * @param tableRow tableRow * @throws java.sql.SQLException java.sql.SQLException */ public synchronized void dropTable(DBRow tableRow) throws SQLException, AutoCommitActionDuringTransactionException, AccidentalDroppingOfTableException { preventDDLDuringTransaction("DBDatabase.dropTable()"); if (preventAccidentalDroppingOfTables) { throw new AccidentalDroppingOfTableException(); } StringBuilder sqlScript = new StringBuilder(); final String dropTableStart = definition.getDropTableStart(); final String formatTableName = definition.formatTableName(tableRow); final Object endSQLStatement = definition.endSQLStatement(); sqlScript.append(dropTableStart).append(formatTableName).append(endSQLStatement); String sqlString = sqlScript.toString(); try (DBStatement dbStatement = getDBStatement()) { dbStatement.execute(sqlString); dropAnyAssociatedDatabaseObjects(dbStatement, tableRow); } preventAccidentalDroppingOfTables = true; } /** * Drops a table from the database. * *

* The easy way to drop a table that might not exist. Will still throw a * AutoCommitActionDuringTransactionException if you use it during a * transaction or AccidentalDroppingOfTableException if dropping tables is * being prevented by DBvolution. *

* An even worse idea than {@link #dropTable(nz.co.gregs.dbvolution.DBRow)} *

* In General NEVER USE THIS METHOD. * *

* Seriously NEVER USE THIS METHOD. * *

* Your DBA will murder you. * * @param DBRow type * @param tableRow tableRow */ public void dropTableNoExceptions(TR tableRow) throws AccidentalDroppingOfTableException, AutoCommitActionDuringTransactionException { try { this.dropTable(tableRow); } catch (SQLException exp) { } } /** * Returns the DBdefinition used by this DBDatabase * *

* Every DBDatabase has a DBDefinition that defines the syntax used in that * database. * *

* While DBDefinition is important, unless you are implementing support for a * new database you probably don't need this. * *

Support DBvolution at * Patreon

* * @return the DBDefinition used by this DBDatabase instance */ public synchronized DBDefinition getDefinition() { return definition; } /** * Sets the DBdefinition used by this DBDatabase * *

* Every DBDatabase has a DBDefinition that defines the syntax used in that * database. * *

* While DBDefinition is important, unless you are implementing support for a * new database you probably don't need this. * * @param defn the DBDefinition to be used by this DBDatabase instance. */ protected synchronized final void setDefinition(DBDefinition defn) { if (definition == null) { definition = defn; } } /** * Returns whether or not the example has any specified criteria. * * See * {@link DBRow#willCreateBlankQuery(nz.co.gregs.dbvolution.databases.definitions.DBDefinition) willCreateBlankQuery} * on DBRow. * * @param row row *

Support DBvolution at * Patreon

* @return TRUE if the specified row has no specified criteria, FALSE * otherwise */ public boolean willCreateBlankQuery(DBRow row) { return row.willCreateBlankQuery(this.getDefinition()); } /** * The worst idea EVAH. * *

* Do NOT Use This. * * @param doIt don't do it. * @throws * nz.co.gregs.dbvolution.exceptions.AccidentalDroppingOfDatabaseException * @throws java.lang.CloneNotSupportedException * @throws nz.co.gregs.dbvolution.exceptions.UnableToDropDatabaseException */ public synchronized void dropDatabase(boolean doIt) throws AccidentalDroppingOfDatabaseException, UnsupportedOperationException, AutoCommitActionDuringTransactionException, CloneNotSupportedException, UnableToDropDatabaseException, SQLException { preventDDLDuringTransaction("DBDatabase.dropDatabase()"); if (preventAccidentalDroppingOfTables) { throw new AccidentalDroppingOfTableException(); } if (preventAccidentalDroppingDatabase) { throw new AccidentalDroppingOfDatabaseException(); } String dropStr = getDefinition().getDropDatabase(getDatabaseName()); printSQLIfRequested(dropStr); LOG.info(dropStr); if (doIt) { try { this.doTransaction(new DBRawSQLTransaction(dropStr)); } catch (Exception ex) { throw new UnableToDropDatabaseException(ex); } } preventAccidentalDroppingOfTables = true; preventAccidentalDroppingDatabase = true; } /** * The worst idea EVAH. * *

* Do NOT Use This. * * @param databaseName the database to be permanently and completely * destroyed. * @param doIt don't do it. * @throws java.lang.Exception java.lang.Exception */ public synchronized void dropDatabase(String databaseName, boolean doIt) throws Exception, UnsupportedOperationException, AutoCommitActionDuringTransactionException { preventDDLDuringTransaction("DBDatabase.dropDatabase()"); if (preventAccidentalDroppingOfTables) { throw new AccidentalDroppingOfTableException(); } if (preventAccidentalDroppingDatabase) { throw new AccidentalDroppingOfDatabaseException(); } String dropStr = getDefinition().getDropDatabase(databaseName); printSQLIfRequested(dropStr); LOG.info(dropStr); if (doIt) { this.doTransaction(new DBRawSQLTransaction(dropStr)); } preventAccidentalDroppingOfTables = true; preventAccidentalDroppingDatabase = true; } /** * Returns the database name if one was supplied. * *

Support DBvolution at * Patreon

* * @return the database name */ public synchronized String getDatabaseName() { return databaseName; } /** * Sets the database name. * * @param databaseName databaseName */ final protected synchronized void setDatabaseName(String databaseName) { this.databaseName = databaseName; } /** * Returns whether this DBDatabase will attempt to batch multiple SQL * commands. * *

* It is possible to execute several SQL statements in one instruction, and * generally DBvolution attempts to do that when handed several actions at * once. *

* However sometimes this is inappropriate and this method can help with those * times. * *

Support DBvolution at * Patreon

* * @return TRUE if this instance will try to batch SQL statements, FALSE * otherwise */ public synchronized boolean batchSQLStatementsWhenPossible() { return batchIfPossible; } /** * Sets whether this DBDatabase will attempt to batch multiple SQL commands. * *

* It is possible to execute several SQL statements in one instruction, and * generally DBvolution attempts to do that when handed several actions at * once. *

* However sometimes this is inappropriate and this method can help with those * times. * * @param batchSQLStatementsWhenPossible TRUE if this instance will try to * batch SQL statements, FALSE otherwise */ public synchronized void setBatchSQLStatementsWhenPossible(boolean batchSQLStatementsWhenPossible) { batchIfPossible = batchSQLStatementsWhenPossible; } private synchronized void preventDDLDuringTransaction(String message) throws AutoCommitActionDuringTransactionException { if (isInATransaction) { throw new AutoCommitActionDuringTransactionException(message); } } /** * * @param droppingTablesIsAMistake just leave it at TRUE. */ public synchronized void preventDroppingOfTables(boolean droppingTablesIsAMistake) { this.preventAccidentalDroppingOfTables = droppingTablesIsAMistake; } /** * * @param justLeaveThisAtTrue justLeaveThisAtTrue */ public synchronized void preventDroppingOfDatabases(boolean justLeaveThisAtTrue) { this.preventAccidentalDroppingDatabase = justLeaveThisAtTrue; } /** * Get The Rows For The Supplied DBReport Constrained By The Examples. * *

* Calls the * {@link DBReport#getRows(nz.co.gregs.dbvolution.databases.DBDatabase, nz.co.gregs.dbvolution.DBReport, nz.co.gregs.dbvolution.DBRow...) DBReport getRows method}. * * Retrieves a list of report rows from the database using the constraints * supplied by the report and the examples supplied. * * @param DBReport type * @param report report * @param examples examples *

Support DBvolution at * Patreon

* @return A List of instances of the supplied report from the database 1 * Database exceptions may be thrown * @throws java.sql.SQLException java.sql.SQLException */ public List get(A report, DBRow... examples) throws SQLException { return DBReport.getRows(this, report, examples); } /** * Get All The Rows For The Supplied DBReport Constrained By The Examples. * *

* Provides convenient access to the using DBReport with a blank query. * *

* Calls the * {@link DBReport#getAllRows(nz.co.gregs.dbvolution.databases.DBDatabase, nz.co.gregs.dbvolution.DBReport, nz.co.gregs.dbvolution.DBRow...) DBReport getRows method}. * * Retrieves a list of report rows from the database using the constraints * supplied by the report and the examples supplied. * * @param the DBReport to be derived from the database data. * @param report the report to be produced * @param examples DBRow subclasses that provide extra criteria *

Support DBvolution at * Patreon

* @return A list of the DBreports generated * @throws SQLException database exceptions */ public List getAllRows(A report, DBRow... examples) throws SQLException { return DBReport.getAllRows(this, report, examples); } /** * Get The Rows For The Supplied DBReport Constrained By The Examples. * *

* Calls the * {@link DBReport#getRows(nz.co.gregs.dbvolution.databases.DBDatabase, nz.co.gregs.dbvolution.DBReport, nz.co.gregs.dbvolution.DBRow...) DBReport getRows method}. * * Retrieves a list of report rows from the database using the constraints * supplied by the report and the examples supplied. * * @param DBReport type * @param report report * @param examples examples *

Support DBvolution at * Patreon

* @return A List of instances of the supplied report from the database 1 * Database exceptions may be thrown * @throws java.sql.SQLException java.sql.SQLException */ public List getRows(A report, DBRow... examples) throws SQLException { return DBReport.getRows(this, report, examples); } /** * Provided to allow DBDatabase sub-classes to tweak their connections before * use. * *

* Used by {@link SQLiteDB} in particular. * *

Support DBvolution at * Patreon

* * @return The connection configured ready to use. 1 Database exceptions may * be thrown * @throws java.sql.SQLException java.sql.SQLException */ protected Connection getConnectionFromDriverManager() throws SQLException { try { return DriverManager.getConnection(getJdbcURL(), getUsername(), getPassword()); } catch (SQLException e) { throw new DBRuntimeException("Connection Failed to URL " + getJdbcURL(), e); } } /** * @param jdbcURL the jdbcURL to set */ protected synchronized void setJdbcURL(String jdbcURL) { this.jdbcURL = jdbcURL; } /** * @param username the username to set */ protected synchronized void setUsername(String username) { this.username = username; } /** * @param password the password to set */ protected synchronized void setPassword(String password) { this.password = password; } /** * Called after DROP TABLE to allow the DBDatabase to clean up any extra * objects created with the table. * * @param DBRow type * @param dbStatement statement for executing the changes, don't close it! * @param tableRow tableRow * @throws java.sql.SQLException java.sql.SQLException */ @SuppressWarnings("empty-statement") protected void dropAnyAssociatedDatabaseObjects(DBStatement dbStatement, R tableRow) throws SQLException { ; } /** * Used by DBStatement to release the connection back into the connection * pool. * * * 1 Database exceptions may be thrown * * @param connection connection * @throws java.sql.SQLException java.sql.SQLException */ public synchronized void unusedConnection(Connection connection) throws SQLException { if (supportsPooledConnections()) { getConnectionList(BUSY_CONNECTION).remove(connection); getConnectionList(FREE_CONNECTIONS).add(connection); } else { discardConnection(connection); } } /** * Used to indicate that the DBDatabase class supports Connection Pooling. * *

* The default implementation returns TRUE, and so will probably every * implementation. * *

Support DBvolution at * Patreon

* * @return TRUE if the DBDatabase supports connection pooling, FALSE * otherwise. */ protected boolean supportsPooledConnections() { return true; } private synchronized void usedConnection(Connection connection) { if (supportsPooledConnections()) { getConnectionList(FREE_CONNECTIONS).remove(connection); getConnectionList(BUSY_CONNECTION).add(connection); } } /** * Removes a connection from the available pool. * * You'll not need to use this unless you're replacing DBvolution's database * connection handling. * * @param connection the JDBC connection to be removed */ public synchronized void discardConnection(Connection connection) { getConnectionList(BUSY_CONNECTION).remove(connection); getConnectionList(FREE_CONNECTIONS).remove(connection); try { connection.close(); } catch (SQLException ex) { Logger.getLogger(DBDatabase.class .getName()).log(Level.WARNING, null, ex); } } private synchronized List getConnectionList(Map> connectionMap) { List connList = connectionMap.get(this); if (connList == null) { connList = new ArrayList<>(); connectionMap.put(this, connList); } return connList; } /** * Used By Subclasses To Inject Datatypes, Functions, Etc Into the Database. * * @param statement the statement to use when adding features, DO NOT CLOSE * THIS STATEMENT. * @throws SQLException database exceptions may occur * @see PostgresDB * @see H2DB * @see SQLiteDB * @see OracleDB * @see MSSQLServerDB * @see MySQLDB */ @SuppressWarnings("empty-statement") abstract protected void addDatabaseSpecificFeatures(Statement statement) throws SQLException; /** * Used to add features in a just-in-time manner. * *

* During a statement the database may throw an exception because a feature * has not yet been added. Use this method to parse the exception and install * the required feature. * *

* The statement will be automatically run after this method exits. * * @param exp the exception throw by the database that may need fixing * @throws SQLException accessing the database may cause exceptions */ public void addFeatureToFixException(Exception exp) throws Exception { throw exp; } public DBQueryInsert getDBQueryInsert(K mapper) { return new DBQueryInsert<>(this, mapper); } /** * Creates a DBmigration that will do a conversion from one or more database * tables to another database table. * *

* The mapper class should be an extension of a intended DBRow class that has * instances of the DBRows required in the query, constraints, and mappings to * fill the required columns set in the initialization clause.

* *

* See DBMigrationTest for examples.

* * @param the DBRow extension that maps fields of internal DBRows to all * the fields of it's superclass. * @param mapper a class that can be used to map one or more database tables * to a single table. *

Support DBvolution at * Patreon

* @return a DBQueryInsert for the mapper class */ public DBMigration getDBMigration(K mapper) { return new DBMigration<>(this, mapper); } public void doCommit() { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } public void doRollback() { } public synchronized void setExplicitCommitAction(boolean b) { explicitCommitActionRequired = b; } public DBActionList executeDBAction(DBAction action) throws SQLException { return action.execute(this); } public DBQueryable executeDBQuery(DBQueryable query) throws SQLException { return query.query(this); } public String getSQLForDBQuery(DBQueryable query) { return query.toSQLString(this); } @SuppressFBWarnings( value = "REC_CATCH_EXCEPTION", justification = "Database vendors throw all sorts of silly exceptions") public boolean tableExists(DBRow table) throws SQLException { boolean tableExists = false; if (getDefinition().supportsTableCheckingViaMetaData()) { try (DBStatement dbStatement = getDBStatement()) { Connection conn = dbStatement.getConnection(); ResultSet rset = conn.getMetaData().getTables(null, null, table.getTableName(), null); if (rset.next()) { tableExists = true; } } } else { String testQuery = getDBTable(table) .setQueryTimeout(10000) .setBlankQueryAllowed(true) .setRowLimit(1).getSQLForQuery(); try (DBStatement dbStatement = getDBStatement()) { ResultSet results = dbStatement.executeQuery(testQuery); results.close(); tableExists = true; } catch (Exception ex) { // Theoretically this should only need to catch an SQLException // but databases throw allsorts of weird exceptions } } return tableExists; } boolean tableExists(Class tab) throws SQLException { return tableExists(DBRow.getDBRow(tab)); } private void createRequiredTables() { Set tables = DataModel.getRequiredTables(); for (DBRow table : tables) { try { if (!tableExists(table)) { createTable(table); } } catch (SQLException ex) { Logger.getLogger(DBDatabase.class.getName()).log(Level.SEVERE, null, ex); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy