Dans quel cas utilisez-vous l'annotation JPA @JoinTable?

Réponses:

352

EDIT 2017-04-29 : Comme l'ont souligné certains des commentateurs, l' JoinTableexemple n'a pas besoin de l' mappedByattribut d'annotation. En fait, les versions récentes d'Hibernate refusent de démarrer en imprimant l'erreur suivante:

org.hibernate.AnnotationException: 
   Associations marked as mappedBy must not define database mappings 
   like @JoinTable or @JoinColumn

Supposons que vous ayez une entité nommée Projectet une autre entité nommée Tasket chaque projet peut avoir de nombreuses tâches.

Vous pouvez concevoir le schéma de base de données pour ce scénario de deux manières.

La première solution consiste à créer une table nommée Projectet une autre table nommée Tasket ajouter une colonne de clé étrangère à la table de tâches nommée project_id:

Project      Task
-------      ----
id           id
name         name
             project_id

De cette façon, il sera possible de déterminer le projet pour chaque ligne de la table des tâches. Si vous utilisez cette approche, dans vos classes d'entités, vous n'aurez pas besoin d'une table de jointure:

@Entity
public class Project {

   @OneToMany(mappedBy = "project")
   private Collection<Task> tasks;

}

@Entity
public class Task {

   @ManyToOne
   private Project project;

}

L'autre solution consiste à utiliser une troisième table, par exemple Project_Tasks, et à stocker la relation entre les projets et les tâches dans cette table:

Project      Task      Project_Tasks
-------      ----      -------------
id           id        project_id
name         name      task_id

La Project_Taskstable est appelée "Join Table". Pour implémenter cette deuxième solution dans JPA, vous devez utiliser l' @JoinTableannotation. Par exemple, pour implémenter une association un-à-plusieurs unidirectionnelle, nous pouvons définir nos entités comme telles:

Project entité:

@Entity
public class Project {

    @Id
    @GeneratedValue
    private Long pid;

    private String name;

    @JoinTable
    @OneToMany
    private List<Task> tasks;

    public Long getPid() {
        return pid;
    }

    public void setPid(Long pid) {
        this.pid = pid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Task> getTasks() {
        return tasks;
    }

    public void setTasks(List<Task> tasks) {
        this.tasks = tasks;
    }
}

Task entité:

@Entity
public class Task {

    @Id
    @GeneratedValue
    private Long tid;

    private String name;

    public Long getTid() {
        return tid;
    }

