The goal is to have a joining table (FormComponent) which binds a Form (by formId) to a Component (componentId) with an additional column of sortOrder. All three columns together form a unique composite primary key.
My implementation does work if I only define the formId and componentId in my @Embeddable class, but this does not allow me to have duplicates (a form may have the same component multiple times).
If I add the sortOrder-column to the PK and join table class, I get the following error:
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:132
Caused by: org.springframework.beans.factory.BeanCreationException at AbstractAutowireCapableBeanFactory.java:1786
Caused by: javax.persistence.PersistenceException at AbstractEntityManagerFactoryBean.java:421
Caused by: org.hibernate.MappingException at PersistentClass.java:862
The composite PK class:
@EqualsAndHashCode
@AllArgsConstructor
@Getter
@Embeddable
public class FormComponentId implements Serializable {
@Column(name = "form_id")
private Long formId;
@Column(name = "component_id")
private Long componentId;
@Column(name = "sort_order")
private Long sortOrder;
}
The join table class:
@Data
@Getter
@Setter
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "form_component")
public class FormComponent {
@EmbeddedId
private FormComponentId id;
@ManyToOne(fetch = FetchType.EAGER)
@MapsId("formId")
private Form form;
@ManyToOne(fetch = FetchType.EAGER)
@MapsId("componentId")
private Component component;
private Long sortOrder;
}
If I've understood the documentation correctly, I should be able to define a composite PK with more than 2 columns.
The project uses Spring boot 2.5.1.
To answer my own question (after a good nights sleep), the problem was having the sortOrder column defined in the FormComponent class.
With this the form_component gets created correctly:
@Data
@Getter
@Setter
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "form_component")
public class FormComponent {
@EmbeddedId
private FormComponentId id;
@ManyToOne(fetch = FetchType.EAGER)
@MapsId("formId")
private Form form;
@ManyToOne(fetch = FetchType.EAGER)
@MapsId("componentId")
private Component component;
}
form_component table dumped (from MariaDB):
CREATE TABLE form_component (
sort_order bigint(20) NOT NULL,
component_id bigint(20) NOT NULL,
form_id bigint(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
ALTER TABLE form_component
ADD PRIMARY KEY (component_id,form_id,sort_order),
ADD KEY FK1d1gjrgj8elln6irre9vj45e (form_id);
ALTER TABLE form_component
ADD CONSTRAINT FK1d1gjrgj8elln6irre9vj45e FOREIGN KEY (form_id) REFERENCES form (id),
ADD CONSTRAINT FKlbpvqdblh0i147fqctd7dhs6a FOREIGN KEY (component_id) REFERENCES component (id);