Class AbstractSchemaUpdater<D,T>
- Type Parameters:
D
- database typeT
- database transaction type
- Direct Known Subclasses:
PersistentObjectSchemaUpdater
,SQLSchemaUpdater
In this class, a database is some stateful object whose structure and/or content may need to change over time. Updates are uniquely named objects capable of making such changes. Databases are also capable of storing the names of the already-applied updates.
Given a database and a set of current updates, this class will ensure that a database is initialized if necessary and up-to-date with respect to the updates.
The primary method is initializeAndUpdateDatabase()
, which will:
- Initialize an empty database (if necessary);
- Apply any outstanding
SchemaUpdate
s as needed, ordered properly according to their predecessor constraints; and - Keep track of which
SchemaUpdate
s have already been applied across restarts.
-
Field Summary
-
Constructor Summary
-
Method Summary
Modifier and TypeMethodDescriptionprotected void
apply
(T transaction, DatabaseAction<T> action) Execute a database action within an existing transaction.protected void
applyInTransaction
(D database, DatabaseAction<T> action) Execute a database action.protected abstract void
commitTransaction
(T transaction) Commit a previously opened transaction.protected abstract boolean
databaseNeedsInitialization
(T transaction) Determine if the database needs initialization.protected String
generateMultiUpdateName
(SchemaUpdate<T> update, int index) Generate the update name for one action within a multi-action update.Get the names of all updates including multi-action updates.getAppliedUpdateNames
(T transaction) Determine which updates have already been applied to the database.protected Comparator<SchemaUpdate<T>>
Determine the preferred ordering of two updates that do not have any predecessor constraints (including implied indirect constraints) between them.Collection<? extends SchemaUpdate<T>>
Get the configured updates.void
initializeAndUpdateDatabase
(D database) Perform database schema initialization and updates.protected abstract void
initializeDatabase
(T transaction) Initialize an uninitialized database.boolean
Determine whether unrecognized updates are ignored or cause an exception.static boolean
isValidUpdateName
(String updateName) Determine if the given schema update name is valid.protected abstract T
openTransaction
(D database) Begin a transaction on the given database.protected abstract void
recordUpdateApplied
(T transaction, String name) Record an update as having been applied to the database.protected abstract void
rollbackTransaction
(T transaction) Roll back a previously opened transaction.void
setIgnoreUnrecognizedUpdates
(boolean ignoreUnrecognizedUpdates) Configure behavior when an unknown update is registered as having already been applied in the database.void
setUpdates
(Collection<? extends SchemaUpdate<T>> updates) Configure the updates.
-
Field Details
-
log
-
-
Constructor Details
-
AbstractSchemaUpdater
public AbstractSchemaUpdater()
-
-
Method Details
-
getUpdates
Get the configured updates. This property is required.- Returns:
- configured updates
- See Also:
-
setUpdates
Configure the updates. This should be the set of all updates that may need to be applied to the database.For any given application, ideally this set should be "write only" in the sense that once an update is added to the set and applied to one or more actual databases, the update and its name should thereafter never change. Otherwise, it would be possible for different databases to have inconsistent schemas even though the same updates were recorded.
Furthermore, if not configured to ignore unrecognized updates already applied (the default behavior), then updates must never be removed from this set as the application evolves; see
setIgnoreUnrecognizedUpdates(boolean)
for more information on the rationale.- Parameters:
updates
- all updates; each update must have a uniquename
.- See Also:
-
isIgnoreUnrecognizedUpdates
public boolean isIgnoreUnrecognizedUpdates()Determine whether unrecognized updates are ignored or cause an exception.- Returns:
- true if unrecognized updates should be ignored, false if they should cause an exception to be thrown
- See Also:
-
setIgnoreUnrecognizedUpdates
public void setIgnoreUnrecognizedUpdates(boolean ignoreUnrecognizedUpdates) Configure behavior when an unknown update is registered as having already been applied in the database.The default behavior is
false
, which results in an exception being thrown. This protects against accidental downgrades (i.e., running older code against a database with a newer schema), which are not supported. However, this also requires that all updates that might ever possibly have been applied to the database be present in the set of configured updates.Setting this to
true
will result in unrecognized updates simply being ignored. This setting loses the downgrade protection but allows obsolete schema updates to be dropped over time.- Parameters:
ignoreUnrecognizedUpdates
- whether to ignore unrecognized updates- See Also:
-
initializeAndUpdateDatabase
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.
- Parameters:
database
- the database to initialize (if necessary) and update- Throws:
Exception
- if an update failsUnrecognizedUpdateException
- if this instance is not configured to ignore unrecognized updates and an unrecognized update has already been appliedIllegalArgumentException
- if two configured updates have the same nameIllegalArgumentException
- 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)
-
isValidUpdateName
Determine if the given schema update name is valid. Valid names are non-empty and have no leading or trailing whitespace.- Parameters:
updateName
- schema update name- Returns:
- true if
updateName
is valid
-
databaseNeedsInitialization
Determine if the database needs initialization.If so,
initializeDatabase(T)
will eventually be invoked.- Parameters:
transaction
- open transaction- Returns:
- true if the database needs initialization
- Throws:
Exception
- if an error occurs while accessing the database
-
initializeDatabase
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.- Parameters:
transaction
- open transaction- Throws:
Exception
- if an error occurs while accessing the database
-
openTransaction
Begin a transaction on the given database. The transaction will always eventually either be committed or rolled back.- Parameters:
database
- database- Returns:
- transaction handle
- Throws:
Exception
- if an error occurs while accessing the database
-
commitTransaction
Commit a previously opened transaction.- Parameters:
transaction
- open transaction previously returned fromopenTransaction()
- Throws:
Exception
- 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.- Parameters:
transaction
- open transaction previously returned fromopenTransaction()
- Throws:
Exception
- if an error occurs while accessing the database
-
getAppliedUpdateNames
Determine which updates have already been applied to the database.- Parameters:
transaction
- open transaction- Returns:
- set of already-applied updates
- Throws:
Exception
- if an error occurs while accessing the database
-
recordUpdateApplied
Record an update as having been applied to the database.- Parameters:
transaction
- open transactionname
- update name- Throws:
IllegalStateException
- if the update has already been recorded in the databaseException
- if an error occurs while accessing the database
-
getOrderingTieBreaker
Determine the preferred ordering of two updates that do not have any predecessor constraints (including implied indirect constraints) between them.The
Comparator
returned by the implementation inAbstractSchemaUpdater
simply sorts updates by name. Subclasses may override if necessary.- Returns:
- a
Comparator
that sorts incomparable updates in the order they should be applied
-
generateMultiUpdateName
Generate the update name for one action within a multi-action update.The implementation in
AbstractSchemaUpdater
just adds a suffix usingindex + 1
, padded to 5 digits, producing names likename-00001
,name-00002
, etc.- Parameters:
update
- the schema updateindex
- the index of the action (zero based)- Returns:
- update name
- See Also:
-
getAllUpdateNames
Get the names of all updates including multi-action updates.- Returns:
- list of update names
- Throws:
Exception
- if an error occurs
-
apply
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.- Parameters:
transaction
- transaction within which to applyaction
action
- operation to perform- Throws:
Exception
- if an error occurs while accessing the database
-
applyInTransaction
Execute a database action. A new transaction will be created, used, and closed. Delegates toapply()
for the actual execution of the action.If the action or
commitTransaction()
fails, the transaction is rolled back.- Parameters:
database
- database to applyaction
toaction
- operation to perform- Throws:
Exception
- if an error occurs while accessing the database
-