Class SQLSchemaUpdater
- Direct Known Subclasses:
SpringSQLSchemaUpdater
AbstractSchemaUpdater
for SQL databases.
Required properties are the database initialization, update table initialization, and the updates themselves.
Applied updates are recorded in a special update table, which contains two columns: one for the unique
update name and one for a timestamp. The update table and column names
are configurable via setUpdateTableName()
,
setUpdateTableNameColumn()
, and setUpdateTableTimeColumn()
.
By default, this class detects a completely uninitialized database by the absence of the update table itself
in the schema (see databaseNeedsInitialization()
).
When an uninitialized database is encountered, the configured database initialization
and update table initialization actions are applied first to initialize
the database schema.
-
Field Summary
Modifier and TypeFieldDescriptionstatic final String
Default name of the table that tracks schema updates,"SchemaUpdate"
.static final String
Default name of the column in the updates table holding the unique update name,"updateName"
.static final String
Default name of the column in the updates table holding the update's time applied,"updateTime"
.Fields inherited from class org.dellroad.stuff.schema.AbstractSchemaUpdater
log
-
Constructor Summary
-
Method Summary
Modifier and TypeMethodDescriptionprotected void
apply
(Connection c, DatabaseAction<Connection> action) Execute a database action within an existing transaction.protected void
Commit a previously opened transaction.protected boolean
Determine if the database needs initialization.Determine which updates have already been applied.Get the empty database initialization.int
Get transaction isolation level for the schema check/migration transaction.Get the update table initialization.Get the name of the table that keeps track of applied updates.Get the name of the update name column in the table that keeps track of applied updates.Get the name of the update timestamp column in the table that keeps track of applied updates.protected boolean
Determine if an exception thrown duringdatabaseNeedsInitialization(java.sql.Connection)
is consistent with an uninitialized database.void
initializeAndUpdateDatabase
(DataSource dataSource) Perform database schema initialization and updates.protected void
Initialize an uninitialized database.protected Connection
openTransaction
(DataSource dataSource) Begin a transaction on the given connection.protected void
recordUpdateApplied
(Connection c, String updateName) Record an update as having been applied.protected void
Roll back a previously opened transaction.void
setDatabaseInitialization
(SQLCommandList databaseInitialization) Configure how an empty database gets initialized.void
setTransactionIsolation
(int transactionIsolation) Set transaction isolation level for the schema check/migration transaction.void
setUpdateTableInitialization
(SQLCommandList updateTableInitialization) Configure how the update table itself gets initialized.void
setUpdateTableName
(String updateTableName) Set the name of the table that keeps track of applied updates.void
setUpdateTableNameColumn
(String updateTableNameColumn) Set the name of the update name column in the table that keeps track of applied updates.void
setUpdateTableTimeColumn
(String updateTableTimeColumn) Set the name of the update timestamp column in the table that keeps track of applied updates.Methods inherited from class org.dellroad.stuff.schema.AbstractSchemaUpdater
applyInTransaction, generateMultiUpdateName, getAllUpdateNames, getOrderingTieBreaker, getUpdates, isIgnoreUnrecognizedUpdates, isValidUpdateName, setIgnoreUnrecognizedUpdates, setUpdates
-
Field Details
-
DEFAULT_UPDATE_TABLE_NAME
Default name of the table that tracks schema updates,"SchemaUpdate"
.- See Also:
-
DEFAULT_UPDATE_TABLE_NAME_COLUMN
Default name of the column in the updates table holding the unique update name,"updateName"
.- See Also:
-
DEFAULT_UPDATE_TABLE_TIME_COLUMN
Default name of the column in the updates table holding the update's time applied,"updateTime"
.- See Also:
-
-
Constructor Details
-
SQLSchemaUpdater
public SQLSchemaUpdater()
-
-
Method Details
-
getUpdateTableName
Get the name of the table that keeps track of applied updates.- Returns:
- the name of the update table
- See Also:
-
setUpdateTableName
Set the name of the table that keeps track of applied updates. Default value isDEFAULT_UPDATE_TABLE_NAME
.This name must be consistent with the update table initialization.
- Parameters:
updateTableName
- the name of the update table
-
getUpdateTableNameColumn
Get the name of the update name column in the table that keeps track of applied updates.- Returns:
- the name of the name column in the update table
- See Also:
-
setUpdateTableNameColumn
Set the name of the update name column in the table that keeps track of applied updates. Default value isDEFAULT_UPDATE_TABLE_NAME_COLUMN
.This name must be consistent with the update table initialization.
- Parameters:
updateTableNameColumn
- the name of the name column in the update table
-
getUpdateTableTimeColumn
Get the name of the update timestamp column in the table that keeps track of applied updates.- Returns:
- the name of the timestamp column in the update table
- See Also:
-
setUpdateTableTimeColumn
Set the name of the update timestamp column in the table that keeps track of applied updates. Default value isDEFAULT_UPDATE_TABLE_TIME_COLUMN
.This name must be consistent with the update table initialization.
- Parameters:
updateTableTimeColumn
- the name of the timestamp column in the update table
-
getUpdateTableInitialization
Get the update table initialization.- Returns:
- SQL commands to create the update table
- See Also:
-
setUpdateTableInitialization
Configure how the update table itself gets initialized. This update is run when no update table found, which (we assume) implies an empty database with no tables or content. This is a required property.This initialization should create the update table where the name column is the primary key. The name column must have a length limit greater than or equal to the longest schema update name.
The table and column names must be consistent with the values configured via
setUpdateTableName()
,setUpdateTableNameColumn()
, andsetUpdateTableTimeColumn()
.For convenience, pre-defined initialization scripts using the default table and column names are available at the following resource locations. These can be used to configure a
SQLCommandList
:Database Resource MySQL (InnoDB) classpath:org/dellroad/stuff/schema/updateTable-mysql.sql
Oracle classpath:org/dellroad/stuff/schema/updateTable-oracle.sql
Postgres classpath:org/dellroad/stuff/schema/updateTable-postgres.sql
HSQLDB classpath:org/dellroad/stuff/schema/updateTable-hsqldb.sql
- Parameters:
updateTableInitialization
- update table schema initialization- See Also:
-
getDatabaseInitialization
Get the empty database initialization.- Returns:
- SQL commands to initialize an empty database
- See Also:
-
setDatabaseInitialization
Configure how an empty database gets initialized. This is a required property.This update is run when no update table found, which (we assume) implies an empty database with no tables or content. Typically this contains the SQL script that gets automatically generated by your favorite schema generation tool.
This script is expected to initialize the database schema (i.e., creating all the tables) so that when completed the database is "up to date" with respect to the configured schema updates. That is, when this action completes, we assume all updates have already been (implicitly) applied (and they will be recorded as such).
Note this script is not expected to create the update table that tracks schema updates; that function is handled by the update table initialization.
- Parameters:
databaseInitialization
- application database schema initialization
-
getTransactionIsolation
public int getTransactionIsolation()Get transaction isolation level for the schema check/migration transaction.Default is
Connection.TRANSACTION_SERIALIZABLE
.- Returns:
- transaction isolation level, or -1 to leave it alone
-
setTransactionIsolation
public void setTransactionIsolation(int transactionIsolation) Set transaction isolation level for the schema check/migration transaction.Default is
Connection.TRANSACTION_SERIALIZABLE
.- Parameters:
transactionIsolation
- transaction isolation level, or -1 to leave it alone
-
apply
Description copied from class:AbstractSchemaUpdater
Execute a database action within an existing transaction.All database operations in
AbstractSchemaUpdater
are performed via this method; subclasses are encouraged to follow this pattern.The implementation in
AbstractSchemaUpdater
simply invokesaction.apply()
; subclasses may override if desired.- Overrides:
apply
in classAbstractSchemaUpdater<DataSource,
Connection> - Parameters:
c
- transaction within which to applyaction
action
- operation to perform- Throws:
SQLException
-
initializeAndUpdateDatabase
Description copied from class:AbstractSchemaUpdater
Perform database schema initialization and updates.This method applies the following logic: if the database needs initialization, then initialize the database and record each update as having been applied; otherwise, apply any unapplied updates as needed.
Note this implies the database initialization must initialize the database to its current, up-to-date state (with respect to the set of all available updates), not its original, pre-update state.
The database initialization step, and each of the update steps, is performed within its own transaction.
- Overrides:
initializeAndUpdateDatabase
in classAbstractSchemaUpdater<DataSource,
Connection> - Parameters:
dataSource
- the database to initialize (if necessary) and update- Throws:
SQLException
- if an update failsIllegalStateException
- if the database needs initialization and either the database initialization or the update table initialization has not been configuredIllegalStateException
IllegalArgumentException
- if any configured update has a required predecessor which is not also a configured update (i.e., if the updates are not transitively closed under predecessors)
-
openTransaction
Begin a transaction on the given connection.The implementation in
SQLSchemaUpdater
creates a serializable-level transaction.- Specified by:
openTransaction
in classAbstractSchemaUpdater<DataSource,
Connection> - Parameters:
dataSource
- the database on which to open the transaction- Returns:
- new
Connection
with an open transaction - Throws:
SQLException
- if an error occurs while accessing the database
-
commitTransaction
Commit a previously opened transaction.The implementation in
SQLSchemaUpdater
just invokesConnection.commit()
.- Specified by:
commitTransaction
in classAbstractSchemaUpdater<DataSource,
Connection> - Parameters:
c
- the connection on which to commit the transaction- Throws:
SQLException
- if an error occurs while accessing the database
-
rollbackTransaction
Roll back a previously opened transaction. This method will also be invoked ifcommitTransaction()
throws an exception.The implementation in
SQLSchemaUpdater
just invokesConnection.rollback()
.- Specified by:
rollbackTransaction
in classAbstractSchemaUpdater<DataSource,
Connection> - Parameters:
c
- the connection on which to roll back the transaction- Throws:
SQLException
- if an error occurs while accessing the database
-
databaseNeedsInitialization
Determine if the database needs initialization.The implementation in
SQLSchemaUpdater
simply invokesSELECT COUNT(*) FROM UPDATETABLE
and checks for success or failure. If an exception is thrown,indicatesUninitializedDatabase(java.sql.Connection, java.sql.SQLException)
is used to distinguish between an exception caused by an uninitialized database and a truly unexpected one.- Specified by:
databaseNeedsInitialization
in classAbstractSchemaUpdater<DataSource,
Connection> - Parameters:
c
- connection to the database- Returns:
- true if the database needs initialization, otherwise false
- Throws:
SQLException
- if an unexpected error occurs while accessing the database
-
indicatesUninitializedDatabase
Determine if an exception thrown duringdatabaseNeedsInitialization(java.sql.Connection)
is consistent with an uninitialized database.This should return true if the exception would be thrown by an SQL query that attempts to access a non-existent table. For exceptions thrown by other causes, this should return false.
The implementation in
SQLSchemaUpdater
always returns true. Subclasses are encouraged to override with a more precise implementation.- Parameters:
c
- connection on which the exception occurrede
- exception thrown during database access indatabaseNeedsInitialization(java.sql.Connection)
- Returns:
- true if
e
indicates an uninitialized database - Throws:
SQLException
- if an error occurs- See Also:
-
recordUpdateApplied
Record an update as having been applied.The implementation in
SQLSchemaUpdater
does the standard JDBC thing using an INSERT statement into the update table.- Specified by:
recordUpdateApplied
in classAbstractSchemaUpdater<DataSource,
Connection> - Parameters:
c
- SQL connectionupdateName
- update name- Throws:
IllegalStateException
- if the update has already been recorded in the databaseSQLException
- if an error occurs while accessing the database
-
getAppliedUpdateNames
Determine which updates have already been applied.The implementation in
SQLSchemaUpdater
does the standard JDBC thing using a SELECT statement from the update table.- Specified by:
getAppliedUpdateNames
in classAbstractSchemaUpdater<DataSource,
Connection> - Parameters:
c
- open transaction- Returns:
- set of already-applied updates
- Throws:
SQLException
- if an error occurs while accessing the database
-
initializeDatabase
Description copied from class:AbstractSchemaUpdater
Initialize an uninitialized database. This should create and initialize the database schema and content, including whatever portion of that is used to track schema updates.- Specified by:
initializeDatabase
in classAbstractSchemaUpdater<DataSource,
Connection> - Parameters:
c
- open transaction- Throws:
SQLException
-