使用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
相关产品推荐
相关产品推荐

