jpa
От многих до многих
Поиск…
Вступление
Отображение ManyToMany описывает взаимосвязь между объектами, которые могут быть связаны с несколькими экземплярами друг друга, и определяется аннотацией @ManyToMany .
В отличие от @OneToMany где может использоваться столбец внешнего ключа в таблице объекта, ManyToMany требует таблицу соединений, которая сопоставляет сущности друг другу.
параметры
| аннотирование | Цель |
|---|---|
@TableGenerator | Определяет генератор первичных ключей, на который может ссылаться по имени, если для аннотации GeneratedValue указан элемент генератора |
@GeneratedValue | Обеспечивает спецификацию стратегий генерации для значений первичных ключей. Он может применяться к свойству первичного ключа или поле объекта или сопоставленному суперклассу в сочетании с аннотацией идентификатора. |
@ManyToMany | Определяет отношения между Employee и Project, так что многие сотрудники могут работать с несколькими проектами. |
mappedBy="projects" | Определяет двунаправленную связь между Employee и Project |
@JoinColumn | Указывает имя столбца, которое будет ссылаться на Объект, который будет считаться владельцем ассоциации |
@JoinTable | Задает таблицу в базе данных, в которой сотрудник связывается с проектами с использованием внешних ключей |
замечания
- @TableGenerator и @GeneratedValue используются для автоматического создания идентификатора с использованием генератора jpa-таблицы.
- Аннотации @ManyToMany определяют отношения между Employee и объектами Project.
- @JoinTable указывает имя таблицы для использования в качестве таблицы join jpa для многих сопоставлений между Employee и Project с использованием name = "employee_project". Это делается потому, что нет способа определить право собственности на сопоставление jpa many to many, поскольку таблицы базы данных не содержат внешних ключей для ссылки на другую таблицу.
- @JoinColumn указывает имя столбца, которое будет ссылаться на Entity, которое будет считаться владельцем ассоциации, а @inverseJoinColumn указывает имя обратной стороны отношения. (Вы можете выбрать любую сторону, которая будет считаться владельцем. Просто убедитесь, что эти стороны находятся в отношениях). В нашем случае мы выбрали Employee в качестве владельца, поэтому @JoinColumn ссылается на столбец idemployee в таблице соединений employee_project, а @InverseJoinColumn ссылается на idproject, который является обратной стороной jpa для многих сопоставлений.
- @ManyToMany аннотация в объекте Project показывает обратную связь, поэтому она использует mappedBy = projects для ссылки на поле в сущности Employee.
Полный пример можно отнести сюда
Сотрудник для сопоставления многих проектов
Сущность сотрудника.
package com.thejavageek.jpa.entities;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.TableGenerator;
@Entity
public class Employee {
@TableGenerator(name = "employee_gen", table = "id_gen", pkColumnName = "gen_name", valueColumnName = "gen_val", allocationSize = 100)
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "employee_gen")
private int idemployee;
private String name;
@ManyToMany(cascade = CascadeType.PERSIST)
@JoinTable(name = "employee_project", joinColumns = @JoinColumn(name = "idemployee"), inverseJoinColumns = @JoinColumn(name = "idproject"))
private List<Project> projects;
public int getIdemployee() {
return idemployee;
}
public void setIdemployee(int idemployee) {
this.idemployee = idemployee;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Project> getProjects() {
return projects;
}
public void setProjects(List<Project> projects) {
this.projects = projects;
}
}
Объект проекта:
package com.thejavageek.jpa.entities;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.TableGenerator;
@Entity
public class Project {
@TableGenerator(name = "project_gen", allocationSize = 1, pkColumnName = "gen_name", valueColumnName = "gen_val", table = "id_gen")
@Id
@GeneratedValue(generator = "project_gen", strategy = GenerationType.TABLE)
private int idproject;
private String name;
@ManyToMany(mappedBy = "projects", cascade = CascadeType.PERSIST)
private List<Employee> employees;
public int getIdproject() {
return idproject;
}
public void setIdproject(int idproject) {
this.idproject = idproject;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
}
Тестовый код
/ * Создать EntityManagerFactory * / EntityManagerFactory emf = Стойкость .createEntityManagerFactory ("JPAExamples");
/* Create EntityManager */
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();
Employee prasad = new Employee();
prasad.setName("prasad kharkar");
Employee harish = new Employee();
harish.setName("Harish taware");
Project ceg = new Project();
ceg.setName("CEG");
Project gtt = new Project();
gtt.setName("GTT");
List<Project> projects = new ArrayList<Project>();
projects.add(ceg);
projects.add(gtt);
List<Employee> employees = new ArrayList<Employee>();
employees.add(prasad);
employees.add(harish);
ceg.setEmployees(employees);
gtt.setEmployees(employees);
prasad.setProjects(projects);
harish.setProjects(projects);
em.persist(prasad);
transaction.commit();
Как обрабатывать составной ключ без встраиваемой аннотации
Если у вас есть
Role:
+-----------------------------+
| roleId | name | discription |
+-----------------------------+
Rights:
+-----------------------------+
| rightId | name | discription|
+-----------------------------+
rightrole
+------------------+
| roleId | rightId |
+------------------+
В приведенном выше сценарии таблица rightrole имеет составной ключ и для доступа к нему в JPA пользователю необходимо создать объект с Embeddable аннотацией.
Как это:
Объект для таблицы rightrole:
@Entity
@Table(name = "rightrole")
public class RightRole extends BaseEntity<RightRolePK> {
private static final long serialVersionUID = 1L;
@EmbeddedId
protected RightRolePK rightRolePK;
@JoinColumn(name = "roleID", referencedColumnName = "roleID", insertable = false, updatable = false)
@ManyToOne(fetch = FetchType.LAZY)
private Role role;
@JoinColumn(name = "rightID", referencedColumnName = "rightID", insertable = false, updatable = false)
@ManyToOne(fetch = FetchType.LAZY)
private Right right;
......
}
@Embeddable
public class RightRolePK implements Serializable {
private static final long serialVersionUID = 1L;
@Basic(optional = false)
@NotNull
@Column(nullable = false)
private long roleID;
@Basic(optional = false)
@NotNull
@Column(nullable = false)
private long rightID;
.....
}
Встраиваемая аннотация отлично подходит для одного объекта, но она будет выдавать проблему при вставке массовых записей.
Проблема заключается в том, когда пользователь хочет создать новую role с rights тогда первый пользователь должен store(persist) role объект, а затем пользователь должен выполнить flush для получения вновь созданного id для роли. тогда и тогда пользователь может поместить его в объект объекта rightrole .
Чтобы решить этот вопрос, пользователь может писать объект несколько иначе.
Объект для таблицы ролей:
@Entity
@Table(name = "role")
public class Role {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@NotNull
@Column(nullable = false)
private Long roleID;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "role", fetch = FetchType.LAZY)
private List<RightRole> rightRoleList;
@ManyToMany(cascade = {CascadeType.PERSIST})
@JoinTable(name = "rightrole",
joinColumns = {
@JoinColumn(name = "roleID", referencedColumnName = "ROLE_ID")},
inverseJoinColumns = {
@JoinColumn(name = "rightID", referencedColumnName = "RIGHT_ID")})
private List<Right> rightList;
.......
}
Аннотации @JoinTable позаботятся о вставке в таблицу rightrole даже без сущности (пока в этой таблице есть только столбцы id роли и права).
Затем пользователь может просто:
Role role = new Role();
List<Right> rightList = new ArrayList<>();
Right right1 = new Right();
Right right2 = new Right();
rightList.add(right1);
rightList.add(right2);
role.setRightList(rightList);
Вы должны написать @ManyToMany (cascade = {CascadeType.PERSIST}) в inverseJoinColumns, иначе ваши родительские данные будут удалены, если ребенок будет удален.