1、自定义映射resultMap
复习:查询的标签select必须设置属性resultType或resultMap,用于设置实体类和数据库表的映射 关系
resultType:自动映射,用于属性名和表中字段名一致的情况 (或设置了下划线映射为驼峰)。
resultMap:自定义映射,用于一对多或多对一或字段名和属性名不一致的情况
1.1、resultMap处理字段和属性的映射关系
若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射。
(补充:当字段名和属性名一样时,字段和属性是一 一对应的,jdbc会通过反射实现这种对应,所以,能查到完整的对象。当不一样时,又没有自己处理,那字段和属性就对应不上,查出来的对象属性全是null)
<!--
select标签里的resultMap属性设置使用哪个映射规则
resultMap标签:设置自定义映射(自定义字段和属性的映射规则)
属性: id:表示自定义映射的唯一标识,即给你这个具体的映射规则resultMap起一个唯一名字
type:查询的数据要映射的实体类的类型
子标签:
id标签:设置主键的映射关系
result标签:设置普通字段的映射关系。即你想要你的结果是怎么映射的,怎么一一对应填值的
association标签:设置多对一的映射关系。比如Employee类中有一个Department属性
collection标签:设置一对多的映射关系。比如Department类中有一个。List<Employee>属性
属性:
property:设置映射关系中实体类中的属性名,必须是java中实体类的属性名
column:设置映射关系中的字段名.必须是sql查询出的某个字段(出现在sql查询语句的字段)
因为是自定义,所以,java中的属性名不是驼峰的也可,mysql里的属性不是_也可。
property和column的先后位置任意
-->
<resultMap id="userResultMap" type="User">
<id property="userId" column="userid"></id>
<result property="userName" column="user_name"></result>
<result property="password" column="password"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
</resultMap>
<!--List<User> testMohu(@Param("mohu") String mohu);使用刚才定义的映射规则-->
<select id="testMohu" resultMap="userResultMap">
<!--select * from t_user where username like '%${mohu}%'-->
select id,user_name,password,age,sex from t_user where user_name like concat('%',#{mohu},'%')
</select>
若字段名和实体类中的属性名不一致,但是字段名符合数据库的规则,实体类中的属性 名符合Java的规则, 此时也可通过以下3种方式处理字段名和实体类中的属性的映射关系
a>可以通过为字段起别名的方式,保证和实体类中的属性名保持一致
b>可以在MyBatis的核心配置文件中设置一个全局配置信息mapUnderscoreToCamelCase,可 以在查询表中数据时,自动将_类型的字段名转换为驼峰 (推荐)
例如:字段名user_name,设置了mapUnderscoreToCamelCase,此时字段名就会转换为 userName
c> 使用resultMap
1.2、多对一映射处理(对象属性)
场景模拟:
查询员工信息以及员工所对应的【部门信息】
员工表和部门表的关系是多对一
1.2.1、级联方式处理映射关系
多对一映射:多位员工Emp对应一个部门Dept
public class Emp {
private Integer empId;
private String empName;
private Integer age;
private String gender;
//查询员工对应的部门信息时,必须要在Emp类中写一个Dept属性。且提供 get、set,构造器不用加上dept
// 表与表有关系,在数据库中,员工表里有部门id字段。
//表与表之间的关系体现在java中,就是在类中加入对象属性。因为员工和部门是多对一的关系,所以,在员工类中
// 加一个Dept类型的属性。
//补充:若是一对多,那要在少的一方加一个集合,比如,在Dept里里加一个List<Emp>类型的属性.
///根据需求判断 只在一方加,还是两方都加。
private Dept dept;//这就是所谓的 对象属性
public Emp() {
}
public Emp(Integer empId, String empName, Integer age, String gender) {
this.empId = empId;
this.empName = empName;
this.age = age;
this.gender = gender;
}
。。。。。。
//提供所有属性的get、set方法,重写toString
}
public class Dept {
private Integer deptId;
private String depName;
public Dept() {
}
public Dept(Integer deptId, String depName) {
this.deptId = deptId;
this.depName = depName;
}
。。。。。。。。。。。。。。。。。。。。。。。
//提供所有属性的get、set方法,重写toString
}
Mapper接口:
public interface EmpMapper {
/**
* 查询指定员工的信息以及他所在部门的信息
* @param empId
* @return
*/
Emp getEmpAndDeptByEid(@Param("empId") int empId);
}
想明白字段该和哪个属性进行映射
因为dept是Emp类里的对象属性,所以,字段dept_name 对应 dept.depName
<resultMap id="empDeptMap" type="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
<!--这里仍然用result标签。需要部门表里的哪几些字段,这就写几个,但要保证java类中有属性,且提供了get、set方法。dept是Emp类中的属性,depName是dept对象的属性,实际调用getDeptName方法-->
<result column="dept_name" property="dept.depName"></result>
</resultMap>
<!--Emp getEmpAndDeptByEid(@Param("empId") int empId);-->
<select id="getEmpAndDeptByEid" resultMap="empDeptMap">
SELECT t_emp.*, t_dept.dept_name
FROM t_emp NATURAL JOIN t_dept
WHERE t_emp.emp_id=#{empId}
</select>
根据自己具体业务需求写SQL语句,有时候可能写成外连接。
1.2.2、使用association处理映射关系(推荐)
同上。需要先在java类Emp中添加一个属性private Dept dept ;并提供get、set方法,构造器不用加上dept。重写toString。
association标签:处理多对一的映射关系(处理实体类类型的属性)。比如Employee类中有一个Department属性
用上这个标签更好理解,阅读也方便。我个人比较喜欢这种方式。
将映射文件写成下面这样:
<resultMap id="empDeptMap" type="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
<!--<result column="dept_name" property="dept.deptName"></result>-->
<!--<result column="dept_id" property="dept.deptId"></result>-->
<!--只有这发生了变化。-->
<association property="dept" javaType="Dept">
<!--也是需要哪些部门表中的字段,就写哪些。因为我的部门表里总共2个字段,而且查的是t_dept.*,所以,我这写了2个。 -->
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
</association>
</resultMap>
<!--Emp getEmpAndDeptByEid(@Param("empId") int empId);-->
<select id="getEmpAndDeptByEid" resultMap="empDeptMap">
SELECT t_emp.*, t_dept.*
FROM t_emp NATURAL JOIN t_dept
WHERE t_emp.emp_id=#{empId}
</select>
@Test
public void testEmpDept(){
SqlSession sqlSession = sqlSessionUtils.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp empAndDeptByEid = mapper.getEmpAndDeptByEid(1);
System.out.println(empAndDeptByEid);
//Emp{empId=1, empName='赵四', age=20, gender='男', dept=Dept{deptId=1, depName='a部门'}}
}
1.2.3、分步查询
文字记录不太清楚
去看尚硅谷杨博超老师的SSM视频。或其他学习资源
1.3、一对多映射处理( 集合属性)
多对一反过来就是一对多,一个部门对应多个员工。需要在部门类中加一个List<Emp> emps
属性,提供get、set方法,toString。 构造器中不用加List<Emp> emps
。 处理一对多的映射关系:
-
方式一:collection
-
方式二:分步查询
public class Dept {
private Integer deptId;
private String deptName;
//一对多,一个部门里有多个员工
private List<Emp> emps;
..........
}
需求:根据部门id查询部门以及该部门中所有员工的信息
1.3.1、collection
根据具体业务需要写sql语句,看是否需要使用左外连接或右外连接. 例如下面的左外连接,因为有的人没有部门,但是又想查所有人。
SELECT *
FROM t_dept
LEFT JOIN t_emp ON t_dept.dept_id = t_emp.dept_id
WHERE t_dept.dept_id=#{deptId}
* 表示查询2表的所有字段
我的例子用的是自然连接:
SELECT * # 两个表的所有字段
FROM t_dept
NATURAL JOIN t_emp WHERE t_dept.dept_id=#{deptId}
DeptMapper接口里:
/**
* 根据部门id查新部门以及该部门中的员工信息
* @param did
* @return
*/
Dept getDeptAndEmpByDeptId(@Param("deptId") int deptId);
DeptMapper.xml 里:
<resultMap id="deptAndEmpResultMap" type="Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
<!--collection标签:处理一对多的关系映射(处理属性为集合类型的)。一个Dept对象里有多个Emp对象-->
<!--ofType属性:设置集合里面存储的数据类型是啥。存储的是Emp类型-->
<!--因为collection标签已经说明是了处理集合的,所以不能用javaType
collection是集合的意思,List<Emp>正好是一个集合,所以用来处理一对多-->
<collection property="emps" ofType="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
</collection>
</resultMap>
<!--Dept getDeptAndEmpByDeptId(@Param("deptId") int deptId);-->
<select id="getDeptAndEmpByDeptId" resultMap="deptAndEmpResultMap">
SELECT t_dept.*,t_emp.*
FROM t_dept
NATURAL JOIN t_emp WHERE t_dept.dept_id=#{deptId}
</select>
@Test
public void testGetDeptAndEmpByDeptId(){
SqlSession sqlSession = sqlSessionUtils.getSqlSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept deptAndEmpByDeptId = mapper.getDeptAndEmpByDeptId(1);
System.out.println(deptAndEmpByDeptId);
}
DEBUG 09-16 19:51:25,389 ==> Preparing: SELECT t_dept.*,t_emp.* FROM t_dept NATURAL JOIN t_emp WHERE t_dept.dept_id=? (BaseJdbcLogger.java:137)
DEBUG 09-16 19:51:25,421 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137)
DEBUG 09-16 19:51:25,455 <== Total: 2 (BaseJdbcLogger.java:137)
Dept{deptId=1, deptName='a部门',
emps=[
Emp{empId=1, empName='aaaa', age=20, gender='男', dept=null},
Emp{empId=2, empName='bbbb', age=23, gender='男', dept=null}
]
}
个人理解:
association 是“关系,关联”的意思,专门用来 负责属性为实体类对象(非List集合)时的映射规则,
collection 是“集合”的意思,专门用来负责属性为List<xxx> 时的映射规则。
所以,association负责“多对一”,collection负责”一对多“。
1.3.2、分步查询
去看尚硅谷杨博超老师的SSM视频。或其他学习资源