6

我有一个主键列,它是一个 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;

几点:

  • 休眠映射在重构数据类型之前起作用
  • Liquibase 的版本是 2.0.1
  • 这是否适用于 MySQL 尚未经过测试
4

3 回答 3

6

我测试了(Hibernate 3.6.2.Final,H2 1.3.160,方言:org.hibernate.dialect.H2Dialect)在你的情况下发生了什么:

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

你可以做什么:

如果您希望实际生成类型为 SEQUENCE,如前所述,则

@GeneratedValue(strategy = GenerationType.SEQUENCE)

似乎工作。序列本身不需要更改,因为相应的文档类型无论如何都是 BIGINT。我会这样做,因为那样什么都没有真正改变,而且很清楚序列的生成方式

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

于 2011-10-11T17:39:28.220 回答
2

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

您的重构看起来不错,我看不到任何明显的问题。

在引用标识符方面,H2 和 Liquibase 通常不能很好地配合使用。Liquibase 中的 H2 数据库类引用了一些而不引用其他的。也许案例转换把你搞砸了?

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

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

于 2011-10-11T13:15:03.417 回答
2

这是一个老问题,接受的答案有很好的分析,但我遇到了同样的问题,花了 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

从 H2 中的 id 列中删除默认序列值

结果,当您尝试插入新行时,id 为空,因此引发 SQL 错误。

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

在 H2 上,您可以在 modifyDataType 更改后再次使 id auto_increment

<addAutoIncrement
    columnDataType="bigint"
    columnName="id"
    incrementBy="1"
    startWith="1"
    tableName="event"/>
</changeSet>`
于 2018-05-16T23:21:47.600 回答