Аннотация @ManyToMany

Аннотация @ManyToMany определяет отношение многие ко многим — когда объект Entity класса с одной стороны содержит коллекцию объекты Entity классов другой стороны. В свою очередь объекты Entity классов со второй стороны могут содержать коллекцию объектов Entity классов первой стороны.

Все отношения в Java являются однонаправленными, поскольку если исходный объект ссылается на коллекцию целевых объект, нет гарантии, что целевые объекты также имеют ассоциации к исходным объектам. Это главное отличие от реляционных баз данных, когда отношения определяются с помощью внешних ключей и запросов, благодаря чему возможен запрос с обратной стороны ассоциации. Благодаря JPA (Hibernate) исправляется несоответствие парадигм между Java кодом и реляционными СУБД (DBMS).

Для всех отношений ManyToMany на уровне базы данных требуется таблица соединений — JoinTable. Entity класс JoinTable определяется с помощью аннотации @JoinTable или XML-элемента <join-table>. Класс JoinTable определяет внешний ключ к первичному ключу исходного объекта (joinColumns) и внешний ключ к первичному ключу целевого объекта (inverseJoinColumns). Обычно первичный ключ JoinTable является комбинацией обоих внешних ключей.

Рассмотрим отношение многие ко многим на примере Entity классов Country и Nationality. Сущность Country содержит информацию о станах, а сущность Nationality содержит информацию по национальностям. В стране могут проживать граждане разной национальности, в то же время люди определенной национальности могут быть гражданами разных стран. Как и в случае иных ассоциаций отношение многие ко многим (many-to-many) может быть однонаправленным или двунаправленным.

Однонаправленное отношение @ManyToMany (Example unidirectional @ManyToMany).

Для реализации однонаправленной ассоциации many-to-many для классов Country и Nationality создадим свойство [nationalities] на стороне Entity класса Country, представляющее собой перечень национальностей, проживающих на территории страны.

@Entity
@Table(name="COUNTRY")
public class Country implements Serializable {
	
	private static final long serialVersionUID = 1L;
	
	@Id
	@Column(name="ID_COUNTRY")
	private Long id;
	
	// полное наименование
	@Column(name="FULL_NAME", length = 100, nullable = false)
	private String fullName;
	
	// краткое наименование
	@Column(name="SHORT_NAME", length = 30, nullable = false)
	private String shortName;
	
	@ManyToMany
	@JoinTable(
		name="COMPANY_NATIONALITY",
		joinColumns=@JoinColumn(name="ID_COMPANY", referencedColumnName="ID_COMPANY"),
		inverseJoinColumns=@JoinColumn(name="ID_NATIONALITY", referencedColumnName="ID_NATIONALITY"))
	private List<Nationality> nationalities = new ArrayList<>();
	
	public Country() {
	}
	// Определение методов класса
	// не приводится для краткости
}
@Entity
@Table(name="NATIONALITY")
public class Nationality implements Serializable {
	
	private static final long serialVersionUID = 1L;
	
	@Id
	@Column(name="ID_NATIONALITY")
	private Long id;
	
	// наименование
	@Column(name="NAME", length = 50, nullable = false)
	private String name;
	
	public Nationality() {
	}
	// Определение методов класса
	// не приводится для краткости
}

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

 

Двунаправленное отношение @ManyToMany (Example bidirectional @ManyToMany).

Для реализации двунаправленной ассоциации many-to-many для классов Country и Nationality кроме свойства [nationalities] на стороне Entity класса Country, создадим свойство [countries] на стороне Entity класса Nationality представляющее собой перечень стран, на которых проживают граждане определенной национальности. Пример измененного Entity класса Nationality приведен ниже

@Entity
@Table(name="NATIONALITY")
public class Nationality implements Serializable {
	
	private static final long serialVersionUID = 1L;
	
	@Id
	@Column(name="ID_NATIONALITY")
	private Long id;
	
	// наименование
	@Column(name="NAME", length = 50, nullable = false)
	private String name;
	
	@ManyToMany (mappedBy="nationalities")
	private List<Country> countries = new ArrayList<>();
	
	public Nationality() {
	}
	
	// Определение методов класса
	// не приводится для краткости
}

 

Список опций аннотации @ManyToMany
опция тип значение по умолчанию описание
 cascade  CascadeType [] {}  Операции, которые должны быть каскадированы в цель ассоциации. По умолчанию никакие операции не выполняются каскадно.
 fetch FetchType   FetchType.EAGER Определяет как должны загружаться данные ассоциации: отложено или немедленно.
 mappedBy String   "" Поле, которому принадлежат отношения. Этот элемент указывается только на обратной (не принадлежащей) стороне ассоциации.
 targetEntity  java.lang.Class void.class Класс сущностей, который является целью ассоциации. По умолчанию используется тип поля или свойства, которое хранит эту ассоциацию.

Смотрите также примеры JPA аннотаций при использовании Hibernate: