Вопрос по java, orm, jpa, hibernate, one-to-many – В чем разница между @JoinColumn и mappedBy при использовании ассоциации JPA @OneToMany

443

В чем разница между:

@Entity
public class Company {

    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
    @JoinColumn(name = "companyIdRef", referencedColumnName = "companyId")
    private List<Branch> branches;
    ...
}

а также

@Entity
public class Company {

    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY, mappedBy = "companyIdRef")
    private List<Branch> branches;
    ...
}
Также смWhat is the owning side in an ORM mapping вопрос для действительно хорошего объяснения проблем. dirkt

Ваш Ответ

6   ответов
23

Эта статья, если вы используете@OneToMany аннотация с@JoinColumnтогда у вас есть однонаправленная ассоциация.

Если вы используете@OneToMany сmappedBy набор атрибутов, у вас есть двунаправленная связь, то есть вам нужно иметь@ManyToOne ассоциация на стороне ребенка, котораяmappedBy Рекомендации.

однонаправленный@OneToMany association does not perform very wellтак что вам следует избегать этого.

Вам лучше использоватьдвунаправленный@OneToMany which is more efficient.

Просто и лаконично. Это должно быть помечено как правильный ответ.
Оскароносный ответ!
Очень хорошо объяснено в вашей статье!
488

@JoinColumn указывает на то, что этот объект являетсяowner отношения (то есть: соответствующая таблица имеет столбец с внешним ключом к ссылочной таблице), тогда как атрибутmappedBy указывает, что сущность на этой стороне является обратной от отношения, а владелец находится в «другом» юридическое лицо. Это также означает, что вы можете получить доступ к другой таблице из класса, который вы аннотировали с помощью "mappedBy" (полностью двунаправленные отношения).

В частности, для кода в вопросе правильные аннотации будут выглядеть так:

@Entity
public class Company {
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "company")
    private List<Branch> branches;
}

@Entity
public class Branch {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "companyId")
    private Company company;
}
Это еще одна причина, по которой не нравятся ORM. Документация часто слишком изворотлива, и в моих книгах это блуждает на слишком большой волшебной территории. Я боролся с этой проблемой, и когда слово за словом следовало за@OneToOne, дочерние строки обновляются сnull в их столбце FKey, который ссылается на родителя.
в обоих случаях в филиале есть поле с идентификатором компании. Mykhaylo Adamovych
В таблице компаний нет столбца с внешним ключом для ссылочной таблицы - у Branch есть ссылка на компанию. Почему вы говорите "соответствующая таблица имеет столбец с внешним ключом для ссылочной таблицы"? ? Не могли бы вы объяснить еще несколько пожалуйста. Mykhaylo Adamovych
@MykhayloAdamovych Я обновил свой ответ с примером кода. Обратите внимание, что это ошибка в использовании@JoinColumn вCompany
@MykhayloAdamovych: Нет, это на самом деле не совсем верно. ЕслиBranch не имеет свойства, которое ссылаетсяCompany, но в базовой таблице есть столбец, который имеет, то вы можете использовать@JoinTable чтобы сопоставить это. Это необычная ситуация, потому что вы обычно сопоставляете столбец в объекте, который соответствует его таблице, но это может произойти, и это совершенно законно.
19

@JoinColumn не всегда должно быть связано сphysical information location какэтот ответ подсказывает. Вы можете объединить@JoinColumn с@OneToMany даже если родительская таблица не имеет табличных данных, указывающих на дочернюю таблицу.

Как определить однонаправленное отношение OneToMany в JPA

Однонаправленный OneToMany, нет обратного ManyToOne, нет таблицы соединения

Кажется, он доступен только вJPA 2.x+ хоть. Это полезно в ситуациях, когда вы хотите, чтобы дочерний класс просто содержал идентификатор родителя, а не полную ссылку на него.

вы правы, в JPA2 введена поддержка однонаправленных OneToMany без таблицы соединений
184

@JoinColumn could be used on both sides of the relationship. Вопрос был об использовании@JoinColumn на@OneToMany сторона (редкий случай). И дело здесь вphysical information duplication (название столбца) вместе сnot optimized SQL query that will produce some additional UPDATE statements.

В соответствии сдокументация:

посколькуmany to one are (почти) всегдаowner side двунаправленных отношений в спецификации JPA, связь «один ко многим» аннотируется @OneToMany (mappedBy = ...)

