H2数据库中PreparedStatement与自增ID引发无原生标识值错误
解决H2数据库视图写入触发的“无原生生成的标识值”错误
嘿,我来帮你搞定这个问题!首先得搞清楚为啥会报错:你的JPA实体用了GenerationType.IDENTITY,这意味着它依赖数据库自动生成主键后返回给它,但你通过TriggerAdapter里的PreparedStatement处理视图插入时,没把数据库生成的ID正确返回给JPA,所以JPA就喊“找不到原生生成的标识值”了。
下面给你几个可行的解决方案,按优先级排序:
方案1:修改触发器的PreparedStatement,让它返回生成的ID
这是最直接的修复方式,因为问题本质就是JPA拿不到生成的ID。你需要在触发器的插入语句里加上返回主键的逻辑,并且把ID同步回触发上下文:
@Override public void init(Connection conn, String schemaName, String triggerName, String tableName, boolean before, int type) throws SQLException { // 初始化插入语句时,加上RETURNING id,同时指定返回生成的键 prepInsert = conn.prepareStatement( "INSERT INTO school (/* 这里填你的其他列,比如name之类的 */) VALUES (/* 对应参数占位符 */) RETURNING id", Statement.RETURN_GENERATED_KEYS ); // 初始化delete语句的逻辑保持不变... } @Override public void fire(Connection conn, Object[] oldRow, Object[] newRow) throws SQLException { // 处理插入场景:newRow不为null,oldRow为null if (newRow != null && oldRow == null) { // 先给prepInsert设置好所有参数,比如prepInsert.setString(1, newRow[1].toString()); prepInsert.executeUpdate(); // 拿到数据库生成的ID ResultSet generatedKeys = prepInsert.getGeneratedKeys(); if (generatedKeys.next()) { Long generatedId = generatedKeys.getLong(1); // 把生成的ID塞回newRow的对应位置(假设id是实体的第一个列,对应newRow[0]) newRow[0] = generatedId; } } // 处理delete的逻辑... }
这样JPA就能从触发器的操作结果中拿到生成的ID,错误自然就消失了。
方案2:换用序列生成主键(备选方案)
如果触发器的代码不好修改,你可以调整实体的主键生成策略,用序列代替IDENTITY,这样JPA会先从序列拿到ID再插入,不需要依赖数据库返回:
- 修改实体类的主键注解:
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "school_seq") @SequenceGenerator(name = "school_seq", schema = "vw", sequenceName = "school_sequence", allocationSize = 1) private Long id;
- 在Liquibase里添加序列和表的默认值配置:
CREATE SEQUENCE vw.school_sequence START WITH 1 INCREMENT BY 1; ALTER TABLE vw.school ALTER COLUMN id SET DEFAULT NEXT VALUE FOR vw.school_sequence;
这种方式绕开了触发器返回ID的问题,也能正常工作。
方案3:确保触发器是AFTER类型
最后检查一下你的触发器创建语句,一定要用AFTER触发器,而不是BEFORE。因为BEFORE触发器在实际插入表之前执行,这时候主键还没生成,JPA根本拿不到:
CREATE TRIGGER school_view_trigger AFTER INSERT ON vw.your_school_view_name FOR EACH ROW CALL "com.yourpackage.UpdatableViewSchool";
AFTER触发器会在数据插入到实际表、主键生成之后再执行,这时候获取ID才是有效的。
内容的提问来源于stack exchange,提问作者Interlated
相关产品推荐
相关产品推荐

