在MySQL数据库中我们知道列与列之间如果是多对多的关系,系统会需要一张中间表来维护两个表之间的关系,我在这里使用员工employee和角色role来示例
先来一个员工表t_employee
再来一个角色表t_role
需要一个中间表来维护二者之间多对多的关系
启动IDEA 新建项目使用Mybatis框架开干
domain设计
由于Mybatis是手动来维护表与表之间的关系,这里多对多的关系就涉及到中间表t_employee_role是否需要设计domain实体类来进行维护,我在这里选择了不生成实体类使用Map来维护关系,很多时候Map的作用就相当于一个实体类。
EmployeeMapper.xml配置:可以使用generator来生成 基本的CRUD代码生成器会自动生成这里就不多做解释,主要看roles多对多的映射关系。
添加以下配置即可
<!--嵌套查询 由于roles是一个集合,多方使用collection来嵌套查询-->
<collection property="roles" column="id" select="cn.itsource.ssm.mapper.EmployeeMapper.queryRole"></collection>
<!--这里联表查询查出员工对应的角色对象-->
<select id="queryRole" resultType="cn.itsource.ssm.domain.Role">
SELECT r.* FROM t_employee_role er
JOIN t_role r ON er.role_id = r.id
JOIN t_employee e ON er.employee_id = e.id
WHERE e.id = #{id};
</select>
service层略过直接来测试一把
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class EmployeeTest {
@Autowired
private IEmployeeService employeeService;
@Test
public void testSelectAll(){
List<Employee> employees = employeeService.selectAll();
for (Employee employee : employees) {
System.out.println(employee);
}
}
}
控制台输出结果:表明联表查询已经成功
删除,这里有一张中间表也要删除对应数据,Mybatis已经没有级联删除了,所以我们需要手动去删除中间表的数据。
@Test
public void testDelete(){
Long id = 4L;
//删除id为4的员工数据,对应的角色也要删除
employeeService.delete(id);
}
这里的步骤需要先删除中间表,因为存在外键约束,只能够先删除中间表,才能够再删除员工表的数据,所以再delete方法里面需要写删除中间表的代码
//级联删除:先删除employee_role表、再删除t_employee表、
@Override
public void delete(Long id) {
//先删除employee_role表
employeeMapper.deleteEmployeeRoleByEmployeeId(id);
//再删除t_employee表
employeeMapper.delete(id);
}
需要再原来的EmployeeMapper接口里面新增这个方法来删除中间表
@Delete("delete from t_employee_role where employee_id = #{id}")
void deleteEmployeeRoleByEmployeeId(Long id);
到这里已经实现级联删除了
接下来进行添加、修改,此处前端页面部分省略,主要是后台部分。值得注意一点:在进行添加、修改时前台传递到后台的employee对象需要是这种JSON格式:
id:
name:张三
roles[0].id:1
roles[1].id:2
简单提一下如何将employee中的roles对象封装成roles[0].id:1、roles[1].id:2这种格式,在js代码中手动赋值成这种格式
//获取角色复选框
var roles = $("#roles input:checked");
//循环遍历角色复选框
$.each(roles,function (index,obj) {
//将每个复选框格式化成roles[0].id:1的形式,满足后台封装需求
employee["roles["+index+"].id"] = $(obj).val();
})
这是多对多关系中核心部分js代码,其他的基本代码这里就略过,接下来后台接收参数,准备添加
这里偷懒没有写前台页面;使用后台制造假数据来模拟真实情况
/**
* 此方法来模拟前台传过来的Employee对象
* @return
*/
public Employee getEmployee(){
Employee employee = new Employee();
List<Role> roles = new ArrayList<>();
Role role = new Role();
Role role1 = new Role();
role.setId(1L);
role1.setId(2L);
roles.add(role);
roles.add(role1);
employee.setName("佩奇");
employee.setRoles(roles);
return employee;
}
测试一把看employee对象是否是想要的格式
@Test
public void testSave(){
Employee employee = getEmployee();
System.out.println(employee);
}
控制台输出 Employee{id=null, name=‘张三’, roles=[Role{id=1, name=‘null’}, Role{id=2, name=‘null’}],roles这个list集合与之前前台要求传递的参数一致了roles[0].id:1、roles[1].id:2
接下来准备进行添加操作,由于是靠中间表来维护关系,这里需要使用Map来封装中间表的两个id字段
再进行添加操作时同样需要先添加员工表的数据再添加中间表的数据
我们需要再Employee类中添加一个方法来维护中间表两个字段
//此方法来处理中间表对象的关系
public List<Map<String,Long>> getRoleMap(){
List<Map<String,Long>> list = new ArrayList<Map<String, Long>>();
for (Role role : getRoles()) {
Map<String,Long> map = new HashMap<String, Long>();//这个Map就相当于一个domain对象
//与中间表employee_id对应
map.put("employeeId", getId());
//与中间表role_id对应
map.put("roleId",role.getId());
list.add(map);
}
return list;
}
@Test
public void testSave(){
//模拟前台传过来的数据
Employee employee = getEmployee();
//添加操作
employeeService.insert(employee);
}
再service层实现此方法时需要再进行保存中间表的操作
@Override
@Transactional
public void insert(Employee employee) {
//先保存员工
employeeMapper.insert(employee);
//再保存中间表
employeeMapper.saveEmployeeRole(employee.getRoleMap());
}
再EmployeeMapper接口中添加方法:
//保存中间表的操作
void saveEmployeeRole(List<Map<String,Long>> list);
再EmployeeMapper.xml中添加实现方法:
<!--保存中间表的操作-->
<insert id="saveEmployeeRole" parameterType="list">
INSERT INTO t_employee_role VALUES
<foreach collection="list" item="map" separator=",">
/*这里遍历的字段与Employee中维护中间表对应*/
(#{map.employeeId},#{map.roleId})
</foreach>
</insert>
运行测试
员工表
中间表
成功,实现级联添加
最后修改,添加修改步骤大致相同
@Test
public void testUpdate(){
//模拟前台传过来的数据
Employee employee = getEmployee();
//手动设置id执行修改操作
employee.setId(1L);
//修改
employeeService.updateByPrimaryKey(employee);
}
修改分为三个步骤,先修改员工表、再删除之前此员工对应中间表的相关数据、然后再添加修改之后该员工对应的中间表数据
@Override
@Transactional
public void updateByPrimaryKey(Employee employee) {
//修改员工数据
employeeMapper.updateByPrimaryKey(employee);
//先删除原来的中间表对应数据
employeeMapper.deleteEmployeeRoleByEmployeeId(employee.getId());
//再插入新的中间表
employeeMapper.saveEmployeeRole(employee.getRoleMap());
}
测试
看出员工表已经修改成功、再看看在中间表
中间表也修改成功,员工1对应的角色由原来的1和2变成了3和4.
到这里Mybatis中多对多映射关系的CRUD已经全部完成,需要特别注意一点就是参数传递和使用Map集合来代替domain的方式。