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 has a one-to-many relationship to TemplateLabel. TemplateLabel uses Template.id and a code as a composite primary key.
My current JPA entities (getters, setters, and constructors omitted)
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;
}
Saving template
I construct the template and pre-fill it with all its labels, setting everything except for Template.id and TemplateLabel.id.templateId, which should have the same value upon inserting into the db.
Template template = Template.builder()
// all the fields except for id and labels
.build();
List<TemplateLabel> labels = dto.getLabels().stream().map(label ->
TemplateLabel.builder()
.id(new TemplateLabelId(null, label.getCode()))
.template(template)
// other fields
.build()
).toList();
template.setLabels(labels);
templateRepository.save(template);
Problem
I would like to generate the ids from the database rather than from the code, and preferably set up the entities in a way so that I can insert a template and some template labels to the database with just one call to repository.save.
In other words, I would like to create a template object with id=null and pre-fill it with some labels (each with id.templateId=null), and just call repository.save on the template, and have the id of the template and all the labels assigned properly, instead of having to call repository.save first with a template to get a db-generated id, and then manully assigning that id to all labels, and then call repository.save again.
With my current configuration, it throws this exception:
Caused by: java.lang.IllegalArgumentException: Can not set java.util.UUID field my.project.entity.TemplateLabelId.templateId to .hibernate.id.IdentifierGeneratorHelper$1
I saw the same exception discussed in another post but the solution there was to use @EmbeddedId
, which I'm already using, so I'm wondering what else I should change in my entities.
Additional context
I'm not sure if the template field in my TemplateLabel class is necessary. A unidirectional binding is probably preferred as there will never be a case where I would retrieve a label on its own, but I'm not sure if I can achieve the one repository.save thing I explained above without it. I would also like to remove it and just use the UUID if I can, to keep things simple. But it's a more minor problem (and it seems that it might be less performant too?).