Может кто-нибудь объяснить mappedBy в JPA и Hibernate?

175

Я новичок в спящем состоянии, и мне нужно использовать отношения один-ко-многим и многие-к-одному. Это двунаправленные отношения в моих объектах, так что я могу перемещаться в любом направлении. mappedByэто рекомендуемый способ, но я не мог этого понять. Может кто-нибудь объяснить:

  • Каков рекомендуемый способ его использования?
  • с какой целью это решает?

Ради моего примера, вот мои занятия с аннотациями:

  • Airline СВОИХ МНОГО AirlineFlights
  • Многие AirlineFlights принадлежат ОДНОМУ Airline

Авиакомпания :

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

РЕДАКТИРОВАТЬ:

Схема базы данных:

У AirlineFlights есть idAirline как ForeignKey, а у Airline нет idAirlineFlights. Это делает AirlineFlights в качестве владельца / идентифицирующего лица?

Теоретически, я бы хотел, чтобы авиакомпания стала владельцем авиакомпании.

введите описание изображения здесь

brainydexter
источник

Ответы:

151

Указывая @JoinColumnна обе модели, вы не имеете двусторонних отношений. У вас есть два односторонних отношения и очень запутанное отображение этого. Вы говорите обеим моделям, что они "владеют" столбцом IDAIRLINE. На самом деле только один из них на самом деле должен! «Обычная» вещь состоит в том, @JoinColumnчтобы @OneToManyполностью убрать боковую сторону и вместо этого добавить mappedBy в @OneToMany.

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

Это говорит Hibernate: «Иди, посмотри на свойство bean, называемое« авиакомпания », на предмет, который у меня есть, чтобы найти конфигурацию».

Affe
источник
2
Я немного озадачен вашим описанием в конце о mappedBy. Имеет ли значение, как все организовано в БД? @DB: AirlineFlights имеет idAirline в качестве внешнего ключа. Авиакомпания просто имеет idAirline в качестве первичного ключа и не содержит информации о AirlineFlights @ DB.
Brainydexter
10
Да, это важно. Имя в mappedBy сообщает Hibernate, где найти конфигурацию для JoinColumn. (О методе getAirline () из AirlineFlight.) То , как вы замэпили положить JoinColumn на авиакомпании, вы сообщаете Авиакомпанию , что является ответственным за поддержание значений над в другой таблице. Можно сказать, что сущность «владеет» столбцом в другой таблице и отвечает за его обновление. Это не то, что вы обычно хотите сделать, и может вызвать проблемы с порядком выполнения операторов SQL.
Affe
Пожалуйста, смотрите редактирование. На уровне БД таблица AirlinesFlight владеет idAirline в качестве столбца внешнего ключа. Следовательно, JoinColumn должен быть помещен в класс / таблицу AirlinesFlight в соответствующем ManytoOne, так как ему принадлежит этот столбец?
Brainydexter
Да, я бы порекомендовал сделать это таким образом. Это наименее сложный вариант, и вам больше ничего не нужно.
Affe
«полностью уберите @JoinColumn со стороны @OneToMany», вы имеете в виду @ManyToOneсторону, верно?
августа
284

MappedBy сигнализирует, что ключ для отношений находится на другой стороне.

Это означает, что, хотя вы связываете 2 таблицы вместе, только одна из этих таблиц имеет ограничение внешнего ключа для другой. MappedBy позволяет вам по-прежнему ссылаться из таблицы, не содержащей ограничения, на другую таблицу.

Курт дю Буа
источник
3
не могли бы вы уточнить немного больше?
Александр Сурафел
1
@ Kurt Du Bois, почему вы используете, mappedByа не определяете двунаправленность (с ограничениями foreign_key на каждой стороне)?
Кевин Мередит
6
Потому что иногда просто не имеет смысла ставить ключ с обеих сторон. Например, у вас есть компания и портативный компьютер. Портативный компьютер будет принадлежать только одной компании, но у компании будет несколько портативных компьютеров.
Курт Дю Буа
Извините редактора за то, что он откатил мой ответ, но на самом деле в вашей правке не было никакой добавленной стоимости. Последнее предложение даже не имело смысла.
Курт Дю Буа
@KurtDuBois, так сопоставленный, приходит только к картине, как создать вашу базу данных, то есть либо вы используете mappedby, либо не впадаете в спящий режим на стороне Java, ведет себя аналогичным образом.
22

mappedbyговорит сам за себя, говорит hibernate не отображать это поле. это уже отображено этим полем [name = "field"].
поле находится в другом объекте (name of the variable in the class not the table in the database)..

