You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Spring-Data-JPA向H2数据库保存数据失败求助

多对多关系下Spring Data JPA+H2数据库保存失败问题解决

环境信息

  • H2数据库版本:2.2.224
  • Spring Data JPA版本:3.3.3
  • 实体关系:USERS与ROLES为多对多关联

问题现象

保存USERS实体时,首次触发错误:

org.h2.jdbc.JdbcSQLSyntaxErrorException: Generated column "PUBLIC.USERS.USER_ID_PK" cannot be assigned.
SQL statement: insert into users (password,login,user_id_pk) values (?,?,?) [90154-224]

后续调整表结构和ID生成策略后,出现三类核心错误:

  • 主键列设为GENERATED ALWAYS AS IDENTITY,使用AUTO/SEQUENCE策略时,仍抛出Generated column无法赋值错误;
  • 无论调整何种主键生成策略,均出现object references an unsaved transient instance - save the transient instance before flushing: Roles的关联实体未持久化错误;
  • 主键列设为普通主键(无IDENTITY)并使用IDENTITY策略时,抛出**NULL not allowed for column "USER_ID_PK"**错误。

现有配置

自定义序列

CREATE SEQUENCE USERS_SEQ START WITH 1 INCREMENT BY 50;

表结构

CREATE TABLE PUBLIC.ROLES (
   role_id_pk Integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 
   role VARCHAR
);

CREATE TABLE PUBLIC.USERS (
   user_id_pk Integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY, 
   login VARCHAR, 
   password VARCHAR
);

实体类代码

MyUser实体

@Entity
@Table(name = "users")
public class MyUser {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id_pk")
    private Long id;

    @Column(name = "login")
    private String username;

    @Column(name = "password")
    private String password;

    @ManyToMany
    @JoinTable(name = "USERS_ROLES",
               joinColumns = {@JoinColumn(name = "user_id_pk")},
               inverseJoinColumns = {@JoinColumn(name = "role_id_pk")})
    private List<Roles> listRoles;
}

Roles实体

@Entity
@Table(name = "roles")
public class Roles {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "role_id_pk")
    private Long id;

    @Enumerated(EnumType.STRING)
    @Column(length = 20, name = "role")
    private ERole name;

    @ManyToMany(mappedBy="listRoles")
    private List<MyUser> myUsersList;
}

解决方案

1. 修复主键生成冲突

H2的GENERATED ALWAYS AS IDENTITY列禁止手动赋值,而部分JPA策略会尝试传递ID值,需调整如下:

  • 修改表结构主键定义为GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY(BY DEFAULT允许手动赋值,同时支持自动生成,兼容JPA逻辑);
  • 实体类中显式声明主键列不可插入/更新,避免JPA尝试传递值:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id_pk", insertable = false, updatable = false)
private Long id;

2. 解决多对多关联的transient实例错误

多对多关联中,主实体(MyUser)保存前,关联的从实体(Roles)必须处于持久化状态,可通过两种方式处理:

  • 手动持久化关联实体:先调用roleRepository.save()保存所有Roles实例,再关联到MyUser后保存;
  • 启用级联保存:在MyUser的@ManyToMany注解中添加级联策略,让JPA自动保存关联的Roles:
@ManyToMany(cascade = CascadeType.PERSIST)
@JoinTable(name = "USERS_ROLES",
           joinColumns = {@JoinColumn(name = "user_id_pk")},
           inverseJoinColumns = {@JoinColumn(name = "role_id_pk")})
private List<Roles> listRoles;

注:若Roles是系统预设固定角色,建议提前初始化到数据库,避免重复创建;若允许动态创建角色,使用级联保存更便捷。

3. 配置一致性验证

  • 统一实体ID类型与数据库列类型:将数据库中Integer类型主键改为BIGINT,匹配实体类的Long类型,避免类型转换潜在问题;
  • 检查H2连接URL:可添加兼容模式参数(如jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=MySQL),规避语法兼容问题。

内容的提问来源于stack exchange,提问作者Yura Mazurov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 03:16:06