In tal caso, usi l'annotazione JPA @JoinTable?


Risposte:


350

EDIT 2017-04-29 : Come sottolineato da alcuni dei commentatori, l' JoinTableesempio non ha bisogno mappedBydell'attributo annotation. In effetti, le versioni recenti di Hibernate si rifiutano di avviarsi stampando il seguente errore:

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

Facciamo finta di avere un'entità nominata Projecte un'altra entità nominataTask e che ogni progetto possa avere molte attività.

È possibile progettare lo schema del database per questo scenario in due modi.

La prima soluzione è creare una tabella denominata Projecte un'altra tabella denominata Taske aggiungere una colonna chiave esterna alla tabella attività denominata project_id:

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

In questo modo, sarà possibile determinare il progetto per ogni riga nella tabella delle attività. Se usi questo approccio, nelle tue classi di entità non avrai bisogno di una tabella di join:

@Entity
public class Project {

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

}

@Entity
public class Task {

   @ManyToOne
   private Project project;

}

L'altra soluzione consiste nell'utilizzare una terza tabella, ad esempio Project_Tasks, e memorizzare la relazione tra progetti e attività in quella tabella:

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

La Project_Taskstabella si chiama "Unisci tabella". Per implementare questa seconda soluzione in JPA è necessario utilizzare l' @JoinTableannotazione. Ad esempio, al fine di implementare un'associazione uno-a-molti unidirezionale, possiamo definire le nostre entità in quanto tali:

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;
    }

}

Ciò creerà la seguente struttura del database:

Diagramma ER 1

L' @JoinTableannotazione consente inoltre di personalizzare vari aspetti della tabella di join. Ad esempio, avevamo annotato la tasksproprietà in questo modo:

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

Il database risultante sarebbe diventato:

Diagramma ER 2

Infine, se si desidera creare uno schema per un'associazione molti-a-molti, l'utilizzo di una tabella di join è l'unica soluzione disponibile.


1
usando il primo approccio ho riempito il mio progetto con le mie attività e ogni attività riempita con il progetto principale prima dell'unione e funziona, ma tutte le mie voci sono duplicate in base al numero delle mie attività. Un progetto con due attività viene salvato due volte nel mio database. Perché ?
MaikoID

AGGIORNAMENTO Non ci sono voci duplicate nel mio database, l'ibernazione sta selezionando con il join esterno sinistro e non so perché ...
MaikoID

2
Credo che @JoinTable/@JoinColumnpossa essere annotato sullo stesso campo con mappedBy. Quindi l'esempio corretto dovrebbe essere mantenendo l' mappedByin Project, e spostare il @JoinColumnverso Task.project (o viceversa)
Adrian Shum

2
Bello! Ma ho un'altra domanda: se il join tavolo Project_Tasksha bisogno namedi Taskcosì, che diventa tre colonne: project_id, task_id, task_name, come raggiungere questo obiettivo?
Macemer,

5
Penso che non avresti dovuto mappare Per il tuo secondo esempio di utilizzo per prevenire questo erroreCaused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn:
karthik m

14

È anche più pulito da usare @JoinTable quando un'entità potrebbe essere il bambino in diverse relazioni padre / figlio con diversi tipi di genitori. Per dare seguito all'esempio di Behrang, immagina che un'attività possa essere figlia di Progetto, Persona, Dipartimento, Studio e Processo.

La tasktabella dovrebbe avere 5 nullablecampi chiave esterna? Penso di no...


14

È l'unica soluzione per mappare un'associazione ManyToMany: è necessaria una tabella di join tra le due tabelle entità per mappare l'associazione.

Viene anche utilizzato per le associazioni OneToMany (generalmente unidirezionali) quando non si desidera aggiungere una chiave esterna nella tabella dei molti lati e quindi mantenerla indipendente da un lato.

Cerca @JoinTable nella documentazione di ibernazione per spiegazioni ed esempi.


4

Ti consente di gestire la relazione Many to Many. Esempio:

Table 1: post

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

Table 2: user

user has the following columns:

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

Unisci tabella ti consente di creare una mappatura usando:

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

creerà una tabella:

____________________
|  USER_ID| POST_ID |
|_________|_________|
|         |         |
|_________|_________|

1
Domanda: cosa succede se ho già questa tabella aggiuntiva? JoinTable non sovrascriverà quello esistente?
TheWandererr,

@TheWandererr hai trovato la risposta alla tua domanda? Ho un join tavolo già
ASGS

Nel mio caso sta creando una colonna ridondante nella tabella laterale proprietaria. per es. POST_ID in POST. Puoi suggerire perché sta accadendo?
SPS,

0

@ManyToMany associazioni

Molto spesso, sarà necessario utilizzare l' @JoinTableannotazione per specificare la mappatura di una relazione di tabella molti-a-molti:

  • il nome della tabella dei collegamenti e
  • le due colonne chiave esterna

Quindi, supponendo di avere le seguenti tabelle del database:

Rapporto molti-a-molti tavoli

Nella Postentità, si potrebbe mappare questa relazione, in questo modo:

@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' @JoinTableannotazione viene utilizzata per specificare il nome della tabella tramite l' nameattributo, nonché la colonna Chiave esterna che fa riferimento alla posttabella (ad es. joinColumns) E la colonna Chiave esterna nella post_tagtabella dei collegamenti che fa riferimento Tagall'entità tramite l' inverseJoinColumnsattributo.

Si noti che l'attributo cascade @ManyToManydell'annotazione è impostato su PERSISTe MERGEsolo perché il cascading REMOVEè una cattiva idea poiché noi l'istruzione DELETE verrà emessa per l'altro record padre, tagnel nostro caso, non per il post_tagrecord. Per maggiori dettagli su questo argomento, consulta questo articolo .

@OneToManyAssociazioni unidirezionali

Le @OneToManyassociazioni unidirezionali , che mancano a@JoinColumn mappatura, si comportano come relazioni da molti a molti piuttosto che da uno a molti.

Quindi, supponendo che tu abbia i seguenti mapping di entità:

@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 assumerà il seguente schema di database per il mapping delle entità sopra riportato:

Tabelle database unidirezionali <code> @OneToMany </code> JPA

Come già spiegato, la @OneToManymappatura unidirezionale JPA si comporta come un'associazione molti-a-molti.

Per personalizzare la tabella dei collegamenti, puoi anche utilizzare l' @JoinTableannotazione:

@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<>();

E ora verrà chiamata la tabella dei collegamenti post_comment_refe le colonne Chiave esterna saranno post_id, per la posttabella e post_comment_id, per la post_commenttabella.

Le @OneToManyassociazioni unidirezionali non sono efficienti, quindi è meglio usare le @OneToManyassociazioni bidirezionali o solo il @ManyToOnelato. Consulta questo articolo per maggiori dettagli su questo argomento.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.