Liquibase,在Oracle中创建外键,前提条件

时间:2013-11-22 15:20:17

标签: java sql database oracle liquibase

我有一个我的应用程序的生产和QA实例,我正在整合Liquibase。这意味着DDL和数据已经存在(如果在开发框中则不存在)。我必须创建一个changeLog,它将所有内容记录为非空DB上的RAN,但实际上在空DB上执行。 我的方法很好但是我对创建外键有点困惑。 (数据库是Oracle)。

(一般情况下,我正在创建前置条件,期望各种对象不存在,并且在更改时失败MARK_RAN)。

当我不知道外键的确切名称(可能存在或可能不存在)时,我发现编写正确的前提条件时遇到了困难。 liquibase(前置条件)中有<foreignKeyConstraintExists>个标记,但它只需要schemaNameforeignKeyName个属性(并且它们是必需的)。在这些情况下,我不确定外键名称,因为它们不受我的控制。

您可以在以下前提条件下编写自定义SQL:

<changeSet id="1" author="bob">
    <preConditions onFail="WARN">
        <sqlCheck expectedResult="0">select count(*) from oldtable</sqlCheck>
    </preConditions>
    <dropTable tableName="oldtable"/>
</changeSet>

所以我只需创建一个自定义SQL查询,该查询可以检查表A上的列是否具有外键引用表B并使用结果作为前提条件。 这就是我的问题所在,因为你可以在Oracle中做到这一点,但它非常膨胀:

SELECT a.table_name, a.column_name, a.constraint_name, c.owner, 
       c.r_owner, c_pk.table_name r_table_name
  FROM all_cons_columns a
  JOIN all_constraints c ON a.owner = c.owner
                        AND a.constraint_name = c.constraint_name
  JOIN all_constraints c_pk ON c.r_owner = c_pk.owner
                        AND c.r_constraint_name = c_pk.constraint_name
  WHERE c.constraint_type = 'R' AND a.table_name = 'MY_TABLE'
  AND a.column_name = 'MY_COLUMN'
  AND c_pk.table_name = 'MY_OTHER_TABLE';

如果引用MY_COLUMN的{​​{1}} MY_TABLE上存在外键,则会打印一行。将其重写为COUNT后,您可以在不知道其名称的情况下检查是否有外键。

我的问题: 我有几十个外键,我真的要写这么大的SQL几十次吗?有什么建议,比如外包给某些功能?谢谢!

是否值得让Liquibase开发人员将MY_OTHER_TABLE的name属性设置为可选,并使用本地列名引入引用的表属性alogn?

2 个答案:

答案 0 :(得分:2)

还有一种可能性:实现接口http://www.liquibase.org/javadoc/liquibase/precondition/CustomPrecondition.html并将其用作自定义前提条件。更多信息:http://www.liquibase.org/documentation/preconditions.html

以下是实施(已验证):

import liquibase.database.Database;
import liquibase.exception.CustomPreconditionErrorException;
import liquibase.exception.CustomPreconditionFailedException;
import liquibase.precondition.CustomPrecondition;
import liquibase.snapshot.SnapshotGeneratorFactory;
import liquibase.structure.core.ForeignKey;
import liquibase.structure.core.Schema;
import liquibase.structure.core.Table;
import liquibase.util.StringUtils;

/**
 * {@link CustomPrecondition} implementation that checks if a column on a table
 * has a foreign key constraint for some other table.
 */
public final class CheckForeignKey implements CustomPrecondition {

    /**
     * Schema.
     */
    private String schemaName;

    /**
     * Table name (that has the column).
     */
    private String tableName;

    /**
     * Column (that might have the foreign key).
     */
    private String columnName;

    /**
     * Referenced table of the foreign key.
     */
    private String foreignTableName;

    @Override
    public void check(final Database db)
            throws CustomPreconditionFailedException,
            CustomPreconditionErrorException {

        try {
            // The fkey we are looking for
            final ForeignKey fKey = new ForeignKey();

            // Schema, base table
            fKey.setForeignKeyTable(new Table());
            if (StringUtils.trimToNull(getTableName()) != null) {
                fKey.getForeignKeyTable().setName(getTableName());
            }

            final Schema schema = new Schema();
            schema.setName(getSchemaName());
            fKey.getForeignKeyTable().setSchema(schema);

            // Base column
            fKey.addForeignKeyColumn(getColumnName());

            // Referenced table
            fKey.setPrimaryKeyTable(new Table());
            if (StringUtils.trimToNull(getForeignTableName()) != null) {
                fKey.getPrimaryKeyTable().setName(getForeignTableName());
            }

            if (!SnapshotGeneratorFactory.getInstance().has(fKey, db)) {
                throw new CustomPreconditionFailedException(
                        String.format(
                                "Error fkey not found schema %s table %s column %s ftable %s",
                                getSchemaName(), getTableName(),
                                getColumnName(), getForeignTableName()));
            }
        } catch (final CustomPreconditionFailedException e) {
            throw e;
        } catch (final Exception e) {
            throw new CustomPreconditionErrorException("Error", e);
        }
    }

    public String getSchemaName() {
        return schemaName;
    }

    public void setSchemaName(final String schemaName) {
        this.schemaName = schemaName;
    }

    public String getTableName() {
        return tableName;
    }

    public void setTableName(final String tableName) {
        this.tableName = tableName;
    }

    public String getColumnName() {
        return columnName;
    }

    public void setColumnName(final String columnName) {
        this.columnName = columnName;
    }

    public String getForeignTableName() {
        return foreignTableName;
    }

    public void setForeignTableName(final String foreignTableName) {
        this.foreignTableName = foreignTableName;
    }
}

答案 1 :(得分:1)

如果你不了解外键约束名,我认为你必须这样做。

但是如果你可以修改数据库那么你可以准备sql脚本,它准备另一个sql脚本,它将所有FK重命名为众所周知的名称。这样的事情:

BEGIN

FOR cur IN (
    SELECT 
      c_list.CONSTRAINT_NAME as FK_NAME,
      'FK_' || c_dest.TABLE_NAME || '_' || substr(c_dest.COLUMN_NAME, 1, 20) as NEW_FK_NAME,
      c_src.TABLE_NAME as SRC_TABLE,
      c_src.COLUMN_NAME as SRC_COLUMN,
      c_dest.TABLE_NAME as DEST_TABLE,
      c_dest.COLUMN_NAME as DEST_COLUMN
    FROM ALL_CONSTRAINTS c_list, ALL_CONS_COLUMNS c_src, ALL_CONS_COLUMNS c_dest
    WHERE c_list.CONSTRAINT_NAME = c_src.CONSTRAINT_NAME
    AND c_list.R_CONSTRAINT_NAME = c_dest.CONSTRAINT_NAME
    AND c_list.CONSTRAINT_TYPE = 'R'
    AND c_src.TABLE_NAME IN ('<your-tables-here>')
    GROUP BY c_list.CONSTRAINT_NAME, c_src.TABLE_NAME, c_src.COLUMN_NAME, c_dest.TABLE_NAME, c_dest.COLUMN_NAME;
) LOOP

    -- Generate here SQL commands (by string concatenation) something like:
    -- alter table SRC_TABLE rename constraint FK_NAME to NEW_FK_NAME;
    -- then paste this sql commands to some other script and run it

END LOOP;

END;

这是一次性迁移。

在此迁移之后,您知道您的FK约束名称是什么,您可以使用&lt; foreignKeyConstraintExists&gt;您的变更集中的前提条件。