List <? Extends Enum>
dans DTO au lieu de CSV.List <ChildEntity>
etc. dans l'entité, vous irez soit chercher la clé de substitution à chaque fois que vous effectuez une opération de mise à jour, soit vous insérerez tous les enregistrements de ChildEntity. Ce sera corrigé.=> Oui, utilisons la clé primaire composée
Par exemple, dans MySQL, une telle table
CREATE TABLE parent(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
bar VARCHAR(30) NOT NULL
);
CREATE TABLE parent_child(
parent_id INT NOT NULL,
value_ VARCHAR(30) NOT NULL,
PRIMARY KEY(parent_id, value_),
FOREIGN KEY(parent_id) REFERENCES parent(id)
);
ChildEntity
Puisqu'il s'agit d'une clé primaire composite, nous utiliserons @ EmbeddedId
, mais la référence à l'entité parente avec @ ManyToOne
doit être dans le champ de la classe Entity externe au lieu de la classe PK __. [^ 1]
[^ 1]: StackOverflowError se produit pendant l'opération de lecture si la classe PK a un champ pour @ ManyToOne
.
En ajoutant @ MapsId
avec @ ManyToOne
, l'ID du côté de la table parent sera attribué lorsque le parent et l'enfant seront insérés en même temps. Cependant, dans le cas de l'insertion d'enfant de mise à jour parent, il ne s'en charge pas, vous devez donc synchroniser parent et parentId par vous-même.
ChildEntity.java
@Entity
@Table(name = "parent_child")
@ToString(exclude = "parent")
@NoArgsConstructor
public class ChildEntity {
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class PK implements Serializable {
@Column(name = "parent_id")
private int parentId;
@Column(name = "value_")
private String value;
}
@EmbeddedId
@Getter
@Setter
private PK pk;
@ManyToOne
@JoinColumn(name = "parent_id", referencedColumnName = "id")
@MapsId("parentId")
@Getter
@Setter
private ParentEntity parent;
public ChildEntity(@NonNull final ParentEntity parent,
@NonNull final String value) {
this.pk = new PK(parent.getId(), value);
this.parent = parent;
}
public void setParent(@NonNull final ParentEntity parent) {
this.pk.setParentId(parent.getId());
this.parent = parent;
}
}
Notez qu'une utilisation imprudente de lombok.ToString, lombok.EqualsAndHashCode, Jackson, etc. provoquera une boucle infinie car le parent et l'enfant sont des références circulaires.
ParentEntity
Le côté parent n'est pas différent de la normale @ OneToMany
(lors de l'utilisation de clés de substitution).
ParentEntity.java
@Entity
@Table(name = "parent")
@Data
public class ParentEntity {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@OneToMany(mappedBy = "parent", orphanRemoval = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@Fetch(FetchMode.SUBSELECT)
private List<ChildEntity> children;
@Column(name = "bar")
private String bar;
}
Notez que si vous ne définissez pas orphanRemoval dans le paramètre de @ OneToMany
, l'enregistrement enfant ne sera pas supprimé même si vous supprimez l'enfant du côté parent.
De plus, si vous n'ajoutez pas @Fetch (FetchMode.SUBSELECT)
, le problème N + 1 se produira.
Hibernate 5.0.12 ressemble à ceci. Lors de l'insertion du ChildEntity nouvellement ajouté, je crains qu'il ne soit sélectionné un par un séparément de la liste acquise avec ParentEntity. Pour les éléments qui n'ont pas changé, la récupération EAGER a été effectuée, je me demande donc s'il y a un problème dans ce projet.
select parententi0_.id as id1_0_0_, parententi0_.bar as bar2_0_0_, children1_.parent_id as parent_i1_1_1_, children1_.value_ as value_2_1_1_, children1_.parent_id as parent_i1_1_2_, children1_.value_ as value_2_1_2_ from parent parententi0_ left outer join parent_child children1_ on parententi0_.id=children1_.parent_id where parententi0_.id=?
binding parameter [1] as [INTEGER] - [5]
select childentit0_.parent_id as parent_i1_1_0_, childentit0_.value_ as value_2_1_0_ from parent_child childentit0_ where childentit0_.parent_id=? and childentit0_.value_=?
binding parameter [1] as [INTEGER] - [5]
binding parameter [2] as [VARCHAR] - [0.42733237750132513]
insert into parent_child (parent_id, value_) values (?, ?)
binding parameter [1] as [INTEGER] - [5]
binding parameter [2] as [VARCHAR] - [0.42733237750132513]
delete from parent_child where parent_id=? and value_=?
binding parameter [1] as [INTEGER] - [5]
binding parameter [2] as [VARCHAR] - [0.8694858141499555]