All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.samskivert.depot.SchemaMigration Maven / Gradle / Ivy

The newest version!
//
// Depot library - a Java relational persistence library
// https://github.com/threerings/depot/blob/master/LICENSE

package com.samskivert.depot;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;

import com.samskivert.depot.annotation.Column;
import com.samskivert.depot.expression.ColumnExp;
import com.samskivert.depot.impl.FieldMarshaller;
import com.samskivert.depot.impl.Modifier;
import com.samskivert.depot.impl.jdbc.ColumnDefinition;
import com.samskivert.depot.impl.jdbc.DatabaseLiaison;

import static com.google.common.base.Preconditions.checkArgument;
import static com.samskivert.depot.Log.log;

/**
 * Encapsulates the migration of a persistent record's database schema. 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#registerMigration} for details on the migration process.
 *
 * 

Note: these should only be used for actual schema changes (column additions, removals, * renames, retypes, etc.). It should not be used for data migration, use {@link DataMigration} for * that. */ public abstract class SchemaMigration extends Modifier { /** * A convenient migration for dropping a column from an entity. */ public static class Drop extends SchemaMigration { public Drop (int targetVersion, String columnName) { super(targetVersion); _columnName = columnName; } @Override protected int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException { if (!liaison.tableContainsColumn(conn, _tableName, _columnName)) { // we'll accept this inconsistency log.warning(_tableName + "." + _columnName + " already dropped."); return 0; } log.info("Dropping '" + _columnName + "' from " + _tableName); return liaison.dropColumn(conn, _tableName, _columnName) ? 1 : 0; } protected String _columnName; } /** * A convenient migration for renaming a column in an entity. */ public static class Rename extends SchemaMigration { public Rename (int targetVersion, String oldColumnName, ColumnExp newColumn) { super(targetVersion); _oldColumnName = oldColumnName; _fieldName = newColumn.name; } @Override public boolean runBeforeDefault () { return true; } @Override public void init (String tableName, Map> marshallers) { super.init(tableName, marshallers); FieldMarshaller fm = requireMarshaller(marshallers, _fieldName); _newColumnName = fm.getColumnName(); _newColumnDef = fm.getColumnDefinition(); } @Override protected int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException { if (!liaison.tableContainsColumn(conn, _tableName, _oldColumnName)) { // if the new column already exists, log a warning, otherwise freak out checkArgument(liaison.tableContainsColumn(conn, _tableName, _newColumnName), _tableName + " does not contain '" + _oldColumnName + "'"); log.warning(_tableName + "." + _oldColumnName + " already renamed to " + _newColumnName + "."); return 0; } // nor is this checkArgument(!liaison.tableContainsColumn(conn, _tableName, _newColumnName), _tableName + " already contains '" + _newColumnName + "'"); log.info("Renaming '" + _oldColumnName + "' to '" + _newColumnName + "' in: " + _tableName); return liaison.renameColumn( conn, _tableName, _oldColumnName, _newColumnName, _newColumnDef) ? 1 : 0; } protected String _oldColumnName, _fieldName, _newColumnName; protected ColumnDefinition _newColumnDef; } /** * A convenient migration for changing the type of an existing field. */ public static class Retype extends SchemaMigration { public Retype (int targetVersion, ColumnExp column) { super(targetVersion); _fieldName = column.name; } @Override public boolean runBeforeDefault () { return false; } @Override public void init (String tableName, Map> marshallers) { super.init(tableName, marshallers); FieldMarshaller marsh = requireMarshaller(marshallers, _fieldName); _columnName = marsh.getColumnName(); _newColumnDef = marsh.getColumnDefinition(); } @Override protected int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException { log.info("Updating type of '" + _columnName + "' in " + _tableName); return liaison.changeColumn(conn, _tableName, _columnName, _newColumnDef.type, _newColumnDef.nullable, _newColumnDef.unique, _newColumnDef.defaultValue) ? 1 : 0; } protected String _fieldName, _columnName; protected ColumnDefinition _newColumnDef; } /** * A convenient migration for adding a new column that requires a default value to be specified * during the addition. Normally Depot will automatically handle column addition, but if you * have a column that normally does not have a default value but needs one when it is added to * a table with existing rows, you can use this migration. * * @see Column#defaultValue */ public static class Add extends SchemaMigration { public Add (int targetVersion, ColumnExp column, String defaultValue) { super(targetVersion); _fieldName = column.name; _defaultValue = defaultValue; } @Override public boolean runBeforeDefault () { return true; } @Override public void init (String tableName, Map> marshallers) { super.init(tableName, marshallers); FieldMarshaller marsh = requireMarshaller(marshallers, _fieldName); _columnName = marsh.getColumnName(); _newColumnDef = marsh.getColumnDefinition(); } @Override protected int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException { // override the default value in the column definition with the one provided ColumnDefinition defColumnDef = new ColumnDefinition( _newColumnDef.type, _newColumnDef.nullable, _newColumnDef.unique, _defaultValue); // first add the column with the overridden default value if (liaison.addColumn(conn, _tableName, _fieldName, defColumnDef, true)) { // then change the column to the permanent default value liaison.changeColumn(conn, _tableName, _fieldName, _newColumnDef.type, null, null, _newColumnDef.defaultValue); return 1; } return 0; } protected String _fieldName, _columnName, _defaultValue; protected ColumnDefinition _newColumnDef; } /** * A convenient migration for dropping an index from an entity. */ public static class DropIndex extends SchemaMigration { public DropIndex (int targetVersion, String ixName) { super(targetVersion); _ixName = ixName; } @Override protected int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException { String fullIxName = _tableName + "_" + _ixName; if (!liaison.tableContainsIndex(conn, _tableName, fullIxName)) { // we'll accept this inconsistency log.warning("No index '" + fullIxName + "' found on " + _tableName); return 0; } log.info("Dropping index '" + fullIxName + "' from " + _tableName); liaison.dropIndex(conn, _tableName, fullIxName); return 1; } protected String _ixName; } /** * If this method returns true, this migration will be run before 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. The default implementation runs as long as the currentVersion is less than * the target version supplied to the migration at construct time. */ public boolean shouldRunMigration (int currentVersion, int targetVersion) { return (currentVersion < _targetVersion); } /** * This is called to provide the migration with the name of the entity table and access to its * field marshallers prior to being invoked. This will only be called after this * migration has been determined to be runnable so one cannot rely on this method having been * called in {@link #shouldRunMigration}. */ public void init (String tableName, Map> marshallers) { _tableName = tableName; } protected SchemaMigration (int targetVersion) { super(); _targetVersion = targetVersion; } protected FieldMarshaller requireMarshaller ( Map> marshallers, String fieldName) { FieldMarshaller marsh = marshallers.get(fieldName); checkArgument(marsh != null, "'" + _tableName + "' does not contain field '" + fieldName + "'"); return marsh; } protected int _targetVersion; protected String _tableName; }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy