唯一约束 (indexExists) 的 Liquibase 前提条件不适用于 HSQLDB 数据库?
Posted
技术标签:
【中文标题】唯一约束 (indexExists) 的 Liquibase 前提条件不适用于 HSQLDB 数据库?【英文标题】:Liquibase Preconditions of Unique Constraints (indexExists) Doesn't Work for HSQLDB Databases? 【发布时间】:2016-09-08 08:56:49 【问题描述】:当我使用 Liquibase 升级现有 HSQLDB 数据库(数据库不包含 2 个 Liquibase 控制表 - databasechangelog 和 databasechangeloglock)时脚本(显示如下),报错java.sql.SQLSyntaxErrorException: object name already exists: SOME_TABLE_UK in statement [ALTER TABLE PUBLIC.SOME_TABLE ADD CONSTRAINT SOME_TABLE_UK UNIQUE (COLUMN_3, COLUMN_4)]
。
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
<property name="TYPE_AS_ID" value="VARCHAR2(36 BYTE)" dbms="oracle" />
<property name="TYPE_AS_ID" value="VARCHAR(36)" dbms="postgresql" />
<property name="TYPE_AS_ID" value="VARCHAR(36)" dbms="hsqldb" />
<property name="TYPE_AS_NORMAL_TEXT" value="VARCHAR2(255 BYTE)" dbms="oracle" />
<property name="TYPE_AS_NORMAL_TEXT" value="TEXT" dbms="postgresql" />
<property name="TYPE_AS_NORMAL_TEXT" value="VARCHAR(255)" dbms="hsqldb" />
<changeSet author="RayChen" id="1">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="SOME_TABLE" />
</not>
</preConditions>
<createTable tableName="SOME_TABLE">
<column name="COLUMN_1" type="$TYPE_AS_ID">
<constraints nullable="false" />
</column>
<column name="COLUMN_2" type="$TYPE_AS_ID" />
<column name="COLUMN_3" type="$TYPE_AS_NORMAL_TEXT" />
<column name="COLUMN_4" type="$TYPE_AS_NORMAL_TEXT" />
</createTable>
</changeSet>
<changeSet author="RayChen" id="2">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="SOME_OTHER_TABLE" />
</not>
</preConditions>
<createTable tableName="SOME_OTHER_TABLE">
<column name="COLUMN_1" type="$TYPE_AS_ID">
<constraints nullable="false" />
</column>
<column name="COLUMN_2" type="$TYPE_AS_NORMAL_TEXT" />
</createTable>
</changeSet>
<changeSet author="RayChen" id="3">
<preConditions onFail="MARK_RAN">
<not>
<primaryKeyExists tableName="SOME_TABLE" />
</not>
</preConditions>
<addPrimaryKey constraintName="SOME_TABLE_PK" tableName="SOME_TABLE" columnNames="COLUMN_1" />
</changeSet>
<changeSet author="RayChen" id="4">
<preConditions onFail="MARK_RAN">
<not>
<primaryKeyExists tableName="SOME_OTHER_TABLE" />
</not>
</preConditions>
<addPrimaryKey constraintName="SOME_OTHER_TABLE_PK" tableName="SOME_OTHER_TABLE" columnNames="COLUMN_1" />
</changeSet>
<changeSet author="RayChen" id="5">
<preConditions onFail="MARK_RAN">
<not>
<foreignKeyConstraintExists foreignKeyName="SOME_TABLE_FK" />
</not>
</preConditions>
<addForeignKeyConstraint constraintName="SOME_TABLE_FK" baseTableName="SOME_TABLE" baseColumnNames="COLUMN_2" referencedTableName="SOME_OTHER_TABLE" referencedColumnNames="COLUMN_1" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" />
</changeSet>
<changeSet author="RayChen" id="6">
<preConditions onFail="MARK_RAN">
<not>
<indexExists indexName="SOME_TABLE_UK" />
</not>
</preConditions>
<addUniqueConstraint constraintName="SOME_TABLE_UK" tableName="SOME_TABLE" columnNames="COLUMN_3, COLUMN_4" />
</changeSet>
</databaseChangeLog>
但是我已经在变更集中添加了一个 not indexExists 前置条件来创建 SOME_TABLE_UK 唯一键,如下所示,为什么会出现上述错误?
<changeSet author="RayChen" id="6">
<preConditions onFail="MARK_RAN">
<not>
<indexExists indexName="SOME_TABLE_UK" />
</not>
</preConditions>
<addUniqueConstraint constraintName="SOME_TABLE_UK" tableName="SOME_TABLE" columnNames="COLUMN_3, COLUMN_4" />
</changeSet>
让我更困惑的是,当数据库是 Oracle 或 PostgreSQL 时,Liquibase 脚本运行良好且没有错误(由于 not indexExists 前提条件,上述更改集 #6 将被跳过),类似的代码在主键和外键上运行良好,没有错误。
【问题讨论】:
支持唯一约束的索引在 HSQLDB 中没有命名为SOME_TABLE_UK
- 它获得了一个生成的名称。例如SYS_IDX_SOME_TABLE_UK_10120
。您需要一个“sql 前提条件”来检查是否定义了唯一约束,而不是索引
@a_horse_with_no_name 感谢您的回复。我也对此表示怀疑,但是当我在HSQLDB数据库管理器中执行SQL语句ALTER TABLE SOME_TABLE DROP CONSTRAINT SOME_TABLE_UK;
时,它成功了;当我执行 SQL 语句 ALTER TABLE SOME_TABLE DROP CONSTRAINT SYS_IDX_SOME_TABLE_UK_15356;
时,它失败了(SYS_IDX_SOME_TABLE_UK_15356 是 HSQLDB 数据库管理器中唯一键 SOME_TABLE_UK 的显示名称)。
@a_horse_with_no_name 如果我想使用 SQL 前置条件来检查是否存在唯一键,你能举个例子来说明如何编写它吗?谢谢。
约束确实被命名为SOME_TABLE_UK
,但索引不是
@a_horse_with_no_name 你是对的!我将该语句从<indexExists indexName="SOME_TABLE_UK" />
更改为<indexExists indexName="SYS_IDX_SOME_TABLE_UK_15356" />
,它在HSQLDB 中有效。谢谢!但是,我刚刚测试过,该名称每次由 HSQLDB 生成时都会有所不同(在我清除数据库并重新运行 Liquibase 脚本后,现在是 SYS_IDX_SOME_TABLE_UK_15691),所以我不能依赖于编码。我们能否有另一种方法来编写唯一键存在前提条件?
【参考方案1】:
这个问题还有另一种方法。您可以使用 sql 系统对象来管理自己的 precontions。 (我不确定这是否是正确的 HSQLDB 语法) .
<changeSet author="RayChen" id="6">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM sys.objects WHERE name LIKE 'SOME_TABLE_UK%'</sqlCheck>
</preConditions>
<addUniqueConstraint constraintName="SOME_TABLE_UK" tableName="SOME_TABLE" columnNames="COLUMN_3, COLUMN_4" />
<changeSet/>
【讨论】:
以上是关于唯一约束 (indexExists) 的 Liquibase 前提条件不适用于 HSQLDB 数据库?的主要内容,如果未能解决你的问题,请参考以下文章