many2many
many2many也是比较常见的对象关系。在这里使用Student和Teacher对象来完成双向的many2many。对象设计如下:
//学生对象
public class Student {
private Long id;
private String name;
private List<Teacher> teachers = new ArrayList<Teacher>();
//getter & setter
}
//教师对象
public class Teacher {
private Long id;
private String name;
private List<Student> students = new ArrayList<Student>();
//getter & setter
}
many2many关系在数据表中需要第三方的中间表参与。而在处理many2many关系的时候,需要自己手动处理中间表的数据。中间表关系需要一对student+teacher参与才能完成保存操作。在mybatis中,一是可以采用map的方式来完成中间表的映射,但是用map的使用是类型不安全的,所以,更多的,是为中间表创建一个中间对象:
many2many双方的映射都是相同的,但是需要巧用中间对象的映射,而且需要注意对象的保存顺序,废话不多说,看映射文件://映射中间表对象,只是用来做保存和查询使用 public class StudentTeacher { private Long studentId; private Long teacherId; public StudentTeacher(Long studentId, Long teacherId) { super(); this.studentId = studentId; this.teacherId = teacherId; } //getter & setter }
<mapper namespace="cd.itcast.mybatis.many2many.StudentMapper"> <insert id="save" keyProperty="id" parameterType="Student" useGeneratedKeys="true"> INSERT INTO student(name) values (#{name}) </insert> <resultMap type="Student" id="studentmap"> <id column="id" property="id"/> <result column="name" property="name"/> <collection property="teachers" column="id" ofType="Teacher"> <id property="id" column="t_id"/> <result property="name" column="t_name"/> </collection> </resultMap> <select id="get" parameterType="long" resultMap="studentmap"> SELECT s.*,t.name as t_name,t.id as t_id FROM student s join student_teacher st on s.id = st.student_id join teacher t on t.id = st.teacher_id WHERE s.id = #{id} </select> <select id="list" resultMap="studentmap"> SELECT s.*,t.name as t_name,t.id as t_id FROM student s join student_teacher st on s.id = st.student_id join teacher t on t.id = st.teacher_id </select> <delete id="delete" parameterType="long"> DELETE FROM student WHERE id = #{id} </delete> </mapper> <mapper namespace="cd.itcast.mybatis.many2many.TeacherMapper"> <insert id="save" keyProperty="id" parameterType="Teacher" useGeneratedKeys="true"> INSERT INTO teacher(name) values (#{name}) </insert> <insert id="saveRelation" parameterType="StudentTeacher"> INSERT INTO student_teacher(student_id,teacher_id) VALUES (#{studentId},#{teacherId}) </insert> <resultMap type="Teacher" id="teachermap"> <id property="id" column="id"/> <result property="name" column="name"/> <collection property="students" column="id" ofType="Student"> <id property="id" column="s_id"/> <result property="name" column="s_name"/> </collection> </resultMap> <select id="get" parameterType="long" resultMap="teachermap"> SELECT t.*,s.name as s_name,s.id as s_id FROM teacher t join student_teacher st on t.id = st.teacher_id join student s on s.id = st.student_id WHERE t.id = #{id} </select> <select id="list" resultMap="teachermap"> SELECT t.*,s.name as s_name,s.id as s_id FROM teacher t join student_teacher st on t.id = st.teacher_id join student s on s.id = st.student_id </select> <delete id="delete" parameterType="long"> DELETE FROM teacher WHERE id = #{id} </delete> <delete id="deleteRelation" parameterType="StudentTeacher"> <if test="studentId != null"> DELETE FROM student_teacher WHERE student_id = #{studentId} </if> <if test="teacherId != null"> DELETE FROM student_teacher WHERE teacher_id = #{teacherId} </if> </delete> </mapper>
上面的映射文件很明确,有几个比较重要的点需要注意:
第一,都采用内联的映射,而且都没有完成循环映射。具体的原因看上面的one2many/many2one
第二,TeacherMapper中完成对中间表对象的保存和关系的删除,注意在保存和删除时候的顺序
第三,在删除关系映射中,使用判断来确定当前删除的是teacher还是student,根据关联对象的属性值,来确定删除的SQL
来看测试,在测试代码之前,为两个映射创建对应的Mapper接口:
public interface StudentMapper {
void save(Student s);
Student get(Long id);
List<</span>Student> list(RowBounds rb);
void delete(Long id);
}
public interface TeacherMapper {
void save(Teacher t);
void saveRelation(StudentTeacher st);
Teacher get(Long id);
List<</span>Teacher> list(RowBounds rb);
void delete(Long teacherId);
void deleteRelation(StudentTeacher st);
}
来看看测试:
@Test
public void testSave() {
//创建Student对象
Student s = new Student();
s.setName("s");
//创建Teacher对象
Teacher t = new Teacher();
t.setName("t");
//处理Student和Teacher的关系
s.getTeachers().add(t);
t.getStudents().add(s);
//得到对应的Mapper接口
SqlSession session = MyBatisUtil.openSession();
StudentMapper smapper = session.getMapper(StudentMapper.class);
TeacherMapper tmapper = session.getMapper(TeacherMapper.class);
//分别保存Teacher和Student实例
smapper.save(s);
tmapper.save(t);
//在保存完对象之后,需要创建中间表对象。注意顺序。
StudentTeacher st = new StudentTeacher(s.getId(), t.getId());
//保存中间表对象
tmapper.saveRelation(st);
session.commit();
session.close();
}
@Test
public void testGet() {
SqlSession session = MyBatisUtil.openSession();
StudentMapper mapper = session.getMapper(StudentMapper.class);
Student s = mapper.get(2l);
System.out.println(s.getName());
List<</span>Teacher> teachers = s.getTeachers();
for (Teacher t : teachers) {
System.out.println(t.getName());
}
session.close();
}
@Test
public void TestDeleteStudent(){
SqlSession session=MyBatisUtil.openSession();
//要删除Student必须先删除中间表和该Student有关系的数据
StudentTeacher st=new StudentTeacher(2l,null);
//获得TeacherMapper接口
TeacherMapper tmapper=session.getMapper(TeacherMapper.class);
//删除关系
tmapper.deleteRelation(st);
//获得StudentMapper接口
StudentMapper smapper=session.getMapper(StudentMapper.class);
//删除Student
smapper.delete(2l);
session.commit();
session.close();
}