|
||||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | |||||||||
java.lang.Objectorg.dellroad.stuff.schema.AbstractSchemaUpdater<D,T>
D - database typeT - database transaction typepublic abstract class AbstractSchemaUpdater<D,T>
Handles the initialization and schema maintenance of a database.
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:
SchemaUpdates as needed, ordered properly according to
their predecessor constraints; andSchemaUpdates have already been applied across restarts.
| Field Summary | |
|---|---|
protected Logger |
log
|
| Constructor Summary | |
|---|---|
AbstractSchemaUpdater()
|
|
| Method Summary | |
|---|---|
protected 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. |
protected abstract Set<String> |
getAppliedUpdateNames(T transaction)
Determine which updates have already been applied to the database. |
protected Comparator<SchemaUpdate<T>> |
getOrderingTieBreaker()
Determine the preferred ordering of two updates that do not have any predecessor constraints (including implied indirect constraints) between them. |
Collection<? extends SchemaUpdate<T>> |
getUpdates()
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 |
isIgnoreUnrecognizedUpdates()
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. |
| Methods inherited from class java.lang.Object |
|---|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
| Field Detail |
|---|
protected final Logger log
| Constructor Detail |
|---|
public AbstractSchemaUpdater()
| Method Detail |
|---|
public Collection<? extends SchemaUpdate<T>> getUpdates()
setUpdates()public void setUpdates(Collection<? extends SchemaUpdate<T>> updates)
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.
updates - all updates; each update must have a unique name.getUpdates(),
setIgnoreUnrecognizedUpdates(boolean)public boolean isIgnoreUnrecognizedUpdates()
setIgnoreUnrecognizedUpdates()public void setIgnoreUnrecognizedUpdates(boolean ignoreUnrecognizedUpdates)
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.
ignoreUnrecognizedUpdates - whether to ignore unrecognized updatesisIgnoreUnrecognizedUpdates()
public void initializeAndUpdateDatabase(D database)
throws Exception
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.
database - the database to initialize (if necessary) and update
Exception - if an update fails
IllegalStateException - if this instance is not configured to ignore
unrecognized updates and an unrecognized update has already been applied
IllegalArgumentException - if two configured updates have the same name
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)public static boolean isValidUpdateName(String updateName)
protected abstract boolean databaseNeedsInitialization(T transaction)
throws Exception
If so, initializeDatabase(T) will eventually be invoked.
transaction - open transaction
Exception - if an error occurs while accessing the database
protected abstract void initializeDatabase(T transaction)
throws Exception
transaction - open transaction
Exception - if an error occurs while accessing the database
protected abstract T openTransaction(D database)
throws Exception
database - database
Exception - if an error occurs while accessing the database
protected abstract void commitTransaction(T transaction)
throws Exception
transaction - open transaction previously returned from openTransaction()
Exception - if an error occurs while accessing the database
protected abstract void rollbackTransaction(T transaction)
throws Exception
commitTransaction() throws an exception.
transaction - open transaction previously returned from openTransaction()
Exception - if an error occurs while accessing the database
protected abstract Set<String> getAppliedUpdateNames(T transaction)
throws Exception
transaction - open transaction
Exception - if an error occurs while accessing the database
protected abstract void recordUpdateApplied(T transaction,
String name)
throws Exception
transaction - open transactionname - update name
IllegalStateException - if the update has already been recorded in the database
Exception - if an error occurs while accessing the databaseprotected Comparator<SchemaUpdate<T>> getOrderingTieBreaker()
The Comparator returned by the implementation in AbstractSchemaUpdater simply sorts updates by name.
Subclasses may override if necessary.
Comparator that sorts incomparable updates in the order they should be applied
protected String generateMultiUpdateName(SchemaUpdate<T> update,
int index)
The implementation in AbstractSchemaUpdater just adds a suffix using index + 1, padded to
5 digits, producing names like name-00001, name-00002, etc.
update - the schema updateindex - the index of the action (zero based)SchemaUpdate.isSingleAction()
protected void apply(T transaction,
DatabaseAction<T> action)
throws Exception
All database operations in AbstractSchemaUpdater are performed via this method;
subclasses are encouraged to follow this pattern.
The implementation in AbstractSchemaUpdater simply invokes action.apply();
subclasses may override if desired.
Exception - if an error occurs while accessing the database
protected void applyInTransaction(D database,
DatabaseAction<T> action)
throws Exception
apply() for the actual execution of the action.
If the action or commitTransaction() fails, the transaction
is rolled back.
Exception - if an error occurs while accessing the database
|
||||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | |||||||||