resultMap处理字段和属性的映射关系:
若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射
我们创建员工表如下所示:
我们创建的部门表如下所示:
多个员工对应一个部门,是一个多对一的关系。我们要把关系设置在多的地方,即员工这一方,我们需要在员工里面添加dept_id的这个字段。
员工表的数据如下所示:
部门表的数据如下所示:
之后我们创建实体类:
Empt员工类:
package com.rgf.pojo;
public class Emp {
private Integer empId;
private String empName;
private Integer age;
private String gender;
public Emp(Integer empId, String empName, Integer age, String gender) {
this.empId = empId;
this.empName = empName;
this.age = age;
this.gender = gender;
}
public Emp() {
}
public Integer getEmpId() {
return empId;
}
public void setEmpId(Integer empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Emp{" +
"empId=" + empId +
", empName='" + empName + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
}
Dept部门类:
package com.rgf.pojo;
public class Dept {
private Integer deptId;
private String deptName;
public Dept(Integer deptId, String deptName) {
this.deptId = deptId;
this.deptName = deptName;
}
public Dept() {
}
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
@Override
public String toString() {
return "Dept{" +
"deptId=" + deptId +
", deptName='" + deptName + '\'' +
'}';
}
}
我们创建完实体类之后,我们进行创建员工接口,及其配置文件:
EepMapper接口:
package com.rgf.mybatis.mapper;
public interface EmpMapper {
}
EmpMapper.xml接口:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rgf.mybatis.mapper.EmpMapper">
</mapper>
由于我们的表命名为t_emp,t_dept,而我们的类名为Emp和Dept,而且里面的emp_id(字段名)与empId(属性名)不一致。
使用全局配置处理字段名和属性名不一致的情况:
我们设置的方法如下所示:
//根据id查询员工信息
Emp getEmpByEmpId(@Param("empId") Integer empId);
我们设置的映射文件如下所示:
<select id="getEmpByEmpId" resultType="Emp">
select * from t_emp where emp_id=#{empId}
我们所写的测试类如下所示:
import com.rgf.mybatis.mapper.EmpMapper;
import com.rgf.mybatis.pojo.Emp;
import com.rgf.mybatis.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class ResultMapTest {
@Test
public void testGetEmpByEmpId(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpByEmpId(1);
System.out.println(emp);
}
}
我们运行之后如下所示:
我们发现字段名和属性名一致的话,是可以查出来的,里面的age,gender即查出来了。但是员工的id和名字和表里的字段名即不一致,即无法查找出来。
第一种方式:
我们将sql语句进行修改,我们发现如下所示:
<select id="getEmpByEmpId" resultType="Emp">
select emp_id empId,emp_name mepName, age,gender from t_emp where emp_id=#{empId}
</select>
我们运行出来如下所示:
我们运行出来后,我们发现在字段名和属性名一致的情况下可以查找到。
第二种方式:
我们进行修改配置文件,我们点开mybatis-3.5.10.pdf文件里面找到如下所示:
我们将我们的核心配置文件添加如下所示:
<!--mapUnderscoreToCamelCase映射下划线为驼峰,将下划线映射为驼峰-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--MyBatis核心配置文件中的标签必须要按照指定的顺序去配置:
properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?
-->
我们将我们的映射文件还原成如下所示:
<select id="getEmpByEmpId" resultType="Emp">
select * from t_emp where emp_id=#{empId}
我们重新测试后如下所示:
我们也可以将其添加到核心配置模板里面:
第三种方式:使用resultMap,自定义映射来实现:
我们先把如下所示写好,然后下来写resultMap
<select id="getEmpByEmpId" resultMap="emResultMap"> select * from t_user where emp_id=#{empId} </select>
<resultMap>里面常用的标签一共有四个,id来处理主键和属性的映射关系,result来处理普通字段和属性的映射关系,association来处理多对一,collection处理一对多。
我们进行查看主键的属性如下所示:
其中property为属性,column为字段,javaType:属性的类型,jdbcType
xml里面有个规则,注释是不能嵌套的。
我们的映射文件如下所示:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.rgf.mybatis.mapper.EmpMapper"> <!--字段名和属性名不一致的情况,如何来处理映射关系 1.为查询的字段设置别名,和属性名保持一致 2.当字段符合mysql的要求使用下划线_,而属性符合java的要求使用驼峰 此时可以在MyBatis的核心配置文件中设置一个全局配置,可以自动将下划线_映射为驼峰。 emp_id:empId,emp_name:empName, 3.使用resultMap自定义映射处理 --> <!-- resultMap:设置自定义的映射关系 id:唯一标识 type:处理映射关系的实体类的类型 常用的标签: id:处理主键和实体类中属性的映射关系 result:处理普通字段和实体类中属性的映射关系 column:设置映射关系中的字段名,必须是sql查询出的某个字段 property:设置映射关系中的属性的属性名,必须是处理的实体类类型中的属性名 我们需要通过id标签和result标签把咱们映射关系中的字段名通过column设置出来,映射关系中的属性名通过property属性设置出来。 --> <resultMap id="emResultMap" 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> </resultMap> <select id="getEmpByEmpId" resultMap="emResultMap"> select * from t_emp where emp_id=#{empId} </select> </mapper>
我们继续运行测试类如下所示:
我们发现可以运行出来。
多对一映射处理:
表关系有:一对一、多对一、一对多、多对多。表和表之间有关系,它所映射的实体类之间也有关系。
表对应的是实体类,字段对应当前的属性,当前表里面的一条数据对应的是一个实体类对象。
对一对应的是一个对象,对多是对应的一个集合。
我们在实体类里面将员工的部门信息设置如下:
private Dept dept;//当前员工所对应的部门。
我们设置接口的方法如下:
/**
* 获取员工以及对应的部门信息
* @param empId
* @return
*/
Emp getEmpAndDeptByEmpId(@Param("empId") Integer empId);
我们设置的映射文件如下所示:
我们在navicat里面进行查看:
SELECT * FROM t_emp LEFT JOIN t_dept ON t_emp.dept_id=t_dept.dept_id
我们查询之后如下所示:
我们发现此时有多个员工及他们的部门信息。我们来设置出通过id来搜索出相应的部门信息。
我们设置的sql语句如下所示:
SELECT * FROM t_emp LEFT JOIN t_dept ON t_emp.dept_id=t_dept.dept_id WHERE t_emp.emp_id=1
我们搜索后的界面如下所示:
我们发现此时有两个dept_id,我们设置如下所示:
select t_emp.*,t_dept.*
from t_emp
left join t_dept
on t_emp.dept_id=t_dept.dept_id
where t_emp.emp_id=#{empId}
我们建立测试类进行查看:
@Test
public void testGetEmpAndDeptByEmpId(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
Emp emp = mapper.getEmpAndDeptByEmpId(1);
System.out.println(emp);
}
我们运行之后如下所示:
我们重新在核心配置里面将驼峰原则进行配置如下所示:
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
我们继续测试如下所示:
我们发现此时员工信息匹配上了,但是部门信息无法进行匹配,针对这种问题,我们来通过三种方式进行解决:
一、使用级联处理多对一的映射关系:
我们进行设置的映射文件如下所示:
<!--两表联查查询出来的结果一共有七种:A表和B表 A、B、A和B的交集、A减去A和B的交集、B减去A和B的交集、A并B、A和B的并集减去A和B的交集 用左外连接查A,A要放在左边,用右外连接查A,A要放右边。 on 关键字设置他们的关联条件。 --> <resultMap id="empAndDeptResultMap" 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_id" property="dept.deptId"></result> <result column="dept_name" property="dept.deptName"></result> </resultMap> <select id="getEmpAndDeptByEmpId" resultMap="empAndDeptResultMap"> select t_emp.*,t_dept.* from t_emp left join t_dept on t_emp.dept_id=t_dept.dept_id where t_emp.emp_id=#{empId} </select>
我们运行之后如下所示:
二、我们使用association来进行处理映射:
我们将上面的resultMap的id设置为resultMapOne,然后创建如下映射文件,如下所示:
<!-- association:处理多对一的映射关系(处理实体类类型的属性) property:设置需要处理映射关系的属性的属性名 javaType:设置要处理的属性的类型 --> <resultMap id="empAndDeptResultMap" 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> <association property="dept" javaType="Dept"> <id column="dept_id" property="deptId"></id> <result column="dept_name" property="deptName"></result> </association> <!--从sql语句中查询出来的部门的信息和部门这个属性中的对象中的属性进行映射 --> </resultMap> <select id="getEmpAndDeptByEmpId" resultMap="empAndDeptResultMap"> select t_emp.*,t_dept.* from t_emp left join t_dept on t_emp.dept_id=t_dept.dept_id where t_emp.emp_id=#{empId} </select>
我们运行之后如下所示:
三、我们使用分步查询如下:
我们第一步首先通过sql语句映射文件找到员工信息:
<!--我们利用分步查询,查询员工信息以及员工所对应的部门信息,我们可以先把员工查询出来, 再把员工所对应的部门的id作为条件,在部门表里面进行查询,就可以查询出来所对应的部门--> <resultMap id="empAndDeptByStepResultMap" 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> <association property="dept" select="" column=""></association> </resultMap> <select id="getEmpAndDeptByStepOne" resultMap=""> select * from t_emp where emp_id=#{empId} </select>
之后我们再把员工所对应的部门的id作为条件,在部门表里面进行查找:
我们在接口处创建新的方法:
/** * 通过分步查询查询员工以及所对应的部门信息的第一步 * @param empId * @return */ Emp getEmpAndDeptByStep(@Param("empId") Integer empId);
我们创建新的映射文件:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.rgf.mybatis.mapper.DeptMapper"> <select id="getEmpAndDeptByStepTwo" resultType="Dept"> select * from t_dept where dept_id=#{deptId} </select> </mapper>
之后我们再之前的映射文件里面进行编辑如下所示:
<!--我们利用分步查询,查询员工信息以及员工所对应的部门信息,我们可以先把员工查询出来, 再把员工所对应的部门的id作为条件,在部门表里面进行查询,就可以查询出来所对应的部门--> <resultMap id="empAndDeptByStepResultMap" 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> <!-- property:设置需要处理映射关系的属性的属性名 select:设置分步查询的sql的唯一标识 column:设置分步查询的sql的条件(将查询出的某个字段作为分步查询的sql的条件) --> <!--当前property这个属性的值通过当前的DeptMapper里面的查询员工信息的方法查询出来后再给当前的值赋值--> <!--查询当前sql语句查询出来的,namespace.id就是唯一标识--> <!--column写来作为下一步的sql的条件的字段--> <association property="dept" select="com.rgf.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo" column="dept_id"></association> </resultMap> <select id="getEmpAndDeptByStepOne" resultMap="empAndDeptByStepResultMap"> select * from t_emp where emp_id=#{empId} </select> <resultMap id="emResultMap" 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> </resultMap> <select id="getEmpByEmpId" resultMap="emResultMap"> select * from t_emp where emp_id=#{empId} </select>
之后我们编写的测试类为:
@Test public void testGetEmpAndDeptByStep(){ SqlSession sqlSession = SqlSessionUtil.getSqlSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Emp emp = mapper.getEmpAndDeptByStepOne(1); System.out.println(emp); }
我们运行之后如下所示:
分步查询的优点:可以实现延迟加载(用就查,不用就不查,且只查自己要查的,减少内存消耗)
但是必须在核心配置文件中射中全局配置信息:
lazyLoadingEnabled:延迟加载的全局开关,当开启时,所有关联对象都会延迟加载
我们在全局配置里面配置如下所示:
<settings> <!--lazyLoadingEnabled:默认为false,即为没有效果,设定为true,即为有效果,开启懒加载,开启延迟加载。--> <!--开启延迟加载--> <setting name="lazyLoadingEnabled" value="true"/> </settings>
我们设置之后,进行设置测试类如下所示:
@Test public void testGetEmpAndDeptByStepSet(){ SqlSession sqlSession = SqlSessionUtil.getSqlSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Emp emp = mapper.getEmpAndDeptByStepOne(1); System.out.println(emp.getEmpName()); }
运行如下所示:
我们发现当我们搜索id为2的员工姓名的时候,只出来一个员工姓名。只执行了员工的sql,没有执行部门的sql。
我们将该配置删掉之后,我们继续运行测试类如下所示:
我们发现此时把员工也查询出来了。把员工所对应的部门也查询出来了。
aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性,否则,每个属性会按需加载。
我们在核心配置里配置如下示:
《settings> <!--aggressiveLazyLoading,默认值为false(按需加载),设置为true,为完整加载,不管是否开启完整加载,只要需要获取,所有的获取都会执行--> <setting name="aggressiveLazyLoading" value="false"/> </settings>
我们运行之后如下所示:
此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载。fetchType="lazy(延迟加 载)|eager(立即加载)“
因为该配置是在全局进行配置,为了实现我们某一个分步查询实现完整的查询,我们进行设置如下所示:
<!--将当前的分步查询设置为立即加载还是延迟加载 eager:立即加载,lazy:延迟加载--> <!-- fetchType:在开启了延迟加载的环境中,通过该属性设置当前的分步查询是否使用延迟加载 fetchType="eager(立即加载)|lazy(延迟加载)" --> <association property="dept" fetchType="eager" select="com.rgf.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo" column="dept_id"></association>
我们运行之后如下所示:
我们发现现在执行的是两个sql,fetch是在我们开启了延迟加载的环境中,我们可以指定我们的某一个分步查询为延迟加载或立即加载。
实现一对多的映射关系:
使用collection实现:
一个部门对应多个员工,我们在实体类里面添加如下所示:
private List<Emp> emps;
我们设置对应的get和set方法。
public List<Emp> getEmps() { return emps; }
我们之后设置toString方法:
@Override public String toString() { return "Dept{" + "deptId=" + deptId + ", deptName='" + deptName + '\'' + ", emps=" + emps + '}'; }
我们设置的mapper接口里面的方法如下所示:
/** *查询部门以及部门中的员工信息 * @param deptId * @return */ Dept getDeptAndEmpByDeptId(@Param("deptId") Integer deptId);
之后我们设置映射文件如下所示:
我们是设置的sql语句如下所示:select * from t_dept left join t_emp on t_dept.dept_id=t_emp.dept_id where t_dept.dept_id=1;
我们进行搜索之后如下所示:
我们发现一个部门对应了多个部门信息。
我们设置我们的映射文件如下所示:
<resultMap id="deptAndEmpResultMap" type="Dept"> <id column="dept_id" property="deptId"></id> <result column="dept_name" property="deptName"></result> <!-- ofType:设置集合类型的属性中存储的数据的类型 --> <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> <select id="getDeptAndEmpByDeptId" resultMap="deptAndEmpResultMap"> select * from t_dept left join t_emp on t_dept.dept_id=t_emp.dept_id where t_dept.dept_id=#{deptId}; </select>
我们设置我们的测试方法如下所受:
@Test public void testGetEmpAndEmpByDeptId(){ SqlSession sqlSession = SqlSessionUtil.getSqlSession(); DeptMapper mapper = sqlSession.getMapper(DeptMapper.class); Dept dept=mapper.getDeptAndEmpByDeptId(1); System.out.println(dept); }
我们运行之后如下所设计:
使用分步查询处理一对多的映射关系:
我们现在deptMapper里面设置方法如下所示:
/**
* 通过分步查询查询部门以及部门中的员工信息的第一步
* @param deptId
* @return
*/
Dept getDeptAndEmptByStepOne(@Param("dept") Integer deptId);
我们在映射文件里如下所示:
<resultMap id="deptAndEmpResultMapByStep" type="Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
<collection property="emps"
select="com.rgf.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
column="dept_id"></collection>
</resultMap>
<!--查询出来部门信息,用resultMap查询部门所对应的员工查询出来。-->
<select id="getDeptAndEmpByStepOne" resultMap="deptAndEmpResultMapByStep">
s elect * from t_dept where dept_id=#{deptId}
</select>
之后我们设置第二步方法如下所示(EmpMapper):
/**
* 通过分步查询查询员工以及所对应的部门信息的第二步
* @param deptId
* @return
*/
List<Emp> getDeptAndEmpByStepTwo(@Param("deptId") Integer deptId);
我们设置如下映射文件(EmpMapper.xml):
</resultMap>
<select id="getDeptAndEmpByStepTwo" resultType="Emp">
select * from t_emp where dept_id=#{deptId}
</select>
这样子就把员工给查询出来了。
我们在DeptMapper里面将映射文件关联条件如下所示:
<resultMap id="deptAndEmpResultMapByStep" type="Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
<collection property="emps"
select="com.rgf.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
column="dept_id"></collection>
</resultMap>
我们的测试类如下所示:
@Test
public void testGetEmpAndEmpByStep(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept=mapper.getDeptAndEmptByStepOne(1);
System.out.println(dept);
}
我们运行之后如下所示:
我们测试如下所示:
@Test
public void testGetDeptAndEmpByStep(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept=mapper.getDeptAndEmpByStepOne(1);
System.out.println(dept.getDeptName());
}
我们运行之后如下所示:
我们发现 此时通过分布查询我们搜索什么即可查找到什么,不会全部查询出来。
如果我们要设置为立即加载,我们设置如下所示:
<collection property="emps" fetchType="eager"
select="com.rgf.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
column="dept_id"></collection>
我们运行之后如下所示:
我们发现此时两条语句都进行了执行