Kann jemand mappedBy in JPA und Hibernate erklären?


174

Ich bin neu im Winterschlaf und muss Eins-zu-Viele- und Viele-zu-Eins-Beziehungen verwenden. Es ist eine bidirektionale Beziehung in meinen Objekten, so dass ich aus beiden Richtungen durchqueren kann. mappedByist der empfohlene Weg, aber ich konnte es nicht verstehen. Kann jemand erklären:

  • Was ist die empfohlene Art, es zu verwenden?
  • Welchen Zweck löst es?

Für mein Beispiel sind hier meine Klassen mit Anmerkungen:

  • Airline BESITZT viele AirlineFlights
  • Viele AirlineFlights gehören zu EINEM Airline

Fluggesellschaft :

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

BEARBEITEN:

Datenbankschema:

AirlineFlights hat die IDAirline als ForeignKey und Airline hat keine IDAirlineFlights. Dies macht AirlineFlights als Eigentümer / identifizierende Einheit?

Theoretisch möchte ich, dass die Fluggesellschaft Eigentümerin von airlineFlights ist.

Geben Sie hier die Bildbeschreibung ein

Antworten:


150

Durch Angabe der @JoinColumnbei beiden Modellen haben Sie keine wechselseitige Beziehung. Sie haben zwei Einwegbeziehungen und eine sehr verwirrende Abbildung davon. Sie sagen beiden Modellen, dass sie die IDAIRLINE-Spalte "besitzen". Wirklich nur einer von ihnen sollte es tatsächlich! Das 'normale' Ding ist, @JoinColumndie @OneToManySeite komplett abzunehmen und stattdessen mappedBy zum hinzuzufügen @OneToMany.

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

Das sagt Hibernate: "Sehen Sie sich die Bean-Eigenschaft mit dem Namen 'Airline' an, um die Konfiguration zu finden."


1
Ich bin ein wenig verwirrt von Ihrer Beschreibung am Ende über mappedBy. Ist es wichtig, wie die Dinge in db organisiert sind? @DB: AirlineFlights hat idAirline als Fremdschlüssel. Die Fluggesellschaft hat nur idAirline als Primärschlüssel und enthält keine Informationen zu AirlineFlights @ DB.
Brainydexter

10
Ja, das ist wichtig. Der Name in mappedBy teilt Hibernate mit, wo sich die Konfiguration für die JoinColumn befindet. (Auf dem getAirline () -Methode von AirlineFlight.) Die Art und Weise abgebildet Sie den JoinColumn auf Fluglinie setzen, Sie Airline sagen , dass es ist für die Aufrechterhaltung der Werte über in der anderen Tabelle verantwortlich. Es ist möglich, einer Entität mitzuteilen, dass sie eine Spalte in einer anderen Tabelle "besitzt" und für deren Aktualisierung verantwortlich ist. Dies ist normalerweise nicht der Fall und kann zu Problemen bei der Ausführung von SQL-Anweisungen führen.
Affe

Bitte beachten Sie die Bearbeitung. Auf DB-Ebene besitzt die AirlineFlight-Tabelle idAirline als Fremdschlüsselspalte. Daher sollte JoinColumn in der entsprechenden ManytoOne in die airlineFlight-Klasse / -Tabelle aufgenommen werden, da es diese Spalte besitzt.
Brainydexter

Ja, ich würde empfehlen, es so zu machen. Dies ist die am wenigsten komplizierte Option, und Sie scheinen nichts anderes zu benötigen.
Affe

"Nehmen Sie die @ JoinColumn vollständig von der @ OneToMany-Seite", meinen Sie von der @ManyToOneSeite, richtig?
nbro

284

MappedBy-Signale halten den Ruhezustand fest, dass sich der Schlüssel für die Beziehung auf der anderen Seite befindet.

Dies bedeutet, dass, obwohl Sie zwei Tabellen miteinander verknüpfen, nur eine dieser Tabellen eine Fremdschlüsseleinschränkung für die andere hat. Mit MappedBy können Sie weiterhin eine Verknüpfung von der Tabelle, die die Einschränkung enthält, zur anderen Tabelle herstellen.


3
Kannst du bitte etwas mehr klarstellen?
Alexander Suraphel

1
@ Kurt Du Bois, warum sollten Sie mappedByeine bidirektionale Definition verwenden, anstatt sie einzuschränken (mit Foreign_key-Einschränkungen auf jeder Seite)?
Kevin Meredith

6
Denn manchmal macht es einfach keinen Sinn, auf beiden Seiten einen Schlüssel anzubringen. Angenommen, Sie haben eine Firma und ein tragbares Gerät. Ein tragbares Gerät gehört nur einem Unternehmen, ein Unternehmen verfügt jedoch über mehrere tragbare Geräte.
Kurt Du Bois

Es tut dem Redakteur leid, dass er meine Antwort zurückgesetzt hat, aber es gab tatsächlich überhaupt keinen Mehrwert in Ihrer Bearbeitung. Der letzte Satz ergab nicht einmal Sinn.
Kurt Du Bois

@KurtDuBois, das so zugeordnet ist, kommt nur ins Bild, wie Sie Ihre Datenbank erstellen, dh entweder wenn Sie mappedby verwenden oder nicht auf der Java-Seite in den Ruhezustand wechseln, verhält es sich ähnlich.

22

mappedbyspricht für sich selbst, es sagt dem Ruhezustand, dieses Feld nicht zuzuordnen. Es wird bereits von diesem Feld zugeordnet [name = "field"].
Feld ist in der anderen Entität (name of the variable in the class not the table in the database)..

Wenn Sie dies nicht tun, ordnet der Ruhezustand diese beiden Beziehungen zu, da es sich nicht um dieselbe Beziehung handelt

