Project Wonder 5.0

er.extensions.migration
Class ERXMigrationDatabase

java.lang.Object
  extended by er.extensions.migration.ERXMigrationDatabase

public class ERXMigrationDatabase
extends java.lang.Object

ERXMigrationDatabase, ERXMigrationTable, and ERXMigrationColumn exist to make navigating the wonderous API of EOSynchronizationFactory not totally suck. Additionally, these simple models provide a way to insulate yourself from a dependency on EOModels during migrations while still taking advantage of the database independence of the SQL generation that EOF provides. The concept is inspired by the original migration by the Rails migrations API. Currently this API is only suitable for SQL migrations, which is why the terminology is based on the relational model vs EOF's more generic concepts like Models, Entities, and Attributes.

Prior to this API, and still fully supported (and required for more complicated operations), all migrations had to be written with SQL. The downside of writing SQL is that you are writing database-specific operations, which you must provide per-database implementations of. EOF already supports an API for database-agnostic SQL generation that via the EOSynchronizationFactory family of interfaces, but that API is overly complicated. ERXMigrationDatabase aims to provide a much simpler API on top of EOSynchronizationFactory that lets you perform common database-agnostic operations like adding and deleting columns, creating and dropping tables, adding primary keys, and adding foreign keys.

ERXMigrationDatabase is conceptually similar to an EOModel, ERXMigrationTable to an EOEntity, and ERXMigrationColumn to an EOAttribute. The names were specifically chosen to make the SQL-specific nature of the API clear (currently most of the API does not expose SQL-ness, but I'm assuming that in the future it may as the complexity of the operations provided increases). All of the API allows you to build an in-memory model of your structural changes along with some "perform now" method calls that actual execute SQL commands against the provided adaptor channel.

Let's take a look at some examples. Take the very common case of a migration that just adds a new column to a table:

ERXMigrationDatabase.database(channel).existingTableNamed("Request").newStringColumn("requestedByEmailAddress", 255, true);

Another more complex case is that you are introducing an entirely new table that has a foreign key to some existing table:

 ERXMigrationDatabase database = ERXMigrationDatabase.database(channel);
 ERXMigrationTable table = ERXMigrationDatabase.database(channel).newTableNamed("TestPerson");
 table.newStringColumn("FirstName", 100, false);
 table.newStringColumn("LastName", 100, false);
 table.newStringColumn("EmailAddress", 100, false);
 table.newStringColumn("PhoneNumber", 10, true);
 table.newIntegerColumn("PantSize", true);
 table.newTimestampColumn("Birthdate", true);
 table.newBigDecimalColumn("HourlyRate", 32, 4, true);
 table.newFloatColumn("Rating", 10, 2, true);
 table.newBooleanColumn("Married", false);
 table.newIntBooleanColumn("Bald", false);
 table.newIntegerColumn("CompanyID", false);
 table.create();
 table.addForeignKey(table.existingColumnNamed("CompanyID"), database.existingTableNamed("Company").existingColumnNamed("companyID"));
 

In the above examples, database.existingTableNamed and table.existingColumnNamed are called. Calling table/database.existingXxx() does not perform database reverse engineering. It only creates a stub entry that is enough to perform operations like deleting, renaming, foreign keys, etc. Calling table.newXxx does not create the element in the database if the table is new, rather it returns a metadata wrapper (similar to EOAttribute, etc, but with migration-specific API's). However, if the table already exists, calling .newXxxColumn on the table will create the column immediately. You should generally not call .create() on an object you obtained from a call to .existingXxx, because it will only be a stub and generally insufficient to actually create in the database. The call to .existingXxx implies that the corresponding element already exists in the database. If you are creating an entire table, you can use the batching API like the second example where you can call database.newTableNamed(), then .newColumn all the columns in it, followed by a table.create() to create the entire block. For foreign keys, you must have .create()'d both tables (or use existing tables) prior to calling the foreign key methods.

It's important to note that this API relies entirely on EOSynchronizationFactory. If the sync factory for your plugin is wrong, the SQL generation in the ERXMigrationDatabase API's will likewise be wrong.

Author:
mschrag

Nested Class Summary
static class ERXMigrationDatabase.Migration
          A convenience implementation of IERXMigration that passes in an ERXMigrationDatabase instead of channel + model.
 
Field Summary
static org.apache.log4j.Logger log
           
 
Method Summary
 com.webobjects.eoaccess.EOModel _blankModel()
          Returns a blank EOModel with the connection dictionary from the adaptor.
static void _ensureNotEmpty(com.webobjects.foundation.NSArray<com.webobjects.eoaccess.EOSQLExpression> expressions, java.lang.String operationName, boolean required)
          Throws an ERXMigrationFailedException if the array of expressions is empty.
static com.webobjects.foundation.NSArray<java.lang.String> _stringsForExpressions(com.webobjects.foundation.NSArray<com.webobjects.eoaccess.EOSQLExpression> expressions)
          Returns an NSArray of SQL strings that correspond to the NSArray of EOSQLExpressions that were passed in.
 void _tableDropped(ERXMigrationTable table)
          Notification callback to tell the database that the user dropped the given table.
 com.webobjects.eoaccess.EOAdaptor adaptor()
          Returns the adaptor for the given channel.
 com.webobjects.eoaccess.EOAdaptorChannel adaptorChannel()
          Returns the adaptor channel.
static ERXMigrationDatabase database(com.webobjects.eoaccess.EOAdaptorChannel adaptorChannel)
          Returns an ERXMigrationDatabase for the given EOAdaptorChannel.
static ERXMigrationDatabase database(com.webobjects.eoaccess.EOAdaptorChannel adaptorChannel, com.webobjects.eoaccess.EOModel model)
          Returns an ERXMigrationDatabase for the given EOAdaptorChannel.
static ERXMigrationDatabase database(com.webobjects.eoaccess.EOAdaptorChannel adaptorChannel, com.webobjects.eoaccess.EOModel model, com.webobjects.foundation.NSArray<java.lang.String> languages)
          Returns an ERXMigrationDatabase for the given EOAdaptorChannel.
 ERXMigrationColumn existingColumnNamed(java.lang.String tableName, java.lang.String columnName)
          Shortcut to ERXMigrationTable.existingColumnNamed(String).
 ERXMigrationTable existingTableNamed(java.lang.String name)
          Returns an ERXMigrationTable with the given table name.
 boolean is(java.lang.String name)
           
 com.webobjects.foundation.NSArray<java.lang.String> languages()
          Returns the configured default languages for this migration.
 com.webobjects.eoaccess.EOModel model()
          Returns the model associated with this migration.
 ERXMigrationTable newTableNamed(java.lang.String name)
          Creates a new blank ERXMigrationTable.
 java.lang.String productName()
           
 com.webobjects.eoaccess.EOSynchronizationFactory synchronizationFactory()
          Returns the synchronization factory for this adaptor.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

log

public static final org.apache.log4j.Logger log
Method Detail

synchronizationFactory

public com.webobjects.eoaccess.EOSynchronizationFactory synchronizationFactory()
Returns the synchronization factory for this adaptor.

Returns:
the synchronization factory for this adaptor

adaptor

public com.webobjects.eoaccess.EOAdaptor adaptor()
Returns the adaptor for the given channel.

Returns:
the adaptor for the given channel

model

public com.webobjects.eoaccess.EOModel model()
Returns the model associated with this migration.

Returns:
the model associated with this migration

languages

public com.webobjects.foundation.NSArray<java.lang.String> languages()
Returns the configured default languages for this migration.

Returns:
the configured default languages for this migration.

adaptorChannel

public com.webobjects.eoaccess.EOAdaptorChannel adaptorChannel()
Returns the adaptor channel.

Returns:
the adaptor channel

existingTableNamed

public ERXMigrationTable existingTableNamed(java.lang.String name)
Returns an ERXMigrationTable with the given table name. This method does not perform any database reverse engineering. If you ask for an existing table, it will only return a stub of the table that should be sufficient for performing column operations and miscellaneous table operations like dropping. If you call newTableNamed, existingTableNamed will return the tables you create.

Parameters:
name - the name of the table to lookup
Returns:
an ERXMigrationTable instance

existingColumnNamed

public ERXMigrationColumn existingColumnNamed(java.lang.String tableName,
                                              java.lang.String columnName)
Shortcut to ERXMigrationTable.existingColumnNamed(String).

Parameters:
tableName - the name of the existing table
columnName - the name of the existing column
Returns:
the ERXMigrationColumn

newTableNamed

public ERXMigrationTable newTableNamed(java.lang.String name)
Creates a new blank ERXMigrationTable. This is essentially the same as calling existingTableNamed except that it performs some simple validation to make sure this table hasn't been created in this ERXMigrationDatabase yet. Note that this check is not checking the actual database -- it is only verifying that you have not called newTableNamed or existingTableNamed on the name you provide. After calling newTableNamed, the instance returned from this method will also be returned from calls to existingTableNamed. The table will not be created from this call, only an object model is built.

Parameters:
name - the name of the table to create
Returns:
a new ERXMigrationTable

_blankModel

public com.webobjects.eoaccess.EOModel _blankModel()
Returns a blank EOModel with the connection dictionary from the adaptor.

Returns:
a blank EOModel

_tableDropped

public void _tableDropped(ERXMigrationTable table)
Notification callback to tell the database that the user dropped the given table.

Parameters:
table - the table that was dropped

is

public boolean is(java.lang.String name)
Parameters:
name - name of database to match productName()
Returns:
true if productName().equals(name)
See Also:
productName()

productName

public java.lang.String productName()
Returns:
database product name
See Also:
ERXJDBCUtilities#databaseProductName(EOAdaptorChannel)

database

public static ERXMigrationDatabase database(com.webobjects.eoaccess.EOAdaptorChannel adaptorChannel)
Returns an ERXMigrationDatabase for the given EOAdaptorChannel. This will return a new ERXMigrationDatabase for every call, so if you need to perform multiple operations within a single database instance (for instance, adding foreign keys that talk to two tables), you should operate within a single ERXMigrationDatabase instance. If you have a model, you should use database(adaptorChannel, model) instead of this variant so that migrations can use the connection dictionary that is closest to being correct.

Parameters:
adaptorChannel - the adaptor channel to operate within
Returns:
an ERXMigrationDatabase

database

public static ERXMigrationDatabase database(com.webobjects.eoaccess.EOAdaptorChannel adaptorChannel,
                                            com.webobjects.eoaccess.EOModel model,
                                            com.webobjects.foundation.NSArray<java.lang.String> languages)
Returns an ERXMigrationDatabase for the given EOAdaptorChannel. This will return a new ERXMigrationDatabase for every call, so if you need to perform multiple operations within a single database instance (for instance, adding foreign keys that talk to two tables), you should operate within a single ERXMigrationDatabase instance.

Parameters:
adaptorChannel - the adaptor channel to operate within
model - the model that corresponds to this table
languages - the langauges to use for localization
Returns:
an ERXMigrationDatabase

database

public static ERXMigrationDatabase database(com.webobjects.eoaccess.EOAdaptorChannel adaptorChannel,
                                            com.webobjects.eoaccess.EOModel model)
Returns an ERXMigrationDatabase for the given EOAdaptorChannel. This will return a new ERXMigrationDatabase for every call, so if you need to perform multiple operations within a single database instance (for instance, adding foreign keys that talk to two tables), you should operate within a single ERXMigrationDatabase instance.

Parameters:
adaptorChannel - the adaptor channel to operate within
model - the model that corresponds to this table
Returns:
an ERXMigrationDatabase

_ensureNotEmpty

public static void _ensureNotEmpty(com.webobjects.foundation.NSArray<com.webobjects.eoaccess.EOSQLExpression> expressions,
                                   java.lang.String operationName,
                                   boolean required)
Throws an ERXMigrationFailedException if the array of expressions is empty. Not all sync factories support all the listed operations, so this makes sure that the requested operation doesn't silently fail.

Parameters:
expressions - the expressions to check
operationName - the name of the operation being performed (for better error messages)
required - if true, an exception is thrown; if false, an error is logged

_stringsForExpressions

public static com.webobjects.foundation.NSArray<java.lang.String> _stringsForExpressions(com.webobjects.foundation.NSArray<com.webobjects.eoaccess.EOSQLExpression> expressions)
Returns an NSArray of SQL strings that correspond to the NSArray of EOSQLExpressions that were passed in.

Parameters:
expressions - the expressions to retrieve SQL for
Returns:
an NSArray of SQL strings

Last updated: Tue, Feb 21, 2017 • 05:45 PM CET

Copyright © 2002 – 2007 Project Wonder.