    public void setTid(Long tid) {
        this.tid = tid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Cela créera la structure de base de données suivante:

Diagramme ER 1

L' @JoinTableannotation vous permet également de personnaliser divers aspects de la table de jointure. Par exemple, si nous avions annoté la taskspropriété comme ceci:

@JoinTable(
        name = "MY_JT",
        joinColumns = @JoinColumn(
                name = "PROJ_ID",
                referencedColumnName = "PID"
        ),
        inverseJoinColumns = @JoinColumn(
                name = "TASK_ID",
                referencedColumnName = "TID"
        )
)
@OneToMany
private List<Task> tasks;

La base de données résultante serait devenue:

Diagramme ER 2

Enfin, si vous souhaitez créer un schéma pour une association plusieurs-à-plusieurs, l'utilisation d'une table de jointure est la seule solution disponible.

Behrang Saeedzadeh
la source
1
en utilisant la première approche, mon projet est rempli avec mes tâches et chaque tâche remplie avec le projet parent avant la fusion et fonctionne mais toutes mes entrées sont dupliquées en fonction du nombre de mes tâches. Un projet avec deux tâches est enregistré deux fois dans ma base de données. Pourquoi ?
MaikoID
MISE À JOUR Il n'y a pas d'entrées en double dans ma base de données, la mise en veille prolongée est sélectionnée avec la jointure externe gauche et je ne sais pas pourquoi ..
MaikoID
2
Je crois @JoinTable/@JoinColumnpeut être annoté sur le même champ avec mappedBy. Ainsi , l'exemple devrait être correct garder le mappedBydans Projectet déplacez le @JoinColumnvers Task.project (ou vice-versa)
Adrian Shum
2
Agréable! Mais j'ai une autre question: si la table de jointure a Project_Tasksbesoin de namede Taskplus, qui devient trois colonnes: project_id, task_id, task_name, comment y parvenir?
macemers
5
Je pense que vous n'auriez pas dû mapper sur votre deuxième exemple d'utilisation pour éviter cette erreurCaused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn:
karthik m
14

Il est également plus propre à utiliser @JoinTablelorsqu'une entité peut être l'enfant dans plusieurs relations parent / enfant avec différents types de parents. Pour donner suite à l'exemple de Behrang, imaginez qu'une tâche peut être l'enfant d'un projet, d'une personne, d'un département, d'une étude et d'un processus.

La tasktable doit -elle avoir 5 nullablechamps de clé étrangère? Je crois que non...

HDave
la source
14

C'est la seule solution pour mapper une association ManyToMany: vous avez besoin d'une table de jointure entre les deux tables d'entités pour mapper l'association.

Il est également utilisé pour les associations OneToMany (généralement unidirectionnelles) lorsque vous ne souhaitez pas ajouter de clé étrangère dans la table du côté plusieurs et ainsi le garder indépendant du côté un.

Recherchez @JoinTable dans la documentation Hibernate pour obtenir des explications et des exemples.

JB Nizet
la source
4

Il vous permet de gérer la relation plusieurs à plusieurs. Exemple:

Table 1: post

post has following columns
____________________
|  ID     |  DATE   |
|_________|_________|
|         |         |
|_________|_________|

Table 2: user

user has the following columns:

____________________
|     ID  |NAME     |
|_________|_________|
|         |         |
|_________|_________|

Join Table vous permet de créer un mappage en utilisant:

@JoinTable(
  name="USER_POST",
  joinColumns=@JoinColumn(name="USER_ID", referencedColumnName="ID"),
  inverseJoinColumns=@JoinColumn(name="POST_ID", referencedColumnName="ID"))

créera une table:

____________________
|  USER_ID| POST_ID |
|_________|_________|
|         |         |
|_________|_________|
slal
la source
1
Question: et si j'ai déjà ce tableau supplémentaire? Le JoinTable ne remplacera pas celui existant, non?
TheWandererr
@TheWandererr avez-vous trouvé la réponse à votre question? J'ai déjà une table de jointure
asgs
Dans mon cas, cela crée une colonne redondante dans la table d'appoint propriétaire. par exemple. POST_ID dans POST. Pouvez-vous suggérer pourquoi cela se produit?
SPS
0

@ManyToMany les associations

Le plus souvent, vous devrez utiliser une @JoinTableannotation pour spécifier le mappage d'une relation de table plusieurs à plusieurs:

  • le nom de la table de liens et
  • les deux colonnes de clé étrangère

Donc, en supposant que vous ayez les tables de base de données suivantes:

Relation de table plusieurs à plusieurs

Dans l' Postentité, vous mapperiez cette relation, comme ceci:

@ManyToMany(cascade = {
    CascadeType.PERSIST,
    CascadeType.MERGE
})
@JoinTable(
    name = "post_tag",
    joinColumns = @JoinColumn(name = "post_id"),
    inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private List<Tag> tags = new ArrayList<>();

L' @JoinTableannotation est utilisée pour spécifier le nom de la table via l' nameattribut, ainsi que la colonne Clé étrangère qui fait référence à la posttable (par exemple, joinColumns) et la colonne Clé étrangère dans la post_tagtable des liens qui fait référence à l' Tagentité via l' inverseJoinColumnsattribut.

Notez que l'attribut cascade de l' @ManyToManyannotation est défini sur PERSISTet MERGEuniquement parce que la mise en cascade REMOVEest une mauvaise idée puisque l'instruction DELETE sera émise pour l'autre enregistrement parent, tagdans notre cas, pas pour l' post_tagenregistrement. Pour plus de détails sur ce sujet, consultez cet article .

@OneToManyAssociations unidirectionnelles

Les @OneToManyassociations unidirectionnelles , qui n'ont pas de @JoinColumnmappage, se comportent comme des relations de table plusieurs-à-plusieurs, plutôt que comme des relations un-à-plusieurs.

Donc, en supposant que vous ayez les mappages d'entités suivants:

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @OneToMany(
        cascade = CascadeType.ALL,
        orphanRemoval = true
    )
    private List<PostComment> comments = new ArrayList<>();

    //Constructors, getters and setters removed for brevity
}

@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {

    @Id
    @GeneratedValue
    private Long id;

    private String review;

    //Constructors, getters and setters removed for brevity
}

Hibernate assumera le schéma de base de données suivant pour le mappage d'entités ci-dessus:

Tables de base de données d'association JPA unidirectionnelles <code> @OneToMany </code>

Comme déjà expliqué, le @OneToManymappage JPA unidirectionnel se comporte comme une association plusieurs-à-plusieurs.

Pour personnaliser la table des liens, vous pouvez également utiliser l' @JoinTableannotation:

@OneToMany(
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
@JoinTable(
    name = "post_comment_ref",
    joinColumns = @JoinColumn(name = "post_id"),
    inverseJoinColumns = @JoinColumn(name = "post_comment_id")
)
private List<PostComment> comments = new ArrayList<>();

Et maintenant, la table de lien va être appelée post_comment_refet les colonnes de clé étrangère seront post_id, pour la posttable, et post_comment_id, pour la post_commenttable.

Les @OneToManyassociations unidirectionnelles ne sont pas efficaces, il vaut donc mieux utiliser des @OneToManyassociations bidirectionnelles ou juste le @ManyToOnecôté. Consultez cet article pour plus de détails sur ce sujet.

Vlad Mihalcea
la source