@Entity
public class Troop {
    @OneToMany(mappedBy="troop")
    public Set<Soldier> getSoldiers() {
    ...
}

@Entity
public class Soldier {
    @ManyToOne
    @JoinColumn(name="troop_fk")
    public Troop getTroop() {
    ...
} 

Отряд имеет двунаправленное отношение один-ко-многим с Солдатом через свойство отряда. Вы не должны (не должны) определять какое-либо физическое отображение на стороне mappedBy.

Чтобы отобразить двунаправленный один ко многим, сone-to-many side as the owning side, вы должны удалить элемент mappedBy и установить значение many в один @JoinColumn как вставляемое и обновляемое в значение false. Это решение не оптимизировано и будет производить некоторые дополнительные операторы UPDATE.

@Entity
public class Troop {
    @OneToMany
    @JoinColumn(name="troop_fk") //we need to duplicate the physical information
    public Set<Soldier> getSoldiers() {
    ...
}

@Entity
public class Soldier {
    @ManyToOne
    @JoinColumn(name="troop_fk", insertable=false, updatable=false)
    public Troop getTroop() {
    ...
}
это было принято "как есть" со страницы документации (жирный шрифт мой) Mykhaylo Adamovych
Я не могу понять, каким образом Troop может быть владельцем вашего второго фрагмента, Soldier по-прежнему является владельцем, поскольку он содержит внешний ключ, ссылающийся на Troop. (Я использую MySQL, я проверил с вашим подходом).
В вашем примере аннотацияmappedBy="troop" обратитесь к какому полю?
Если вы используете JPA 2.x, мой ответ ниже немного чище. Хотя я предлагаю попробовать оба маршрута и посмотреть, что делает Hibernate при генерации таблиц. Если вы работаете над новым проектом, выберите поколение, которое, по вашему мнению, соответствует вашим потребностям. Если вы работаете с устаревшей базой данных и не хотите менять структуру, выберите любую, соответствующую вашей схеме.
@Fractaliste аннотацияmappedBy="troop" относится к имуществу отряда в классе Soldier. В приведенном выше коде свойство не видно, потому что здесь Михайло его опускает, но вы можете определить его существование с помощью getTroop (). Проверьте ответÓscar López, это очень ясно, и вы получите точку.
28

mappedBy в идеале всегда следует использовать в родительской части (класс компании) двунаправленных отношений, в этом случае он должен быть в классе компании, указывая на переменную-член «компания»; Детский класс (Филиал класс)

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

ниже рабочий пример:

parent class , Company

@Entity
public class Company {


    private int companyId;
    private String companyName;
    private List<Branch> branches;

    @Id
    @GeneratedValue
    @Column(name="COMPANY_ID")
    public int getCompanyId() {
        return companyId;
    }

    public void setCompanyId(int companyId) {
        this.companyId = companyId;
    }

    @Column(name="COMPANY_NAME")
    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    @OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy="company")
    public List<Branch> getBranches() {
        return branches;
    }

    public void setBranches(List<Branch> branches) {
        this.branches = branches;
    }


}

child class, Branch

@Entity
public class Branch {

    private int branchId;
    private String branchName;
    private Company company;

    @Id
    @GeneratedValue
    @Column(name="BRANCH_ID")
    public int getBranchId() {
        return branchId;
    }

    public void setBranchId(int branchId) {
        this.branchId = branchId;
    }

    @Column(name="BRANCH_NAME")
    public String getBranchName() {
        return branchName;
    }

    public void setBranchName(String branchName) {
        this.branchName = branchName;
    }

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="COMPANY_ID")
    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }


}
Спасибо"Company class pointing to the member variable 'company' of the Child class (Branch class)"
5

шрам L & # xF3; pez. Этот ответ неточный!

Это не@JoinColumn что указывает на то, что эта сущность является владельцем отношений. Вместо этого это@ManyToOne аннотация, которая делает это (в своем примере).

Аннотации отношений, такие как@ManyToOne, @OneToMany а также@ManyToMany сказать JPA / Hibernate, чтобыcreate a mapping.  По умолчанию это делается через отдельную таблицу соединений.


@JoinColumn

The purpose of @JoinColumn is to create a join column if one does not already exist. If it does, then this annotation can be used to name the join column.


MappedBy

The purpose of the MappedBy parameter is to instruct JPA: Do NOT create another join table as the relationship is already being mapped by the opposite entity of this relationship.



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

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

Итак, в заключение:@JoinColumn либо создает новый столбец соединения, либо переименовывает существующий; в то время какMappedBy Параметр работает совместно с аннотациями отношений другого (дочернего) класса, чтобы создать сопоставление либо через таблицу соединений, либо путем создания столбца внешнего ключа в связанной таблице объекта-владельца.

Чтобы проиллюстрировать, какMapppedBy работает, рассмотрим код ниже. ЕслиMappedBy параметр должен был быть удален, тогда Hibernate фактически создал бы ДВА таблицы соединения! Зачем? Потому что во взаимоотношениях «многие ко многим» существует симметрия, а в Hibernate нет смысла выбирать одно направление над другим.

Поэтому мы используемMappedBy чтобы сказать Hibernate, мы выбралиthe other сущность, чтобы продиктовать отображение отношений между двумя сущностями.

@Entity
public class Driver {
    @ManyToMany(mappedBy = "drivers")
    private List<Cars> cars;
}

@Entity
public class Cars {
    @ManyToMany
    private List<Drivers> drivers;
}

Добавление @JoinColumn (name = & quot; driverID & quot;) в класс владельца (см. Ниже) предотвратит создание таблицы соединения и вместо этого создаст столбец внешнего ключа driverID в таблице Cars для создания сопоставления:

@Entity
public class Driver {
    @ManyToMany(mappedBy = "drivers")
    private List<Cars> cars;
}

@Entity
public class Cars {
    @ManyToMany
    @JoinColumn(name = "driverID")
    private List<Drivers> drivers;
}

Похожие вопросы