Qualcuno può spiegare mappedBy in JPA e Hibernate?


175

Sono nuovo in letargo e ho bisogno di usare relazioni uno-a-molti e molti-a-uno. È una relazione bidirezionale nei miei oggetti, in modo che io possa attraversare da entrambe le direzioni. mappedByè il modo consigliato per farlo, tuttavia, non riuscivo a capirlo. Qualcuno può spiegare:

  • qual è il modo raccomandato per usarlo?
  • quale scopo risolve?

Per il mio esempio, ecco le mie lezioni con annotazioni:

  • Airline PROPRIO molti AirlineFlights
  • Molti AirlineFlights appartengono a ONE Airline

Compagnia aerea :

@Entity 
@Table(name="Airline")
public class Airline {
    private Integer idAirline;
    private String name;

    private String code;

    private String aliasName;
    private Set<AirlineFlight> airlineFlights = new HashSet<AirlineFlight>(0);

    public Airline(){}

    public Airline(String name, String code, String aliasName, Set<AirlineFlight> flights) {
        setName(name);
        setCode(code);
        setAliasName(aliasName);
        setAirlineFlights(flights);
    }

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="IDAIRLINE", nullable=false)
    public Integer getIdAirline() {
        return idAirline;
    }

    private void setIdAirline(Integer idAirline) {
        this.idAirline = idAirline;
    }

    @Column(name="NAME", nullable=false)
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = DAOUtil.convertToDBString(name);
    }

    @Column(name="CODE", nullable=false, length=3)
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = DAOUtil.convertToDBString(code);
    }

    @Column(name="ALIAS", nullable=true)
    public String getAliasName() {
        return aliasName;
    }
    public void setAliasName(String aliasName) {
        if(aliasName != null)
            this.aliasName = DAOUtil.convertToDBString(aliasName);
    }

    @OneToMany(fetch=FetchType.LAZY, cascade = {CascadeType.ALL})
    @JoinColumn(name="IDAIRLINE")
    public Set<AirlineFlight> getAirlineFlights() {
        return airlineFlights;
    }

    public void setAirlineFlights(Set<AirlineFlight> flights) {
        this.airlineFlights = flights;
    }
}

AirlineFlights:

@Entity
@Table(name="AirlineFlight")
public class AirlineFlight {
    private Integer idAirlineFlight;
    private Airline airline;
    private String flightNumber;

    public AirlineFlight(){}

    public AirlineFlight(Airline airline, String flightNumber) {
        setAirline(airline);
        setFlightNumber(flightNumber);
    }

    @Id
    @GeneratedValue(generator="identity")
    @GenericGenerator(name="identity", strategy="identity")
    @Column(name="IDAIRLINEFLIGHT", nullable=false)
    public Integer getIdAirlineFlight() {
        return idAirlineFlight;
    }
    private void setIdAirlineFlight(Integer idAirlineFlight) {
        this.idAirlineFlight = idAirlineFlight;
    }

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="IDAIRLINE", nullable=false)
    public Airline getAirline() {
        return airline;
    }
    public void setAirline(Airline airline) {
        this.airline = airline;
    }

    @Column(name="FLIGHTNUMBER", nullable=false)
    public String getFlightNumber() {
        return flightNumber;
    }
    public void setFlightNumber(String flightNumber) {
        this.flightNumber = DAOUtil.convertToDBString(flightNumber);
    }
}

MODIFICARE:

Schema del database:

AirlineFlights ha idAirline come ForeignKey e Airline non ha idAirlineFlights. Questo fa di AirlineFlights il proprietario / entità identificativa?

Teoricamente, vorrei che la compagnia aerea fosse proprietaria dei voli della compagnia aerea.

inserisci qui la descrizione dell'immagine

Risposte:


151

Specificando @JoinColumnsu entrambi i modelli non si ha una relazione bidirezionale. Hai due relazioni a senso unico e una mappatura molto confusa di ciò. Stai dicendo a entrambi i modelli che "possiedono" la colonna IDAIRLINE. Davvero solo uno di loro dovrebbe davvero! La cosa 'normale' è togliere @JoinColumncompletamente il @OneToManylato e invece aggiungere mappedBy a @OneToMany.

