JPA中实现含数据库生成外键的复合主键关联实体一键保存及报错解决咨询
Schema
CREATE TABLE Template ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, -- omitted ); CREATE TABLE TemplateLabel ( template_id UUID REFERENCES Template(id) ON DELETE CASCADE, code TEXT NOT NULL, -- omitted PRIMARY KEY (template_id, code) );
Template与TemplateLabel是一对多关系,TemplateLabel使用Template.id和code作为复合主键。
我当前的JPA实体(省略了getter、setter和构造方法)
Template.java:
@Entity public class Template { @Id @GeneratedValue(strategy = GenerationType.UUID) private UUID id; @OneToMany(mappedBy = "template", cascade = CascadeType.ALL, orphanRemoval = true) private List<TemplateLabel> labels; }
TemplateLabel.java:
@Entity public class TemplateLabel { @EmbeddedId private TemplateLabelId id; @ManyToOne(fetch = FetchType.LAZY) @MapsId("templateId") @JoinColumn(name = "template_id") private Template template; }
TemplateLabelId.java:
@Embeddable public class TemplateLabelId { @Column(name = "template_id") private UUID templateId; private String code; }
保存模板的代码
我构造了Template对象并预先填充所有Label,只留Template.id和TemplateLabel.id.templateId未设置,期望插入数据库时这两个值自动设为相同值。
Template template = Template.builder() // 填充除id和labels外的所有字段 .build(); List<TemplateLabel> labels = dto.getLabels().stream().map(label -> TemplateLabel.builder() .id(new TemplateLabelId(null, label.getCode())) .template(template) // 填充其他字段 .build() ).toList(); template.setLabels(labels); templateRepository.save(template);
遇到的问题
我希望通过数据库自动生成ID,并且最好能配置实体实现只调用一次repository.save就能同时插入Template和对应的TemplateLabel。
换句话说,我想创建一个id为null的Template对象,预先填充一些Label(每个Label的id.templateId也为null),只需调用一次templateRepository.save(template),就能让Template的id和所有Label的id.templateId自动被正确赋值,而不需要先调用save获取Template的数据库生成ID,再手动把这个ID赋值给所有Label,最后再调用一次save。
但当前配置下会抛出以下异常:
Caused by: java.lang.IllegalArgumentException: Can not set java.util.UUID field my.project.entity.TemplateLabelId.templateId to org.hibernate.id.IdentifierGeneratorHelper$1
我之前看过类似问题的讨论,那边的解决方案是使用@EmbeddedId,但我已经在用了,所以想知道实体配置还需要改哪里。
额外上下文
我不确定TemplateLabel类中的template字段是否必要。
可能单向关联更好,因为我永远不会单独查询Label,但不确定如果去掉这个字段,是否还能实现上述“一次save完成所有插入”的需求。如果可以的话,我更希望去掉这个字段,只用UUID来关联,让结构更简单。不过这是次要问题,而且似乎双向关联的性能可能更差?
备注:内容来源于stack exchange,提问作者Coco Liliace

