[JAVA] Ajouter @ManyToOne à une partie de la clé primaire composite dans Hibernate JPA

Ce volet horaire (≒ journal)

=> Oui, utilisons la clé primaire composée

Exemple de table

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.

SQL émis au moment de la mise à jour

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]

Recommended Posts

Ajouter @ManyToOne à une partie de la clé primaire composite dans Hibernate JPA
clé composite de mise en veille prolongée
Jusqu'à l'utilisation de Spring Data et JPA Part 2
Jusqu'à l'utilisation de Spring Data et JPA Part 1
Générez un numéro de série avec TableGenerator of Hibernate (JPA) et stockez-le dans l'ID de String.
Résumer les principaux points de démarrage avec JPA appris avec Hibernate
Élimine les tracas liés au traitement des tableaux C comme des tuples par Swift
Comment obtenir l'identifiant de la clé PRIMAY incrémentée automatiquement dans MyBatis