@OneToMany(cascade = CascadeType.ALL, mappedBy="airline")
public Set<AirlineFlight> getAirlineFlights() {
    return airlineFlights;
}

Questo dice a Hibernate "Vai a dare un'occhiata alla proprietà bean denominata" compagnia aerea "sulla cosa di cui ho una raccolta per trovare la configurazione."


2
Sono un po 'confuso dalla tua descrizione alla fine di MappedBy. Importa come sono organizzate le cose in db? @DB: AirlineFlights ha idAirline come chiave esterna. La compagnia aerea ha solo idAirline come chiave primaria e non mantiene informazioni su AirlineFlights @ DB.
Brainydexter,

10
Sì, importa. Il nome in mappedBy indica a Hibernate dove trovare la configurazione per JoinColumn. (Sul metodo getAirline () di AirlineFlight.) Il modo in cui lo hai mappato mettendo JoinColumn sulla compagnia aerea, stai dicendo a Airline che è responsabile del mantenimento dei valori nell'altra tabella. È possibile dire a un'entità che "possiede" una colonna in una tabella diversa ed è responsabile dell'aggiornamento. Non è qualcosa che di solito vuoi fare e può causare problemi con l'ordine in cui vengono eseguite le istruzioni SQL.
Affeziona il

Si prega di vedere la modifica. A livello di DB, la tabella AirlineFlight possiede idAirline come colonna di chiave esterna. Quindi, JoinColumn dovrebbe essere messo su AirlineFlight class / table nel corrispondente ManytoOne, dal momento che possiede quella colonna?
Brainydexter,

Sì, consiglierei di farlo in questo modo. È l'opzione meno complicata e non sembra che tu abbia bisogno di nient'altro.
Affe,

"togli completamente @JoinColumn dal lato @OneToMany" intendi dire il @ManyToOnelato, giusto?
nbro,

284

MappedBy segnala che la chiave per la relazione è dall'altra parte.

Ciò significa che sebbene si colleghino 2 tabelle insieme, solo 1 di quelle tabelle ha un vincolo di chiave esterna rispetto all'altra. MappedBy ti consente di collegare ancora dalla tabella che non contiene il vincolo all'altra tabella.


3
puoi per favore chiarire un po 'di più?
Alexander Suraphel,

1
@Kurt Du Bois, perché dovresti usare mappedBypiuttosto che definire un bidirezionale (con vincoli Foreign_key su ciascun lato)?
Kevin Meredith,

6
Perché a volte non ha senso mettere una chiave su entrambi i lati. Supponiamo che tu abbia un'azienda e un portatile. Un portatile apparterrà a una sola società, ma una società avrà più portatili.
Kurt Du Bois,

Ci scusiamo con l'addetto alla modifica per il rollback della mia risposta, ma in realtà non c'era alcun valore aggiunto nella tua modifica. L'ultima frase non aveva nemmeno senso.
Kurt Du Bois,

@KurtDuBois così mappato da venire solo in foto come creare il tuo database, cioè o se stai usando mappedby o non ibernare sul lato Java si comporta in modo simile.

22

mappedbyparla da sé, dice al letargo di non mappare questo campo. è già mappato da questo campo [name = "field"].
campo è nell'altra entità (name of the variable in the class not the table in the database)..

Se non lo fai, l'ibernazione mapperà queste due relazioni poiché non è la stessa relazione

quindi dobbiamo dire a ibernazione di eseguire la mappatura solo su un lato e coordinarsi tra di loro.


is mappedBy è facoltativo? Perché senza usare mappedBy sto ottenendo lo stesso risultato, ad es. Mappatura bidirezionale degli oggetti
Freelancer,

non puoi usare on2many e many2one senza usare mappedBy in una delle stesse cose lato per many2many devi usare mappedBy in una parte
Charif DZ

Grazie per aver sottolineato cosa significa il valore dell'attributo che è il nome del campo nell'altra tabella.
Gab 是 好人

2
Forse l'ibernazione non parla sempre da sé, ma quando lo fa, almeno usa la punteggiatura
Amalgovinus

