The Many-To-Many annotation is one of the most important annotations provided by the EclipseLink – JPA. It defines a many-valued association with ManyToMany multiplicity (See single-valued multiplicity and different kind of many-valued multiplicity). A newly concept has introduced such as (JoinTable) as well as a new annotation like (@JoinTable) when we are coming to introduce the @ManyToMany. At this tutorial we should cover the anatomy of the annotations that have been used for establishing a ManyToMany association, also we will provide you the anatomy of the database tables that are involved in this operation. This tutorial assumes that you’ve looked before on the @OneToOne and @OneToMany kind of associations, cause the samples that are used in this tutorial depend on the samples that was provided at the other kind of association.
Anatomy of @ManyToMany Annotation
- Target: fields (Including property get methods)
- Uses: @ManyToMany
- Arguments: The @ManyToMany annotation takes multiple arguments when it used and these are:
- targetEntity (Optional): It is used to determine the entity class that is the target of the association.
- cascade (Optional): It is used to determine the operations that must be cascaded to the target of the association.
- fetch (Optional): It is used to determine whether the association should be Lazily loaded or must be eagerly fetched. The EAGER strategy is a requirement on the persistence provider runtime that the associated entity must be eagerly fetched. The LAZILY strategy is just a hint to the persistence provider runtime.
- mappedBy (Optional): The field that owns the relationship. The mappedBy element is only specified on the inverse (non-owning) side of the association. This argument is very important one and it is will be clarified when we are coming to discuss the Bidirectional and Unidirectional.
Anatomy of @JoinTable Annotation
- Target: fields (Including property get methods)
- Uses: @JoinTable
- Arguments: The @JoinTable annotation takes multiple arguments when it used and these are:
- name (Optional): Used to specify the name of the join table.
- catalog (Optional): Used to specify the catalog of the table.
- schema (Optional): Used to specify the schema of the table.
- joinColumns (Optional): Used to specify the foreign key columns of the join table which reference the primary table of the entity owning the association (i.e. the owning side of the association)
- inverseJoinColumns (Optional): The foreign key columns of the join table which reference the primary table of the entity that doesn’t own the association (i.e. the inverse side of the association).
- uniqueConstraints (Optional): Unique constraints that are to be placed on the table. These are only used of table generation is in effect.
Anatomy of Database Relationship
First of all, let’s define the ManyToMany association from the database perspective to see the impact of that definition on our understanding of @ManyToMany annotation. The ManyToMany relationship happened when a single record in one table can relate to many records in another, and a single record in that second table can also relate to many records in the first. The implementation of the ManyToMany relation inside the database considers putting the primary key of the first table and primary key of the second table in a third table, that’s called JoinTable. Let’s we assume that we have set of projects inside an organization that the employees are working on to deliver. A certain project has many developers (employees) have been working on, and many developers from different departments also do a lot of work on a many projects (that’s results in a ManyToMany relationship). See the Figure 1.1 that shows you the suggested relations.
Figure 1.1
You’ve noticed the following at Figure 1.1:
- The ManyToMany relationship that relate the Employee and Project is achieved through a JoinTable named (Employee_Project) table.
- The Employee_Project table contains two foreign keys, one of them refers the Employee’s primary key and the other refers the Project’s Primary Key.
- No way to avoid the join table solution for achieving a ManyToMany association, cause if you’ve thought that you can achieve it through any other technique (like add a new columns, or comma separated values), you are almost getting a performance issue.
Required Persistence Configuration
There is no additional fragment of lines that should be added to achieve the ManyToMany association, except that line that should define a Project entity inside the persistence context.
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> <persistence-unit name="EclipseLink-JPA-Installation" transaction-type="RESOURCE_LOCAL"> <class>net.javabeat.eclipselink.data.Employee</class> <class>net.javabeat.eclipselink.data.Address</class> <class>net.javabeat.eclipselink.data.Phone</class> <class>net.javabeat.eclipselink.data.Project</class> <properties> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/JavaBeat"/> <property name="javax.persistence.jdbc.user" value="root"/> <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/> <property name="javax.persistence.jdbc.password" value="root"/> <property name="eclipselink.logging.level" value="OFF"/> </properties> </persistence-unit> </persistence>
Achieving a Unidirectional ManyToMany Association
A ManyToMany association has two sides, the owning side and the non-owning side, or inverse side. The join table is specified on the owning side.If the association is bidirectional, either side may be designated as the owning side. Let’s we assume that the Employee is the owner side and the assocaition that discussed here is a unidirectional one. Let’s see the Employee and Project entities
// The Employee entity package net.javabeat.eclipselink.data; import java.util.List; import javax.persistence.Basic; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.OneToOne; @Entity public class Employee { @Id private int employeeId; @Basic(optional=false) private String employeeName; @OneToMany(mappedBy="employee", cascade=CascadeType.ALL) private List<Phone> phones; @ManyToMany(cascade=CascadeType.ALL) @JoinTable( joinColumns={@JoinColumn(name="employeeId")}, inverseJoinColumns={@JoinColumn(name="projectId")} ) private List<Project> projects; @OneToOne(cascade=CascadeType.ALL) @JoinColumn(name="AddressId") private Address address; public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public int getEmployeeId() { return employeeId; } public void setEmployeeId(int employeeId) { this.employeeId = employeeId; } public String getEmployeeName() { return employeeName; } public void setEmployeeName(String employeeName) { this.employeeName = employeeName; } public List<Phone> getPhones() { return phones; } public void setPhones(List<Phone> phones) { this.phones = phones; } public List<Project> getProjects() { return projects; } public void setProjects(List<Project> projects) { this.projects = projects; } } // The Project entity package net.javabeat.eclipselink.data; import javax.persistence.Entity; import javax.persistence.Id; @Entity(name="Project") public class Project { @Id private int projectId; private String projectName; public int getProjectId() { return projectId; } public void setProjectId(int projectId) { this.projectId = projectId; } public String getProjectName() { return projectName; } public void setProjectName(String projectName) { this.projectName = projectName; } }
The above Java code sample shows you the following major points:
- The ManyToMany association that has achieved is a unidirectional ManyToMany association, so you’ve noticed that the Project entity doesn’t affected anymore.
- The Employee entity has the @ManyToMany annotation.
- The Employee entity has the @JoinTable, so the Employee entity considered the owner of the association.
- If you’ve missed the @JoinTable, or already use it in an empty form, you almost probably got a default (JPA generated) name of the join table. As well as the joinColumns and inverseJoinColumns would be named by the JPA.
- I have missed intentionally the name of the join table, cause the generated name by the JPA is identical to that name that we have.
- You are almost about getting an exception if you’ve missed the joinColumns or inverseJoinColumns that must be defined inside @JoinTable annotation. .
- In case you’ve used a collection defined using generics to specify the element type (attribute type), the associated target entity class doesn’t need to be specified; otherwise it must be specified. Try to remove the generics and you will get a compiler error tells you (Target entity isn’t defined).
Achieving a Bidirectional ManyToMany Association
To achieve a bidirectional association, you’ve a need to add a new property (employees) at the Project entity and annotate it with @ManyToMany. But this time, you’ve not use @JoinTable, cause the @JoinTable already used in the Employee entity. Also it is important to remember you that the @JoinTable is used only by the owner of the association.
package net.javabeat.eclipselink.data; import java.util.List; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.ManyToMany; @Entity(name="Project") public class Project { @Id private int projectId; private String projectName; @ManyToMany(mappedBy="projects",,cascade=CascadeType.ALL) private List<Employee> employees; public int getProjectId() { return projectId; } public void setProjectId(int projectId) { this.projectId = projectId; } public String getProjectName() { return projectName; } public void setProjectName(String projectName) { this.projectName = projectName; } public List<Employee> getEmployees() { return employees; } public void setEmployees(List<Employee> employees) { this.employees = employees; } }
- To achieve a bidirectional annotation, we’ve used an @ManyToMany annotation, but with a mappedBy attribute. As mentioned previously, the mappedBy should refer the attribute that hold the association details at the other side of association. In our case the Employee is the owner side and the (projects) attribute hold the details of the association.
- No need to change anything at the owner side of the associaiton.
Executable Sample
The following snippet of code shows you an executable application that persist a ManyToMany association.
public static void main(String [] args){ em.getTransaction().begin(); createEmployee(); em.getTransaction().commit(); } <span style="font-size: 12px; line-height: 18px;">public static void createEmployee(){</span> // Create an address entity Address address = new Address(); address.setAddressId(1); address.setAddressCountry("United Kingdom"); address.setAddressCity("London"); // Create an employee entity Employee employee = new Employee(); employee.setEmployeeId(1); employee.setEmployeeName("John Smith"); // Associate the address with the employee employee.setAddress(address); // Create a Phone entity Phone firstPhone = new Phone(); firstPhone.setPhoneId(1); firstPhone.setPhoneNumber("+221 4050 615"); firstPhone.setEmployee(employee); // Create a new phone entity Phone secondPhone = new Phone(); secondPhone.setPhoneId(2); secondPhone.setPhoneNumber("+221 4050 619"); // Use the old employee entity secondPhone.setEmployee(employee); // Create a list of phone List<Phone> phones = new ArrayList<Phone>(); phones.add(firstPhone); phones.add(secondPhone); // Create a Project entity Project project = new Project(); project.setProjectId(1); project.setProjectName("Nasa Project"); // Create a list of projects List<Project> projects = new ArrayList<Project>(); // add the project into the list projects.add(project); // Set the project into employee employee.setProjects(projects); // Set the phones into your employee employee.setPhones(phones); // Persist the employee em.persist(employee); }
Summary
This tutorial covers both use of @ManyToMany. The ManyToMany association is one of the important association that might be used in the Object-Relational Mapping implementations. To achieve a ManyToMany association you must create a new table called (JoinTable) that holding the primary keys for those tables that are going to refer each other. In case you are creating a unidirectional association, you just have to use the @ManyToMany annotation at any entity that you would consider it as owner of the relationship. The owner of the association should has the @ManyToMany and @JoinTable in case you wouldn’t rely on the default values that created by the JPA when you’ve omit the @JoinTable. The bidirectional association needs to use the mappedBy attribute at the inverse side of the association. Missing of @JoinTable makes your code error-prone, so we are preferring you mention it.