Если вы этого не сделаете, Hibernate отобразит эти два отношения, поскольку это не то же самое отношение

поэтому мы должны сказать hibernate сделать отображение только в одну сторону и координировать их.

Charif DZ
источник
mappedBy не является обязательным? Потому что без использования mappedBy я получаю тот же результат, т. Е. Двунаправленное сопоставление объектов
Freelancer
Вы не можете использовать on2many и many2one без использования mappedBy в одной из сторон. Для many2many вы должны использовать mappedBy в одну сторону
Charif DZ
Спасибо, что указали, что означает значение атрибута, которое является именем поля в другой таблице.
Габ 是 好人
2
Может быть, спящий режим не всегда говорит сам за себя, но когда он это делает, по крайней мере, он использует знаки препинания
Amalgovinus
1
Для меня это не говорит само за себя; наоборот, это очень запутанно. Просто посмотрите на количество вопросов относительно того, что на самом деле mappedByи inversedBy. Другие OR использовать гораздо более умным belongsToMany, hasManyатрибуты.
Ян Боднар
12

mappedby = «объект сущности того же класса, созданный в другом классе»

Примечание: -Карта может использоваться только в одном классе, потому что одна таблица должна содержать ограничение внешнего ключа. если сопоставление может быть применено к обеим сторонам, тогда он удаляет внешний ключ из обеих таблиц, и без внешнего ключа нет никакой связи между двумя таблицами.

Примечание: - его можно использовать для следующих аннотаций: - 1. @ OneTone 2. @ OneToMany 3. @ ManyToMany

Примечание --- Его нельзя использовать для следующей аннотации: - 1. @ ManyToOne

Один в один: - Выполните на любой стороне сопоставления, но выполните только на одной стороне. Это удалит дополнительный столбец ограничения внешнего ключа в таблице, к которому класс применяется.

Например, Если мы применим отображение в классе Employee к объекту employee, внешний ключ из таблицы Employee будет удален.

кс гуджар
источник
12

Отношение таблицы против отношения сущности

В системе реляционных баз данных связь между one-to-manyтаблицами выглядит следующим образом:

Соотношение между таблицами <code> один-ко-многим </ code>

Обратите внимание, что связь основана на столбце внешнего ключа (например, post_id) в дочерней таблице.

Таким образом, существует единый источник правды, когда речь идет об управлении one-to-manyотношениями за столом.

Теперь, если вы берете двунаправленное отношение сущности, которое отображается в one-to-manyотношении таблицы, которое мы видели ранее:

Двунаправленная ассоциация <code> один-ко-многим </ code>

Если вы посмотрите на диаграмму выше, вы увидите, что есть два способа управления этими отношениями.

В Postсущности у вас есть commentsколлекция:

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

И, в PostComment, postассоциация отображается следующим образом:

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

Поскольку существует два способа представления столбца «Внешний ключ», необходимо определить источник истины, когда дело доходит до перевода изменения состояния ассоциации в эквивалентную модификацию значения столбца «Внешний ключ».

MappedBy

mappedByАтрибут говорит о том , что @ManyToOneсторона отвечает за управление столбцом внешнего ключа, а коллекция используется только для извлечения дочерних объектов и каскадных родительской сущность изменений состояния детей (например, удаление родителя должен также удалить дочерние объекты).

Синхронизировать обе стороны двунаправленной ассоциации

Теперь, даже если вы определили mappedByатрибут и дочерняя сторона @ManyToOneуправляет столбцом внешнего ключа, вам все равно нужно синхронизировать обе стороны двунаправленной ассоциации.

Лучший способ сделать это - добавить эти два служебных метода:

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

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

addCommentИ removeCommentметоды обеспечения того , чтобы обе стороны синхронизированы. Таким образом, если мы добавляем дочернюю сущность, дочерняя сущность должна указывать на родителя, а родительская сущность должна иметь дочерний объект, содержащийся в дочерней коллекции.

Для получения дополнительной информации о наилучшем способе синхронизации всех типов двунаправленных ассоциаций сущностей ознакомьтесь с этой статьей .

Влад Михалча
источник
0

Вы начали с сопоставления ManyToOne, а затем добавили сопоставление OneToMany для двунаправленного способа. Затем на стороне OneToMany (обычно это ваша родительская таблица / класс), вы должны упомянуть «mappedBy» (отображение выполняется и в дочерней таблице / классе), поэтому hibernate не будет создавать таблицу отображения EXTRA в БД (как TableName = parent_child).

Акшай Н. Шелке
источник