在上一篇文章中,我们研究了一对一的关系。在这篇文章中,我们将研究一对多,多一对一的和多一对的关系。
多对一的关系
在多对一关系中,多个实体与其他一个实体完全关联。就数据库表而言,表中的多个记录可以与另一个表格中的一个记录相关联。
要更好地理解它,让我们创建一个称为分配的实体。分配实体包含以下成员变量。
@Entity
@Table(name = "grade_table")
public class Assignment {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String title;
@Temporal(value = TemporalType.DATE)
private Date dueDate;
}
这里的想法是,可以分配许多分配给一个学生。因此,我们可以建立学生和分配实体之间的许多与一段关系。
为此,我们将使用 @manytoone 注释。现在,问题是我们在哪个实体中进行注释?在学生实体或任务实体中。
在建模这类关系时,我们还需要了解表记录将如何受到影响,只有这样我们才能决定在哪个实体中放置注释。
让我们假设我们只是像一对一的关系中那样简单地放入学生实体。然后,预计学生表中有一个外国密钥列,上面有分配实体的主要密钥。由于学生无法在单列中存储多个键的一个以上的作业。
应该注意的是,多个任务将分配给一个学生。因此,我们可以将学生ID存储在分配表的“外键”列中。让我们现在就这样做
@Entity
@Table(name = "grade_table")
public class Assignment {
.
.
.
@ManyToOne
private Student assignedTo;
}
创建一些新的分配对象,并在数据库中坚持使用新关系。
Assignment assl = new Assignment();
assl.setTitle("JAVA");
assl.setDueDate(new Date());
assl.setAssignedTo(studentl) ;
Assignment ass2 = new Assignment();
ass2.setTitte("DSA");
ass2.setDueDate(new Date()) ;
ass2.setAssignedTo(studentl) ;
在数据库中,我们可以看到学生表没有更改。新作业表有两个分配,具有相同的学生ID。
一对一的关系
如果获取学生的详细信息,我们将无法将作业分配给学生。那是因为学生实体无法访问任务实体。正如我们在一对一关系中看到的那样,我们可以通过简单地反映学生实体的关系来解决这一问题。当镜像关系时,定义主要实体并自动成为镜像实体很重要。哪个实体成为主要的数据库设计和数据本身。我们在上一篇文章中讨论了它。检查它以更详细地了解它。
要反映学生实体中多对分的关系,我们需要一份作业列表,因为学生可能有很多作业。
@Entity
@Table(name = "Student_Table")
public class Student {
.
.
.
@OneToMany(mappedBy = "assignedTo")
private List<Assignment> assignmentList;
}
一对多,多一对镜像。有时,我们可能会对在哪个实体中使用哪种注释感到困惑。好吧,这是一个诀窍,在上面的示例中,学生实体具有许多任务实体。将其阅读为一个的学生拥有许多作业。因此 @Onetomany 注释。以同样的方式,任务实体将其读为许多作业,但一个sut剂时代 @manytoone 注释。
由于学生和分配实体互相反映了彼此的关系,因此请确保正确地在两个实体中格式化 tosting()方法,以使他们无法访问彼此的数据,从而导致无限循环。
学生详细信息中存在两个作业。现在,我们可以轻松地从学生数据中访问分配数据,反之亦然。
提取类型
在上一篇文章中,我们看到默认的获取类型很急切。但这并非如此,一对一和一对一的关系并非如此。默认的获取类型是懒惰。这是有道理的,因为我们不想与学生实体一起获取不同的多个实体。只有在访问分配数据时才获取分配数据。
请检查冬眠生成的查询以详细了解。
多一的关系
了解多对多的关系,让我们创建一个称为俱乐部的实体。俱乐部实体包含以下成员变量
@Entity
@Table(name = "Student_club")
public class Club {
@Id
@GeneratedValue
private int id;
private int name;
}
在多一关系中,一个表中的多个记录可以与另一表中的多个记录关联。在我们的示例中,学生可以成为许多俱乐部的一部分,并且俱乐部可以有很多学生。为了在数据库中表示这种关系,我们通常使用接线表或关联表。
在一对多和多一对一的关系的情况下,我们注意到表中的外键只能引用一个主要键。因此,我们将学生的主要键转介给了作业表的外键列。但是,在多一关系的情况下,不可能直接使用单个外键来建立两个实体之间的关联。这是因为两个实体都与彼此的多个实例相关联。
使用接线表方法解决了此问题。界面表是一个全新的表,可以充当两个实体之间的桥梁,并建立了多对多的关系。在交界表中,退出包含两个实体主要键的外键列。
现在让我们添加 @manytomany 注释我们想要的实体的成员变量。
@Entity
@Table(name = "Student_club")
public class Club {
@Id
@GeneratedValue
private int id;
private int name;
@ManyToMany
private List<Student> studentList;
}
@Entity
@Table(name = "Student_Table")
public class Student {
.
.
@ManyToMany
private List<Club> clubs;
}
如果您注意到,成员是类型列表,指示我们在这里建立的关系类型。我们可能会有会员注释的变量用 @manytomany 注释,但它并没有准确地建立我们想要的关系。
让我们创建几个俱乐部和学生对象,然后将它们保留在数据库中。通过这样做,我们可以观察到数据库表的确切发生了什么。
//Student 1 object
Student student1 = new Student();
student1.setName("Sohail Shah");
student1.setAge(18);
student1.setAdmission_no("1234");
student1.setDob(new Date());
student1.setStudentType(StudentType.SCHOLARSHIP);
//Student 2 object
Student student2 = new Student();
student2.setName("Ali");
student2.setAge(20);
student2.setAdmission_no("1235");
student2.setDob(new Date());
student2.setStudentType(StudentType.NON_SCHOLARSHIP);
//club 1 object
Club club1 = new Club();
club1.setName("Java Club");
club1.addStudent(student1);
club1.addStudent(student2);
student1.addClub(club1);
student2.addClub(club1);
//club 2 object
Club club2 = new Club();
club2.setName("Spring Club");
club2.addStudent(student2);
student2.addClub(club2);
student1.addClub(club1);
student1.addClub(club2);
student2.addClub(club1);
student2.addClub(club2);
//persisting the objects
entityManager.persist(student1);
entityManager.persist(student2);
entityManager.persist(club1);
entityManager.persist(club2);
运行应用程序后,在数据库中,我们将拥有两个不同的表格,这些表具有相同的数据或刚刚镜像的外键。
JPA不知道这两个表都包含相同的数据,并且正在跟踪它们两次。
通过在多到多的注释中使用Mappedby参数可以很容易地求解。我们也在其他关系中也使用了这一点。您可以将Mappapby放在任何实体中。
@ManyToMany(mappedBy = "clubs")
private List<Student> studentList = new ArrayList<>()
如果我们再次运行应用程序,则只有一个表包含两个实体的主要键。
确保正确使用
<property name="hibernate.hbm2ddl.auto" value="create"/>
,以查看正确的数据。如果遇到错误只需删除表并再次运行应用程序。
拿来
多对多关系的默认获取类型与一对多和多一对一的懒惰相同。您当然可以使用 fetch.eager 和 fetch.lazy
来更改此默认行为确定获取类型取决于您的数据库架构和需求。通常建议使用懒惰的类型来进行多一关系。