Daher müssen wir den Ruhezustand anweisen, das Mapping nur auf einer Seite durchzuführen und zwischen ihnen zu koordinieren.


ist mappedBy ist optional? Denn ohne Verwendung von mappedBy erhalte ich das gleiche Ergebnis, dh bidirektionale Objektzuordnung
Freelancer

Sie können on2many und many2one nicht verwenden, ohne mappedBy auf einer Seite zu verwenden. Für many2many müssen Sie mappedBy auf einer Seite verwenden
Charif DZ

Vielen Dank, dass Sie darauf hingewiesen haben, was der Attributwert bedeutet. Dies ist der Name des Felds in der anderen Tabelle.
Gab

2
Vielleicht spricht der Winterschlaf nicht immer für sich selbst, aber wenn er es tut, verwendet er zumindest Interpunktion
Amalgovinus

1
Für mich spricht es nicht für sich selbst; umgekehrt ist es sehr verwirrend. Schauen Sie sich einfach die Anzahl der Fragen an, was tatsächlich ist mappedByund inversedBy. Andere ORMs verwenden viel intelligenter belongsToMany, hasManyAttribute.
Jan Bodnar

12

mappedby = "Objekt einer Entität derselben Klasse, die in einer anderen Klasse erstellt wurde"

Hinweis: -Mapped by kann nur in einer Klasse verwendet werden, da eine Tabelle eine Fremdschlüsseleinschränkung enthalten muss. Wenn die Zuordnung von auf beiden Seiten angewendet werden kann, wird der Fremdschlüssel aus beiden Tabellen entfernt, und ohne Fremdschlüssel besteht keine Beziehung zwischen zwei Tabellen.

Hinweis: - Es kann für folgende Anmerkungen verwendet werden: - 1. @ OneTone 2. @ OneToMany 3. @ ManyToMany

Hinweis --- Es kann nicht für folgende Anmerkungen verwendet werden: - 1. @ ManyToOne

Eins zu eins: - Führen Sie auf jeder Seite des Mappings durch, aber nur auf einer Seite. Dadurch wird die zusätzliche Spalte der Fremdschlüsseleinschränkung in der Tabelle entfernt, auf die die Klasse angewendet wird.

Zum Beispiel. Wenn wir die Zuordnung von in der Employee-Klasse auf das Mitarbeiterobjekt anwenden, wird der Fremdschlüssel aus der Employee-Tabelle entfernt.


12

Tabellenbeziehung vs. Entitätsbeziehung

In einem relationalen Datenbanksystem sieht eine one-to-manyTabellenbeziehung wie folgt aus:

<code> Eins-zu-Viele </ code> -Tabellenbeziehung

Beachten Sie, dass die Beziehung auf der Spalte Fremdschlüssel (z. B. post_id) in der untergeordneten Tabelle basiert .

Es gibt also eine einzige Quelle der Wahrheit, wenn es darum geht, eine one-to-manyTabellenbeziehung zu verwalten.

Wenn Sie nun eine bidirektionale Entitätsbeziehung verwenden, die der one-to-manyzuvor gesehenen Tabellenbeziehung zugeordnet ist:

Bidirektionale <code> Eins-zu-Viele </ code> -Entitätszuordnung

Wenn Sie sich das obige Diagramm ansehen, sehen Sie, dass es zwei Möglichkeiten gibt, diese Beziehung zu verwalten.

In der PostEntität haben Sie die commentsSammlung:

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

In der PostCommentwird die postZuordnung wie folgt zugeordnet:

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

Da es zwei Möglichkeiten gibt, die Fremdschlüsselspalte darzustellen, müssen Sie definieren, welche Quelle die Wahrheit ist, wenn es darum geht, die Zuordnungsstatusänderung in die entsprechende Änderung des Fremdschlüsselspaltenwerts zu übersetzen.

MappedBy

Das mappedByAttribut gibt an, dass die @ManyToOneSeite für die Verwaltung der Fremdschlüsselspalte verantwortlich ist. Die Auflistung wird nur zum Abrufen der untergeordneten Entitäten und zum Kaskadieren von Statusänderungen der übergeordneten Entität an untergeordnete Entitäten verwendet (z. B. sollte durch Entfernen der übergeordneten Entitäten auch die untergeordneten Entitäten entfernt werden).

Synchronisieren Sie beide Seiten einer bidirektionalen Zuordnung

Selbst wenn Sie das mappedByAttribut definiert haben und die untergeordnete @ManyToOneZuordnung die Spalte Fremdschlüssel verwaltet, müssen Sie jetzt beide Seiten der bidirektionalen Zuordnung synchronisieren.

Der beste Weg, dies zu tun, besteht darin, diese beiden Dienstprogrammmethoden hinzuzufügen:

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

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

Die Methoden addCommentund stellen removeCommentsicher, dass beide Seiten synchronisiert sind. Wenn wir also eine untergeordnete Entität hinzufügen, muss die untergeordnete Entität auf die übergeordnete Entität verweisen, und die übergeordnete Entität sollte das untergeordnete Element in der untergeordneten Sammlung enthalten.

Weitere Informationen zum besten Synchronisieren aller bidirektionalen Entitätszuordnungstypen finden Sie in diesem Artikel .


0

Sie haben mit der ManyToOne-Zuordnung begonnen und dann die OneToMany-Zuordnung auch für die biDirektionale Methode festgelegt. Dann müssen Sie auf der OneToMany-Seite (normalerweise Ihre übergeordnete Tabelle / Klasse) "mappedBy" erwähnen (die Zuordnung erfolgt durch und in der untergeordneten Tabelle / Klasse), damit im Ruhezustand keine EXTRA-Zuordnungstabelle in der Datenbank erstellt wird (wie TableName = parent_child).

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.