Added support for hand-registered migrations (untested).
This commit is contained in:
@@ -21,7 +21,6 @@
|
|||||||
package com.samskivert.jdbc.depot;
|
package com.samskivert.jdbc.depot;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DatabaseMetaData;
|
import java.sql.DatabaseMetaData;
|
||||||
@@ -37,6 +36,7 @@ import java.util.HashSet;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import com.samskivert.io.PersistenceException;
|
||||||
import com.samskivert.jdbc.depot.annotation.Computed;
|
import com.samskivert.jdbc.depot.annotation.Computed;
|
||||||
import com.samskivert.jdbc.depot.annotation.Entity;
|
import com.samskivert.jdbc.depot.annotation.Entity;
|
||||||
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
|
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
|
||||||
@@ -101,7 +101,8 @@ public class DepotMarshaller<T>
|
|||||||
int mods = field.getModifiers();
|
int mods = field.getModifiers();
|
||||||
|
|
||||||
// check for a static constant schema version
|
// check for a static constant schema version
|
||||||
if ((mods & Modifier.STATIC) != 0 && field.getName().equals(SCHEMA_VERSION_FIELD)) {
|
if (java.lang.reflect.Modifier.isStatic(mods) &&
|
||||||
|
field.getName().equals(SCHEMA_VERSION_FIELD)) {
|
||||||
try {
|
try {
|
||||||
_schemaVersion = (Integer)field.get(null);
|
_schemaVersion = (Integer)field.get(null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -111,7 +112,8 @@ public class DepotMarshaller<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// the field must be public, non-static and non-transient
|
// the field must be public, non-static and non-transient
|
||||||
if (((mods & Modifier.PUBLIC) == 0) || ((mods & Modifier.STATIC) != 0) ||
|
if (!java.lang.reflect.Modifier.isPublic(mods) ||
|
||||||
|
java.lang.reflect.Modifier.isStatic(mods) ||
|
||||||
field.getAnnotation(Transient.class) != null) {
|
field.getAnnotation(Transient.class) != null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -292,104 +294,12 @@ public class DepotMarshaller<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the table used by this marshaller. If the table does not exist, it will be
|
* Returns true if this marshaller has been initialized ({@link #init} has been called), its
|
||||||
* created. If the schema version specified by the persistent object is newer than the database
|
* migrations run and it is ready for operation. False otherwise.
|
||||||
* schema, it will be migrated.
|
|
||||||
*/
|
*/
|
||||||
public void init (Connection conn)
|
public boolean isInitialized ()
|
||||||
throws SQLException
|
|
||||||
{
|
{
|
||||||
// if we have no table (i.e. we're a computed entity), we have nothing to create
|
return _initialized;
|
||||||
if (getTableName() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check to see if our schema version table exists, create it if not
|
|
||||||
JDBCUtil.createTableIfMissing(conn, SCHEMA_VERSION_TABLE,
|
|
||||||
new String[] { "persistentClass VARCHAR(255) NOT NULL",
|
|
||||||
"version INTEGER NOT NULL" }, "");
|
|
||||||
|
|
||||||
// now create the table for our persistent class if it does not exist
|
|
||||||
if (!JDBCUtil.tableExists(conn, getTableName())) {
|
|
||||||
log.fine("Creating table " + getTableName() + " (" +
|
|
||||||
StringUtil.join(_columnDefinitions, ", ") + ") " + _postamble);
|
|
||||||
JDBCUtil.createTableIfMissing(conn, getTableName(), _columnDefinitions, _postamble);
|
|
||||||
updateVersion(conn, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we have a key generator, initialize that too
|
|
||||||
if (_keyGenerator != null) {
|
|
||||||
_keyGenerator.init(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if schema versioning is disabled, stop now
|
|
||||||
if (_schemaVersion < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure the versions match
|
|
||||||
int currentVersion = readVersion(conn);
|
|
||||||
if (currentVersion == _schemaVersion) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("Migrating " + getTableName() + " from " +
|
|
||||||
currentVersion + " to " + _schemaVersion + "...");
|
|
||||||
|
|
||||||
// otherwise try to migrate the schema; doing column additions magically and running any
|
|
||||||
// registered hand-migrations
|
|
||||||
DatabaseMetaData meta = conn.getMetaData();
|
|
||||||
ResultSet rs = meta.getColumns(null, null, getTableName(), "%");
|
|
||||||
HashSet<String> columns = new HashSet<String>();
|
|
||||||
while (rs.next()) {
|
|
||||||
columns.add(rs.getString("COLUMN_NAME"));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String fname : _columnFields) {
|
|
||||||
FieldMarshaller fmarsh = _fields.get(fname);
|
|
||||||
if (columns.contains(fmarsh.getColumnName())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise add the column
|
|
||||||
String coldef = fmarsh.getColumnDefinition();
|
|
||||||
String query = "alter table " + getTableName() + " add column " + coldef;
|
|
||||||
|
|
||||||
// try to add it to the appropriate spot
|
|
||||||
int fidx = ListUtil.indexOf(_allFields, fmarsh.getColumnName());
|
|
||||||
if (fidx == 0) {
|
|
||||||
query += " first";
|
|
||||||
} else {
|
|
||||||
query += " after " + _allFields[fidx-1];
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("Adding column to " + getTableName() + ": " + coldef);
|
|
||||||
Statement stmt = conn.createStatement();
|
|
||||||
try {
|
|
||||||
stmt.executeUpdate(query);
|
|
||||||
} finally {
|
|
||||||
stmt.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the column is a TIMESTAMP column, we need to run a special query to update all
|
|
||||||
// existing rows to the current time because MySQL annoyingly assigns them a default
|
|
||||||
// value of "0000-00-00 00:00:00" regardless of whether we explicitly provide a
|
|
||||||
// "DEFAULT" value for the column or not
|
|
||||||
if (coldef.toLowerCase().indexOf(" timestamp") != -1) {
|
|
||||||
query = "update " + getTableName() + " set " + fmarsh.getColumnName() + " = NOW()";
|
|
||||||
log.info("Assigning current time to TIMESTAMP column: " + query);
|
|
||||||
stmt = conn.createStatement();
|
|
||||||
try {
|
|
||||||
stmt.executeUpdate(query);
|
|
||||||
} finally {
|
|
||||||
stmt.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: run any registered hand migrations
|
|
||||||
|
|
||||||
updateVersion(conn, _schemaVersion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -628,6 +538,167 @@ public class DepotMarshaller<T>
|
|||||||
return pstmt;
|
return pstmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is called by the persistence context to register a migration for the entity managed by
|
||||||
|
* this marshaller.
|
||||||
|
*/
|
||||||
|
protected void registerMigration (EntityMigration migration)
|
||||||
|
{
|
||||||
|
_migrations.add(migration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the table used by this marshaller. This is called automatically by the {@link
|
||||||
|
* PersistenceContext} the first time an entity is used. If the table does not exist, it will
|
||||||
|
* be created. If the schema version specified by the persistent object is newer than the
|
||||||
|
* database schema, it will be migrated.
|
||||||
|
*/
|
||||||
|
protected void init (PersistenceContext ctx)
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
if (_initialized) { // sanity check
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Cannot re-initialize marshaller [type=" + _pclass + "].");
|
||||||
|
}
|
||||||
|
_initialized = true;
|
||||||
|
|
||||||
|
// if we have no table (i.e. we're a computed entity), we have nothing to create
|
||||||
|
if (getTableName() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check to see if our schema version table exists, create it if not
|
||||||
|
ctx.invoke(new Modifier(null) {
|
||||||
|
public int invoke (Connection conn) throws SQLException {
|
||||||
|
JDBCUtil.createTableIfMissing(
|
||||||
|
conn, SCHEMA_VERSION_TABLE,
|
||||||
|
new String[] { "persistentClass VARCHAR(255) NOT NULL",
|
||||||
|
"version INTEGER NOT NULL" }, "");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// now create the table for our persistent class if it does not exist
|
||||||
|
ctx.invoke(new Modifier(null) {
|
||||||
|
public int invoke (Connection conn) throws SQLException {
|
||||||
|
if (!JDBCUtil.tableExists(conn, getTableName())) {
|
||||||
|
log.fine("Creating table " + getTableName() + " (" +
|
||||||
|
StringUtil.join(_columnDefinitions, ", ") + ") " + _postamble);
|
||||||
|
JDBCUtil.createTableIfMissing(
|
||||||
|
conn, getTableName(), _columnDefinitions, _postamble);
|
||||||
|
updateVersion(conn, 1);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// if we have a key generator, initialize that too
|
||||||
|
if (_keyGenerator != null) {
|
||||||
|
ctx.invoke(new Modifier(null) {
|
||||||
|
public int invoke (Connection conn) throws SQLException {
|
||||||
|
_keyGenerator.init(conn);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// if schema versioning is disabled, stop now
|
||||||
|
if (_schemaVersion < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the versions match
|
||||||
|
int currentVersion = ctx.invoke(new Modifier(null) {
|
||||||
|
public int invoke (Connection conn) throws SQLException {
|
||||||
|
String query = "select version from " + SCHEMA_VERSION_TABLE +
|
||||||
|
" where persistentClass = '" + getTableName() + "'";
|
||||||
|
Statement stmt = conn.createStatement();
|
||||||
|
try {
|
||||||
|
ResultSet rs = stmt.executeQuery(query);
|
||||||
|
return (rs.next()) ? rs.getInt(1) : 1;
|
||||||
|
} finally {
|
||||||
|
stmt.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (currentVersion == _schemaVersion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Migrating " + getTableName() + " from " +
|
||||||
|
currentVersion + " to " + _schemaVersion + "...");
|
||||||
|
|
||||||
|
// otherwise try to migrate the schema
|
||||||
|
final HashSet<String> columns = new HashSet<String>();
|
||||||
|
ctx.invoke(new Modifier(null) {
|
||||||
|
public int invoke (Connection conn) throws SQLException {
|
||||||
|
DatabaseMetaData meta = conn.getMetaData();
|
||||||
|
ResultSet rs = meta.getColumns(null, null, getTableName(), "%");
|
||||||
|
while (rs.next()) {
|
||||||
|
columns.add(rs.getString("COLUMN_NAME"));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// run our pre-default-migrations
|
||||||
|
for (EntityMigration migration : _migrations) {
|
||||||
|
if (migration.runBeforeDefault() &&
|
||||||
|
migration.shouldRunMigration(currentVersion, _schemaVersion)) {
|
||||||
|
ctx.invoke(migration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// do our "default" migrations magically
|
||||||
|
for (String fname : _columnFields) {
|
||||||
|
FieldMarshaller fmarsh = _fields.get(fname);
|
||||||
|
if (columns.contains(fmarsh.getColumnName())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise add the column
|
||||||
|
String coldef = fmarsh.getColumnDefinition();
|
||||||
|
String query = "alter table " + getTableName() + " add column " + coldef;
|
||||||
|
|
||||||
|
// try to add it to the appropriate spot
|
||||||
|
int fidx = ListUtil.indexOf(_allFields, fmarsh.getColumnName());
|
||||||
|
if (fidx == 0) {
|
||||||
|
query += " first";
|
||||||
|
} else {
|
||||||
|
query += " after " + _allFields[fidx-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Adding column to " + getTableName() + ": " + coldef);
|
||||||
|
ctx.invoke(new Modifier.Simple(query));
|
||||||
|
|
||||||
|
// if the column is a TIMESTAMP column, we need to run a special query to update all
|
||||||
|
// existing rows to the current time because MySQL annoyingly assigns them a default
|
||||||
|
// value of "0000-00-00 00:00:00" regardless of whether we explicitly provide a
|
||||||
|
// "DEFAULT" value for the column or not
|
||||||
|
if (coldef.toLowerCase().indexOf(" timestamp") != -1) {
|
||||||
|
query = "update " + getTableName() + " set " + fmarsh.getColumnName() + " = NOW()";
|
||||||
|
log.info("Assigning current time to TIMESTAMP column: " + query);
|
||||||
|
ctx.invoke(new Modifier.Simple(query));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// run our post-default-migrations
|
||||||
|
for (EntityMigration migration : _migrations) {
|
||||||
|
if (!migration.runBeforeDefault() &&
|
||||||
|
migration.shouldRunMigration(currentVersion, _schemaVersion)) {
|
||||||
|
ctx.invoke(migration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// record our new version in the database
|
||||||
|
ctx.invoke(new Modifier(null) {
|
||||||
|
public int invoke (Connection conn) throws SQLException {
|
||||||
|
updateVersion(conn, _schemaVersion);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
protected void updateVersion (Connection conn, int version)
|
protected void updateVersion (Connection conn, int version)
|
||||||
throws SQLException
|
throws SQLException
|
||||||
{
|
{
|
||||||
@@ -645,20 +716,6 @@ public class DepotMarshaller<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int readVersion (Connection conn)
|
|
||||||
throws SQLException
|
|
||||||
{
|
|
||||||
String query = "select version from " + SCHEMA_VERSION_TABLE +
|
|
||||||
" where persistentClass = '" + getTableName() + "'";
|
|
||||||
Statement stmt = conn.createStatement();
|
|
||||||
try {
|
|
||||||
ResultSet rs = stmt.executeQuery(query);
|
|
||||||
return (rs.next()) ? rs.getInt(1) : 1;
|
|
||||||
} finally {
|
|
||||||
stmt.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void requireNotComputed (String action)
|
protected void requireNotComputed (String action)
|
||||||
throws SQLException
|
throws SQLException
|
||||||
{
|
{
|
||||||
@@ -700,6 +757,12 @@ public class DepotMarshaller<T>
|
|||||||
/** Used when creating and migrating our table schema. */
|
/** Used when creating and migrating our table schema. */
|
||||||
protected String _postamble = "";
|
protected String _postamble = "";
|
||||||
|
|
||||||
|
/** Indicates that we have been initialized (created or migrated our tables). */
|
||||||
|
protected boolean _initialized;
|
||||||
|
|
||||||
|
/** A list of hand registered entity migrations to run prior to doing the default migration. */
|
||||||
|
protected ArrayList<EntityMigration> _migrations = new ArrayList<EntityMigration>();
|
||||||
|
|
||||||
/** The name of the table we use to track schema versions. */
|
/** The name of the table we use to track schema versions. */
|
||||||
protected static final String SCHEMA_VERSION_TABLE = "DepotSchemaVersion";
|
protected static final String SCHEMA_VERSION_TABLE = "DepotSchemaVersion";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
//
|
||||||
|
// samskivert library - useful routines for java programs
|
||||||
|
// Copyright (C) 2006 Michael Bayne
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or modify it
|
||||||
|
// under the terms of the GNU Lesser General Public License as published
|
||||||
|
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
package com.samskivert.jdbc.depot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These can be registered with the {@link PersistenceContext} to effect hand-coded migrations
|
||||||
|
* between entity versions. The modifier should override {@link #invoke} to perform its
|
||||||
|
* migrations. See {@link PersistenceContext#registerPreMigration} and {@link
|
||||||
|
* PersistenceContext#registerPostMigration}.
|
||||||
|
*/
|
||||||
|
public abstract class EntityMigration extends Modifier
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* If this method returns true, this migration will be run <b>before</b> the default
|
||||||
|
* migrations, if false it will be run after.
|
||||||
|
*/
|
||||||
|
public boolean runBeforeDefault ()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When an Entity is being migrated, this method will be called to check whether this migration
|
||||||
|
* should be run.
|
||||||
|
*/
|
||||||
|
public abstract boolean shouldRunMigration (int currentVersion, int targetVersion);
|
||||||
|
|
||||||
|
protected EntityMigration ()
|
||||||
|
{
|
||||||
|
super(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ package com.samskivert.jdbc.depot;
|
|||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
|
||||||
import com.samskivert.jdbc.depot.clause.Where;
|
import com.samskivert.jdbc.depot.clause.Where;
|
||||||
|
|
||||||
@@ -30,6 +31,27 @@ import com.samskivert.jdbc.depot.clause.Where;
|
|||||||
*/
|
*/
|
||||||
public abstract class Modifier
|
public abstract class Modifier
|
||||||
{
|
{
|
||||||
|
/** A simple modifier that executes a single SQL statement. No cache flushing is done as a
|
||||||
|
* result of this operation. */
|
||||||
|
public static class Simple extends Modifier
|
||||||
|
{
|
||||||
|
public Simple (String query) {
|
||||||
|
super(null);
|
||||||
|
_query = query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int invoke (Connection conn) throws SQLException {
|
||||||
|
Statement stmt = conn.createStatement();
|
||||||
|
try {
|
||||||
|
return stmt.executeUpdate(_query);
|
||||||
|
} finally {
|
||||||
|
stmt.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String _query;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract int invoke (Connection conn) throws SQLException;
|
public abstract int invoke (Connection conn) throws SQLException;
|
||||||
|
|
||||||
protected Modifier (Where key)
|
protected Modifier (Where key)
|
||||||
|
|||||||
@@ -57,26 +57,48 @@ public class PersistenceContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the marshaller for the specified persistent object class,
|
* Registers a migration for the specified entity class.
|
||||||
* creating and initializing it if necessary.
|
*
|
||||||
|
* <p> This method must be called <b>before</b> an Entity is used by any repository. Thus you
|
||||||
|
* should register all migrations immediately after creating your persistence context or if you
|
||||||
|
* are careful to ensure that your Entities are only used by a single repository, you can
|
||||||
|
* register your migrations in the constructor for that repository.
|
||||||
|
*
|
||||||
|
* <p> Note that the migration process is as follows:
|
||||||
|
*
|
||||||
|
* <ul><li> Note the difference between the entity's declared version and the version recorded
|
||||||
|
* in the database.
|
||||||
|
* <li> Run all registered pre-migrations
|
||||||
|
* <li> Perform all default migrations (column additions and removals)
|
||||||
|
* <li> Run all registered post-migrations </ul>
|
||||||
|
*
|
||||||
|
* Thus you must either be prepared for the entity to be at <b>any</b> version prior to your
|
||||||
|
* migration target version because we may start up, find the schema at version 1 and the
|
||||||
|
* Entity class at version 8 and do all "standard" migrations in one fell swoop. So if a column
|
||||||
|
* got added in version 2 and renamed in version 6 and your migration was registered for
|
||||||
|
* version 6 to do that migration, it must be prepared for the column not to exist at all.
|
||||||
|
*
|
||||||
|
* <p> If you want a completely predictable migration process, never use the default migrations
|
||||||
|
* and register a pre-migration for every single schema migration and they will then be
|
||||||
|
* guaranteed to be run in registration order and with predictable pre- and post-conditions.
|
||||||
|
*/
|
||||||
|
public <T> void registerMigration (Class<T> type, EntityMigration migration)
|
||||||
|
{
|
||||||
|
getRawMarshaller(type).registerMigration(migration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the marshaller for the specified persistent object class, creating and initializing
|
||||||
|
* it if necessary.
|
||||||
*/
|
*/
|
||||||
public <T> DepotMarshaller<T> getMarshaller (Class<T> type)
|
public <T> DepotMarshaller<T> getMarshaller (Class<T> type)
|
||||||
throws PersistenceException
|
throws PersistenceException
|
||||||
{
|
{
|
||||||
@SuppressWarnings("unchecked") DepotMarshaller<T> marshaller =
|
DepotMarshaller<T> marshaller = getRawMarshaller(type);
|
||||||
(DepotMarshaller<T>)_marshallers.get(type);
|
if (!marshaller.isInitialized()) {
|
||||||
if (marshaller == null) {
|
// initialize the marshaller which may create or migrate the table for its underlying
|
||||||
_marshallers.put(
|
// persistent object
|
||||||
type, marshaller = new DepotMarshaller<T>(type, this));
|
marshaller.init(this);
|
||||||
// initialize the marshaller which may create or migrate the table
|
|
||||||
// for its underlying persistent object
|
|
||||||
final DepotMarshaller<T> fm = marshaller;
|
|
||||||
invoke(new Modifier(null) {
|
|
||||||
public int invoke (Connection conn) throws SQLException {
|
|
||||||
fm.init(conn);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return marshaller;
|
return marshaller;
|
||||||
}
|
}
|
||||||
@@ -105,6 +127,20 @@ public class PersistenceContext
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up and creates, but does not initialize, the marshaller for the specified Entity
|
||||||
|
* type.
|
||||||
|
*/
|
||||||
|
protected <T> DepotMarshaller<T> getRawMarshaller (Class<T> type)
|
||||||
|
{
|
||||||
|
@SuppressWarnings("unchecked") DepotMarshaller<T> marshaller =
|
||||||
|
(DepotMarshaller<T>)_marshallers.get(type);
|
||||||
|
if (marshaller == null) {
|
||||||
|
_marshallers.put(type, marshaller = new DepotMarshaller<T>(type, this));
|
||||||
|
}
|
||||||
|
return marshaller;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal invoke method that takes care of transient retries
|
* Internal invoke method that takes care of transient retries
|
||||||
* for both queries and modifiers.
|
* for both queries and modifiers.
|
||||||
|
|||||||
@@ -36,6 +36,6 @@ public @interface Entity
|
|||||||
String name () default "";
|
String name () default "";
|
||||||
|
|
||||||
/** Additional SQL to be added when creating this entity's table for the first time. You can
|
/** Additional SQL to be added when creating this entity's table for the first time. You can
|
||||||
* specify additional indices here or supply a MySQL table storage type for example. */
|
* specify a MySQL table storage type for example. */
|
||||||
String postamble () default "";
|
String postamble () default "";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user