我有一个主键列,这是一个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, AbstractBaseEvent在代码中定义了以下Hibernate映射:
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
几点说明:
- hibernate映射在重构数据类型 之前工作
- Liquibase版本为2.0.1
- 是否适用于MySQL尚未测试
我测试了(Hibernate 3.6.2)。最后,H2 1.3.160,方言:org.hibernate.dialect.H2Dialect)你的情况是什么:
- 当GenerationType为AUTO, data type为INT时,实际生成
- 当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和以前一样。
这是一个老问题,公认的答案有很好的分析但是我遇到了同样的问题,花了3个小时才找到真正的问题和解决方案,所以值得把它留在这里供将来参考。
所以真正的问题是在液体基础上这句话
<changeSet id="1" author="trond">
<modifyDataType tableName="event" columnName="id" newDataType="BIGINT" />
<rollback>
<modifyDataType tableName="event" columnName="id" newDataType="INT" />
</rollback>
</changeSet>`
正在生成alter查询
ALTER TABLE event ALTER COLUMN id BIGINT
从H2
的id列中删除默认的序列值因此,当您尝试插入新行时,id为空,从而抛出SQL错误。
不幸的是,ALTER列的行为因数据库供应商而异,所以最好的解决方案可能是为测试(H2)和生产(MySQL)创建不同的迁移更改日志
和H2你可以使id auto_increment在modifyDataType更改后再次
<addAutoIncrement
columnDataType="bigint"
columnName="id"
incrementBy="1"
startWith="1"
tableName="event"/>
</changeSet>`
我首先将您的@GenerationType
更改为特定的东西(如IDENTITY
),以排除Hibernate从序列中获取奇怪值的任何问题。或者完全删除。
你的重构看起来很好,我看不出有什么明显的问题。
当涉及到引号标识符时,H2和Liquibase通常不能很好地配合;Liquibase中的H2数据库类引用了一些,而不引用其他的。也许案子转换把你搞砸了?EclipseLink有时会有问题,当一个基本类型是0
(!),因为它有时会把这样的值作为null
或未初始化,但据我所知,Hibernate没有这个限制。
我知道这不是一个真正的答案,但希望能让你指向正确的方向。