2013-11-22 3 views
2

У меня есть экземпляр экземпляра и экземпляр QA, в который я интегрирую Liquibase. Это означает, что DDL и данные уже существуют (или нет, если в блоке разработки). Я должен создать changeLog, который записывает все как RAN на непустых БД, но фактически выполняет их на пустых БД. Я нахожусь на хорошем пути, но я немного застрял в создании внешних ключей. (база данных - это Oracle).Liquibase, создавать внешние ключи в Oracle, предварительные условия

(В общем, я создаю предпосылки, ожидающие, что различные объекты НЕ будут существовать и в случае неудачи MARK_RAN это изменение).

Мне трудно найти правильное условие, когда я не знаю точное имя внешних ключей, которое может или не может существовать. Существует отметка <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 из MY_TABLE, который ссылается на MY_OTHER_TABLE. После перезаписи в COUNT вы можете проверить, есть ли внешний ключ, не зная его имени.

Мой вопрос: Я десятки иностранных ключей, я действительно должен написать это большие SQL такие десятки раз? Любые предложения, например, аутсорсинг этой функции? Благодаря!

Стоит ли попросить разработчиков Liquibase указать атрибут имени <foreignKeyConstraintExists> и ввести атрибут таблицы ссылок alogn с локальным именем столбца?

+1

Это хорошая идея, я создал https: //liquibase.jira.com/browse/CORE-1639 –

+0

Действительно с нетерпением жду этого в Liquibase! – gyorgyabraham

ответ

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

Я думаю, что вам нужно сделать это, как вы предложили, если вы не знаете имена ограничений внешнего ключа.

Но если вы можете изменить базу данных, тогда вы можете подготовить 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, и вы можете использовать <foreignKeyConstraintExists> precondition в ваших наборах изменений.

+0

Спасибо, может быть хорошим решением в целом. К сожалению, у меня нет доступа к QA и производственным базам данных, поскольку они обрабатываются назначенными администраторами баз данных. – gyorgyabraham

Смежные вопросы