Liquibase:使用带有H2数据库的modifyDataType重构将INT自动增量列更改为BIGINT

时间:2011-10-11 09:51:01

标签: hibernate types refactoring h2 liquibase

我有一个主键列,它是一个INT列,我想将其更改为BIGINT。我们的测试和生产环境使用MySQL,但对于单元测试,我们使用嵌入式H2数据库。

我创建了以下Liquibase重构:

...
<changeSet id="1" author="trond">
    <modifyDataType tableName="event" columnName="id" newDataType="BIGINT" />
    <rollback>
        <modifyDataType tableName="event" columnName="id" newDataType="INT" />
    </rollback>
</changeSet>
...

重构有效,但是当我尝试使用Hibernate将对象持久化到数据库时,我收到以下错误消息(我已经包装了错误消息):

ERROR org.hibernate.util.JDBCExceptionReporter [main]: NULL not allowed for column "ID"; 
    SQL statement: insert into event (id, eventtime, guid, meta, objectguid, originatorid, subtype, type) values (null, ?, ?, ?, ?, ?, ?, '0') [90006-140]

JDBC exception on Hibernate data access: 
    SQLException for SQL [insert into event (id, eventtime, guid, meta, objectguid, originatorid, subtype, type) values (null, ?, ?, ?, ?, ?, ?, '0')]; 
    SQL state [90006]; error code [90006]; could not insert: [event.MyEvent]; 
    nested exception is org.hibernate.exception.GenericJDBCException: could not insert: [event.MyEvent]

MyEvent类继承自AbstractBaseEvent,它在代码中定义了以下Hibernate映射:

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;

几点:

  • hibernate映射在重构数据类型
  • 之前有效
  • Liquibase的版本是2.0.1
  • 这是否适用于MySQL尚未经过测试

3 个答案:

答案 0 :(得分:6)

我测试了(Hibernate 3.6.2.Final,H2 1.3.160,dialect:org.hibernate.dialect.H2Dialect)你的案例中发生了什么:

  • 当GenerationType为AUTO且数据类型为INT时,实际生成 类型是SEQUENCE。
  • 当GenerationType为AUTO且数据类型为BIGINT时,为实际值 生成类型是IDENTITY。因此,如果id-field为,则会失败 定义为ID BIGINT PRIMARY KEY,而不是ID BIGINT IDENTITY (在这里用H2添加PRIMARY KEY将是多余的)。

你能做什么:

如果您希望实际生成类型为SEQUENCE,那么

@GeneratedValue(strategy = GenerationType.SEQUENCE)

似乎有效。序列本身不需要进行任何更改,因为无论如何,documentation类型都是BIGINT。我会这样做,因为那时没有什么真正改变,并且很清楚生成序列的方式

其他可能性是使用startValue将列定义为IDENTITY(因为可能存在的值),并像以前一样使用GenerationType.AUTO。

答案 1 :(得分:1)

我首先会将您的@GenerationType更改为特定的内容(例如IDENTITY),以排除Hibernate从序列中获取奇怪值的任何问题。或者完全删除它。

你的重构看起来很好,我看不出任何明显的问题。

当引用标识符时,H2和Liquibase通常不能很好地协同工作; Liquibase中的H2数据库类引用了一些,并没有引用其他类。案件转换可能让你搞砸了吗?

当原始类型为0(!)时,EclipseLink有时会出现问题,因为它有时会将这样的值视为null或未初始化,但据我所知,Hibernate不会受到此限制。< / p>

我知道,这不是一个真正的答案,但希望能让你指出正确的方向。

答案 2 :(得分:1)

这是一个老问题,接受的答案有很好的分析 但我遇到了同样的问题,花了3个小时才找到真正的问题和解决方案,因此将其留在这里以供将来参考。

所以真正的问题是liquibase 这句话

<changeSet id="1" author="trond"> <modifyDataType tableName="event" columnName="id" newDataType="BIGINT" /> <rollback> <modifyDataType tableName="event" columnName="id" newDataType="INT" /> </rollback> </changeSet>

正在产生这个 改变查询

ALTER TABLE event ALTER COLUMN id BIGINT

从id列

中删除默认序列值

因此,当您尝试插入新行时,id为null,因此抛出了sql错误。

不幸的是,ALTER列的行为因数据库供应商而异,因此最好的解决方案可能是为测试(H2)和生产(mysql)创建不同的迁移更改日志

并且在H2上,您可以在modifyDataType更改

后再次进行id auto_increment

<addAutoIncrement columnDataType="bigint" columnName="id" incrementBy="1" startWith="1" tableName="event"/> </changeSet>