1
Per me, non parla da solo; al contrario, è molto confuso. Basta guardare la quantità di domande riguardanti ciò che realmente è mappedBye inversedBy. Altri ORM usano molto più intelligente belongsToMany, hasManyattributi.
Jan Bodnar,

12

mappedby = "oggetto di entità della stessa classe creata in un'altra classe”

Nota: -Mapped può essere utilizzato solo in una classe perché una tabella deve contenere un vincolo di chiave esterna. se mappato da può essere applicato su entrambi i lati, rimuove la chiave esterna da entrambe le tabelle e senza chiave esterna non esiste alcuna relazione tra due tabelle.

Nota: - può essere utilizzato per le seguenti annotazioni: - 1. @ OneTone 2. @ OneToMany 3. @ ManyToMany

Nota --- Non può essere utilizzato per le seguenti annotazioni: - 1. @ ManyToOne

In uno a uno: - Esegui su qualsiasi lato della mappatura ma esegui solo su un lato. Rimuoverà la colonna aggiuntiva del vincolo di chiave esterna sulla tabella su quale classe viene applicata.

Per es. Se applichiamo la mappatura nella classe Employee sull'oggetto Employee, la chiave esterna dalla tabella Employee verrà rimossa.


12

Relazione tra tabella e relazione tra entità

In un sistema di database relazionale, una one-to-manyrelazione tra tabelle ha il seguente aspetto:

Relazione tabella <code> one-to-many </code>

Si noti che la relazione si basa sulla colonna Chiave esterna (ad esempio, post_id) nella tabella figlio.

Quindi, c'è una sola fonte di verità quando si tratta di gestire una one-to-manyrelazione tra tavoli.

Ora, se prendi una relazione di entità bidirezionale che mappa sulla one-to-manyrelazione di tabella che abbiamo visto in precedenza:

Associazione bidirezionale <code> One-To-Many </code>

Se dai un'occhiata al diagramma sopra, puoi vedere che ci sono due modi per gestire questa relazione.

Nella Postentità, si ha la commentscollezione:

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

E, in PostComment, l' postassociazione è mappata come segue:

@ManyToOne(
    fetch = FetchType.LAZY
)
@JoinColumn(name = "post_id")
private Post post;

Poiché esistono due modi per rappresentare la colonna Chiave esterna, è necessario definire qual è la fonte della verità quando si tratta di tradurre la modifica dello stato dell'associazione nella modifica equivalente del valore della colonna Chiave esterna.

mappedBy

L' mappedByattributo dice che il@ManyToOne parte è incaricata di gestire la colonna Chiave esterna e la raccolta viene utilizzata solo per recuperare le entità figlio e per trasferire in cascata le modifiche dello stato dell'entità padre ai figli (ad esempio, la rimozione del genitore dovrebbe anche rimuovere le entità figlio).

Sincronizza entrambi i lati di un'associazione bidirezionale

Ora, anche se hai definito l' mappedByattributo e l' @ManyToOneassociazione lato figlio gestisce la colonna Chiave esterna, devi comunque sincronizzare entrambi i lati dell'associazione bidirezionale.

Il modo migliore per farlo è aggiungere questi due metodi di utilità:

public void addComment(PostComment comment) {
    comments.add(comment);
    comment.setPost(this);
}

public void removeComment(PostComment comment) {
    comments.remove(comment);
    comment.setPost(null);
}

I metodi addCommente removeCommentassicurano che entrambe le parti siano sincronizzate. Pertanto, se aggiungiamo un'entità figlio, l'entità figlio deve puntare al genitore e l'entità genitore dovrebbe avere il figlio contenuto nella raccolta figlio.

Per ulteriori dettagli sul modo migliore per sincronizzare tutti i tipi di associazione di entità bidirezionali, consulta questo articolo .


0

Hai iniziato con la mappatura ManyToOne, quindi hai inserito anche la mappatura OneToMany per il modo bidirezionale. Quindi sul lato OneToMany (di solito la tabella / classe parent), devi menzionare "mappedBy" (la mappatura viene eseguita da e nella tabella / classe child), quindi ibernazione non creerà una tabella di mappatura EXTRA nel DB (come TableName = parent_child).

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.