@ManyToMany 注解用来定义具有多对多多重性的多值关联。
每个多对多关系都有两个方面,关系的拥有方和非拥有方。连接表(中间表)在关系的拥有方指定。
如果关联是双向的,任何一方都可以被指定为关系的拥有方。
如果关系是双向的,则关系的非拥有方必须使用 @ManyToMany 注解的 mappingBy 属性来指定拥有方的关系字段或属性。例如:
public class Teacher { //... @ManyToMany(mappedBy = "teacherList") private List<Student> studentList; } public class Student { //... @ManyToMany private List<Teacher> teacherList; }
上面代码中,在 Teacher 中使用 mappedBy 指定多对多关系由 Student 中的 teacherList 进行维护。Student 则为关系的拥有方,关联表也由 Student 维护。
@ManyToMany 注解可以在包含在实体类中的可嵌入类中使用,以指定与实体集合的关系。
如果关系是双向的,并且包含可嵌入类的实体是关系的所有者,则非拥有方必须使用 @ManyToMany 注解的 mappingBy 属性来指定可嵌入类的关系字段或属性。
在 mappedBy 元素中必须使用点(“.”)符号语法来指示嵌入属性中的关系属性。与点符号一起使用的每个标识符的值是相应嵌入字段或属性的名称。
@ManyToMany 注解属性详解:
targetEntity:(可选)指定关联目标的实体类。只有当使用 Java 泛型定义集合值关系属性时才可选,默认为集合的参数化类型。否则,必须另外指定 targetEntity 属性。例如:
@ManyToMany private List<Student> students; @ManyToMany(targetEntity=Student.class) private List students;
cascade:(可选)指定级联到关联目标的操作。当目标集合是 java.util.Map 时,级联元素适用于映射值(即 Map 中的 value)。默认值为 {}
fetch:(可选)关联是应该延迟加载还是必须马上加载。EAGER 策略表示必须马上获取关联的实体,LAZY 策略表示用到关联对象时才去加载。默认值为 javax.persistence.FetchType.LAZY
mappedBy:拥有关系的字段,单向关系不需要指定改属性;双向关系必须指定改属性的值。默认值为空字符串,例如:教师和学生的关系,一个学生有多个老师,一个老师有多个学生。代码如下:
public class Teacher { //... @ManyToMany(mappedBy = "teacherList") private List<Student> studentList; } public class Student { //... @ManyToMany private List<Teacher> teacherList; }
上面代码中,在 Teacher 中使用 mappedBy 指定多对多关系由 Student 中的 teacherList 进行维护。执行它,会创建如下三张表:
CREATE TABLE `student` ( `id` int(11) NOT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `student_teacher` ( `STUDENTLIST_ID` int(11) DEFAULT NULL, `TEACHERLIST_ID` int(11) DEFAULT NULL, KEY `I_STDNCHR_ELEMENT` (`TEACHERLIST_ID`), KEY `I_STDNCHR_STUDENTLIST_ID` (`STUDENTLIST_ID`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `teacher` ( `id` int(11) NOT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
其中,student_teacher 表将由 Student 进行维护(新增、修改和删除);而 Teacher 是关系的被维护方。如果我们去掉 Teacher 中的 mappedBy 属性,JPA 将会创建四张表,分别如下:
CREATE TABLE `student` ( `id` int(11) NOT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `student_teacher` ( `STUDENTLIST_ID` int(11) DEFAULT NULL, `TEACHERLIST_ID` int(11) DEFAULT NULL, KEY `I_STDNCHR_ELEMENT` (`TEACHERLIST_ID`), KEY `I_STDNCHR_STUDENTLIST_ID` (`STUDENTLIST_ID`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `teacher` ( `id` int(11) NOT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `teacher_student` ( `TEACHER_ID` int(11) DEFAULT NULL, `STUDENTLIST_ID` int(11) DEFAULT NULL, KEY `I_TCHRDNT_ELEMENT` (`STUDENTLIST_ID`), KEY `I_TCHRDNT_TEACHER_ID` (`TEACHER_ID`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
此时,多对多的关系同时被 Teacher 和 Student 进行维护,显然维护两份关系是多余的,浪费资源。我们可以使用 mappedBy 属性放弃一方去放弃关系维护 @ManyToMany(mappedBy = "teacherList") 要求 Teacher 方放弃关系维护。