我有一个我的应用程序的生产和QA实例,我正在整合Liquibase。这意味着DDL和数据已经存在(如果在开发框中则不存在)。我必须创建一个changeLog,它将所有内容记录为非空DB上的RAN,但实际上在空DB上执行。 我的方法很好但是我对创建外键有点困惑。 (数据库是Oracle)。
(一般情况下,我正在创建前置条件,期望各种对象不存在,并且在更改时失败MARK_RAN)。
当我不知道外键的确切名称(可能存在或可能不存在)时,我发现编写正确的前提条件时遇到了困难。
liquibase(前置条件)中有<foreignKeyConstraintExists>
个标记,但它只需要schemaName
和foreignKeyName
个属性(并且它们是必需的)。在这些情况下,我不确定外键名称,因为它们不受我的控制。
您可以在以下前提条件下编写自定义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?
答案 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;您的变更集中的前提条件。