In a relational database in a many-to-many relationship, a row in table X can have more than one matching row in table Y, a row in table Y can have more than one matching row in table X.
In Object oriented programming, one instance of entity refers to multiple instances of another entity and an instance of that another entity refers to multiple instances of the other entity in the relation called many-to-many relation.
In this tutorial used the relationship between Engineering Branch and Subjects that are having each Engineering branch in a semester. In an Engineering college each Branch have many Subjects and each Subject may appear in many Engineering branch semester.
many-to-many Tables structure in Database :
what is join table?
A join table is a table in database that represents the multiple references of one or more table . In other words a join table is used to refer multiple records of one or more tables by using foreign key constraints. Below image illustrates more about many-to-many association.
Technologies Used in following example :
- JPA 2.1
- Hibernate 5.2.6
- MySql 8.0
- Maven 3
- Spring Tool Suite (STS) 3.9.8
- Java 1.8
Subject.java mapping :
/** * The persistent class for the Subject database table. * */ @Entity public class Subject implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO, generator = "native") @GenericGenerator(name = "native", strategy = "native") @Column(name="SUBJECT_ID") private int subjectId; @Column(name="SUBJECT_DESC") private String subjectDesc; @Column(name="SUBJECT_NAME") private String subjectName; //Setters and getters
Branch.java mapping :
/** * The persistent class for the BRANCH database table. * */ @Entity public class Branch implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO, generator = "native") @GenericGenerator(name = "native", strategy = "native") @Column(name="BRANCH_ID") private int branchId; @Column(name="BRANCH_NAME") private String branchName; @Column(name="BRANCH_SHORT_NAME") private String branchShortName; private String description; //Uni-directional many-to-many association to Subject @ManyToMany(cascade={CascadeType.ALL}) @JoinTable(name="BRANCH_SUBJECT", [email protected](name="BRANCH_ID"), [email protected](name="SUBJECT_ID"))//@JoinTable is used to map Join table in database private List<Subject> subjects; //Setters and getters
@JoinTable
- @JoinTable Used in the mapping of associations. It is specified on the owning side of an association.
- @JoinTable can be used with embeddable types as well.
- When a join table is used in mapping a relationship with an embeddable class on the owning side of the relationship, the containing entity rather than the embeddable class is considered the owner of the relationship.
To Understand more about @JoinTable mapping in entity associations see :
Key points to understand associations
many-to-many association UniDirectional testing
/** * JPA Many-To-Many UniDirectional * */ public class App { public static void main( String[] args ) { EntityManagerFactory emf = null; EntityManager entityManager = null; EntityTransaction transaction = null; try{ emf = Persistence.createEntityManagerFactory("jbd-pu"); entityManager = emf.createEntityManager(); transaction = entityManager.getTransaction(); transaction.begin(); Branch branch1 = getBranch1();//CSE Branch in Engineering Branch branch2 = getBranch2();//IT Branch in Engineering branch1.setSubjects(new ArrayList()); branch1.addSubject(getSubject1());//Software Engineering branch1.addSubject(getSubject2());//Distributed System branch2.setSubjects(new ArrayList()); branch2.addSubject(getSubject1());//Software Engineering branch2.addSubject(getSubject3());//Business Analysis and Optimization entityManager.persist(branch1); entityManager.persist(branch2); transaction.commit(); }catch(Exception e){ transaction.rollback(); e.printStackTrace(); }finally{ entityManager.close(); emf.close(); } } private static Subject getSubject1(){ Subject subject = new Subject(); subject.setSubjectName("Software Engineering"); subject.setSubjectDesc("Apply key aspects of software engineering processes for the development of a complex software system"); return subject; } private static Subject getSubject2(){ Subject subject = new Subject(); subject.setSubjectName("Distributed System"); subject.setSubjectDesc("Explore recent advances in distributed computing systems"); return subject; } private static Subject getSubject3(){ Subject subject = new Subject(); subject.setSubjectName("Business Analysis and Optimization"); subject.setSubjectDesc("understand the Internal and external factors that impact the business strategy"); return subject; } private static Branch getBranch1(){ Branch branch = new Branch(); branch.setBranchName("Computer Science and Engineering"); branch.setBranchShortName("CSE"); branch.setDescription("CSE department offers courses under ambitious curricula in computer science and computer engineering.."); return branch; } private static Branch getBranch2(){ Branch branch = new Branch(); branch.setBranchName("Information Technology"); branch.setBranchShortName("IT"); branch.setDescription("IT is the business side of computers - usually dealing with databases, business, and accounting"); return branch; } }
Output :
INFO - HHH000400: Using dialect: org.hibernate.dialect.MySQLInnoDBDialect INFO - HHH000397: Using ASTQueryTranslatorFactory Hibernate: insert into Branch (BRANCH_NAME, BRANCH_SHORT_NAME, description) values (?, ?, ?) Hibernate: insert into Subject (SUBJECT_DESC, SUBJECT_NAME) values (?, ?) Hibernate: insert into Subject (SUBJECT_DESC, SUBJECT_NAME) values (?, ?) Hibernate: insert into Branch (BRANCH_NAME, BRANCH_SHORT_NAME, description) values (?, ?, ?) Hibernate: insert into Subject (SUBJECT_DESC, SUBJECT_NAME) values (?, ?) Hibernate: insert into Subject (SUBJECT_DESC, SUBJECT_NAME) values (?, ?) Hibernate: insert into BRANCH_SUBJECT (BRANCH_ID, SUBJECT_ID) values (?, ?) Hibernate: insert into BRANCH_SUBJECT (BRANCH_ID, SUBJECT_ID) values (?, ?) Hibernate: insert into BRANCH_SUBJECT (BRANCH_ID, SUBJECT_ID) values (?, ?) Hibernate: insert into BRANCH_SUBJECT (BRANCH_ID, SUBJECT_ID) values (?, ?) INFO - HHH000030: Cleaning up connection pool [jdbc:mysql://localhost:3306/jpa_JBD]
Download Application – JPA-ManyToMany-UniDirection.zip (14 KB)
Hi,
I don’t want to create a subject entry each time the entitymanager persists. I want to save a branch and joining table with existing subject Id. Could you please explain how that becomes possible?
Hi Navya, you set all subjects which already exist and have the Id to Branch object and use entityManager.merge(branchObj) instead persist(branchObj). Refer Merging detached Entities
In a Branch entity, cascade is use as a CascadeType.ALL i.e. @ManyToMany(cascade={CascadeType.ALL}) . If we delete the Subject entity it would delete the Branch entity which would have referenced to another Subject entity as well so it should not have ALL cascade type, It would be Cascade.PERSIST and Cascade.Merge.
Please correct me If I am wrong
Yes, it’s true. Best practices, should not have ALL cascade type in many-to-many.