自定义resultMap映射处理
1.resultMap 处理字段和属性的映射关系
- emp表
- Emp类相关属性(有参无参构造方法、提供set、get、重写toString)
private Integer empId; //员工id
private String empName; //员工姓名
private Integer age; //员工年龄
private String sex; //员工性别
- EmpMapper
/**
* 根据id查询员工
* @param empId
* @return
*/
Emp getEmpByEmpId(@Param("empId") Integer empId);
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qcby.mapper.EmpMapper">
<!--Emp getEmpByEmpId(@Param("empId") Integer empId);-->
<select id="getEmpByEmpId" resultType="emp">
select * from emp where emp_id=#{empId}
</select>
</mapper>
@Test
public void testGetEmpByEmpId() {
Emp empByEmpId = empMapper.getEmpByEmpId(1);
System.out.println(empByEmpId);
}
如果字段名和实体类中的属性名不一致的情况下,可以通过resultMap
设置自定义映射。
什么是字段名和实体类中的属性名不一致,见下图
不一致就无法正确查询
解决方法:
(1)可以通过为字段起别名的方式,别名起成和属性名一致。保证字段名和实体类中的属性名一致
<!--Emp getEmpByEmpId(@Param("empId") Integer empId);-->
<select id="getEmpByEmpId" resultType="emp">
<!--select * from emp where emp_id=#{empId}-->
select emp_id empId,emp_name empName,age,sex from emp where emp_id = #{empId}
</select>
(2)如果字段名和实体类中的属性名不一致的情况下,但是字段名符合数据库的规则(使用_ ),实体类中使用的属性名符合java 的规则(使用驼峰命名),可以在 MyBatis 的核心配置文件 mybatis-config.xml 中设置一个全局配置信息mapUnderscoreToCamelCase
,将其设置为true,便可以在查询表中的数据时,自动将带下划线“ _ ”的字段名转为驼峰命名
如:user name:userName
emp id:empld
<settings>
<!--将数据库字段名的下划线映射为驼峰-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--Emp getEmpByEmpId(@Param("empId") Integer empId);-->
<select id="getEmpByEmpId" resultType="emp">
select * from emp where emp_id=#{empId}
</select>
(3)使用resutlMap自定义映射处理
resultMap:设置自定义的映射关系
id:唯一标识(与resultMap设置的名称对应)
type:处理映射关系的实体类的类型 一般使用MyBatis的别名
常用的标签
id:处理主键和实体类中属性的映射关系
result:处理普通字段(主键之外的字段)和实体类中属性的映射关系
column:设置映射关系中的字段名,必须是sql查询出的某个字段(否则查询出来的结果为null)
property:设置映射关系中的属性的属性名,必须是处理的实体类型中的属性名
“column:设置映射关系中的字段名,必须是sql查询出的某个字段(否则查询出来的结果为null)”:
”property:设置映射关系中的属性的属性名,必须是处理的实体类型中的属性名“
<!--Emp getEmpByEmpId(@Param("empId") Integer empId);-->
<select id="getEmpByEmpId" resultMap="empResultMap">
<!--select * from emp where emp_id=#{empId}-->
</select>
<resultMap id="empResultMap" type="emp">
<id property="empId" column="emp_id"/>
<result property="empName" column="emp_name"/>
<result property="sex" column="sex"/>
<result property="age" column="age"/>
</resultMap>
2. 一对一映射处理
以人与身份证号为例
创建person表和idcard表
两张表中数据
创建实体类,添加上相应的有参无参构造方法、get、set、toString方法
- IdCard类
private Integer id; //主键id
private String code; //身份证号码
- Person类
private Integer id; //主键id
private String name; //姓名
private Integer age; //年龄
private String sex; //性别
private IdCard card; //不写外键的信息,而是直接让人员关联证件
sql语句
SELECT person.*, idcard.`code` FROM person,idcard WHERE person.card_id=idcard.id AND person.id=1
- 测试类
/**
* 测试类
*/
public class ResultMapTest {
private SqlSession sqlSession = null;
private EmpMapper empMapper = null;
private PersonMapper personMapper = null;
@Before
public void init() {
// 1.获取sqlsession对象
sqlSession = SqlSessionUtil.getSqlSession();
// 2.获取代理实现类对象(因为接口是不能创建实例对象的,所以此处用的是代理实现类对象)
empMapper = sqlSession.getMapper(EmpMapper.class);
personMapper = sqlSession.getMapper(PersonMapper.class);
}
@After
public void destory() {
// 5.关闭资源
sqlSession.close();
SqlSessionUtil.closeInputStream();
}
- SqlSessionUtil
public class SqlSessionUtil {
//getSqlSession()为静态方法,因此要给变量加上static才能在getSqlSession()中使用
private static InputStream inputStream = null;
//静态方法,方便别的类直接调用
public static SqlSession getSqlSession() {
SqlSession sqlSession = null;
try {
// 1.获取核心配置文件的输入流
//(可能会出现异常,因为此方法会被其他类调用,所以选择内部try-catch处理异常)
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 2.获取sqlsessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 3.获取sqlsessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
// 4.获取sqlsession对象(默认为false,不会自动提交对象;改为true就会自动提交)
sqlSession = sqlSessionFactory.openSession(true);
} catch (IOException e) {
e.printStackTrace();
}
return sqlSession;
}
public static void closeInputStream() {
//关闭文件可能关闭成功也可能关闭失败,因此使用try-catch处理一下
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
1.1 级联
创建mapper文件
public interface PersonMapper {
/**
* 根据id查询人员信息
* @param id
* @return
*/
Person findPersonById(@Param("id") Integer id);
}
要返回的是Person中的四个属性+IdCard中的code属性,没有相应的实体类全部包含这五个属性,因此使用resultMap
而不使用resultType
<!-- 级联方式-->
<!--Person findPersonById(@Param("id") Integer id);-->
<!--IdCard中的id对我们来说没有意义,我们要的是相应的code-->
<select id="findPersonById" resultMap="IdCardWithPersonResult">
SELECT person.*, idcard.`code` FROM person,idcard
WHERE person.card_id=idcard.id
AND person.id=1
</select>
<!--返回person类型对象-->
<resultMap id="IdCardWithPersonResult" type="person">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<result property="card.id" column="id"/>
<result property="card.code" column="code"/>
</resultMap>
1.2 association
Person findPersonById2(@Param("id") Integer id);
<!-- association-->
<!--Person findPersonById2(@Param("id") Integer id);-->
<select id="findPersonById2" resultMap="IdCardWithPersonResult2">
SELECT person.*, idcard.`code` FROM person,idcard
WHERE person.card_id=idcard.id
AND person.id=1
</select>
<resultMap id="IdCardWithPersonResult2" type="person">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<association property="card" javaType="IdCard">
<id property="id" column="id"/>
<result property="code" column="code"/>
</association>
</resultMap>
@Test
public void testFindPersonById2() {
Person person = personMapper.findPersonById2(1);
System.out.println(person);
}
1.3 分步查询
第一步
SELECT * FROM person WHERE id=1
- PersonMapper
Person findPersonById3(@Param("id") Integer id);
- PersonMapper.xml
<!-- 分步查询第一步-->
<!--Person findPersonById3(@Param("id") Integer id);-->
<select id="findPersonById3" resultMap="IdCardWithPersonResult3">
SELECT * FROM person WHERE id=#{id}
</select>
<resultMap id="IdCardWithPersonResult3" type="person">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<association property="card" javaType="idCard" column="card_id"
select="com.qcby.mapper.IdCardMapper.findCodeById">
</association>
</resultMap>
第二步
- IdCardMapper
public interface IdCardMapper {
/**
* 分步查询第二步
* 根据id查询证件信息
* @param id
* @return
*/
IdCard findCodeById(@Param("id") Integer id);
}
- IdCardMapper.xml
<mapper namespace="com.qcby.mapper.IdCardMapper">
<!--分步查询第二步-->
<!--IdCard findCodeById(@Param("id") Integer id);-->
<select id="findCodeById" resultType="idCard">
SELECT * FROM idcard WHERE id=#{id}
</select>
</mapper>
运行结果
1.4 association相关属性
association
处理一对一或者多对一的映射关系(处理的是实体类类型的属性)
property
设置需要处理映射关系的属性的属性名
javaType
设置需要处理的属性的类型
column
第一步传递给第二步的字段,映射关系的表关联的字段
3. 多对一映射处理
以员工和部门为例(多个员工可以有同一个部门)
创建员工和部门两个表
- 部门类
/**
* 部门表
*/
public class Dept {
private Integer deptId; //部门id
private String deptName; //部门名称
}
- 员工类
/**
* 员工表
*/
public class Emp {
private Integer empId; //员工id
private String empName; //员工姓名
private Integer age; //员工年龄
private String sex; //员工性别
//外键 (不写相应的外键名而直接写类)
private Dept dept;
}
sql
SELECT emp.*, dept.* FROM emp LEFT JOIN dept ON emp.dept_id=dept.dept_id WHERE emp.emp_id=1
2.1 级联
/**
* 通过员工id获取员工以及其对应的部门信息
*/
Emp getEmpAndDeptByEmpId(@Param("id") Integer id);
<!--Emp getEmpAndDeptByEmpId(@Param("id") Integer id);-->
<select id="getEmpAndDeptByEmpId" resultMap="empAndDeptResultMap">
SELECT emp.*, dept.*
FROM emp
LEFT JOIN dept ON emp.dept_id=dept.dept_id
WHERE emp.emp_id=#{id}
</select>
<resultMap id="empAndDeptResultMap" type="emp">
<id column="emp_id" property="empId"/>
<result column="emp_name" property="empName"/>
<result column="age" property="age"/>
<result column="sex" property="sex"/>
<result column="dept_id" property="dept.deptId"/>
<result column="dept_name" property="dept.deptName"/>
</resultMap>
@Test
public void testGetEmpAndDeptByEmpId() {
Emp emp = empMapper.getEmpAndDeptByEmpId(1);
System.out.println(emp);
}
2.2 association
Emp getEmpAndDeptByEmpId2(@Param("id") Integer id);
<!--Emp getEmpAndDeptByEmpId2(@Param("id") Integer id);-->
<select id="getEmpAndDeptByEmpId2" resultMap="empAndDeptResultMap2">
SELECT emp.*, dept.*
FROM emp
LEFT JOIN dept ON emp.dept_id=dept.dept_id
WHERE emp.emp_id=#{id}
</select>
<resultMap id="empAndDeptResultMap2" type="emp">
<id column="emp_id" property="empId"/>
<result column="emp_name" property="empName"/>
<result column="age" property="age"/>
<result column="sex" property="sex"/>
<association property="dept" javaType="dept">
<id column="dept_id" property="deptId"/>
<result column="dept_name" property="deptName"/>
</association>
</resultMap>
2.3 分步查询
第一步
根据员工id查询员工信息
- emp接口
/**
* 通过分步查询来查询员工以及所对应部门信息的第一步
*/
Emp getEmpAndDeptByStep1(@Param("empId") Integer empId);
第二步
根据员工信息中查询到的dept_id查询部门信息
- dept接口
/**
* 通过分步查询来查询员工以及所对应部门信息的第二步:查询部门信息
*/
Dept getEmpAndDeptByStep2(@Param("deptId") Integer deptId);
- emp映射文件中
<!--Emp getEmpAndDeptByStep1(@Param("empId") Integer empId);-->
<select id="getEmpAndDeptByStep1" resultMap="empAndDeptResultMap3">
SELECT * FROM emp WHERE emp_id=#{empId}
</select>
<resultMap id="empAndDeptResultMap3" type="emp">
<id property="empId" column="emp_id"/>
<result property="empName" column="emp_name"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
<association property="dept" column="dept_id"
select="com.qcby.mapper.DeptMapper.getEmpAndDeptByStep2">
</association>
</resultMap>
- dept映射文件中
<!--Dept getEmpAndDeptByStep2(@Param("deptId") Integer deptId);-->
<select id="getEmpAndDeptByStep2" resultType="dept">
SELECT * FROM dept WHERE dept_id=#{deptId}
</select>
- 测试
@Test
public void testGetEmpAndDeptByStep() {
Emp emp = empMapper.getEmpAndDeptByStep1(2);
System.out.println(emp);
}
分步查询的优点:
可以实现延迟加载(懒加载),但是必须在核心配置文件中设置全局配置信息
lazyLoadingEnabled
:延迟加载的全局开关,当开启时(true),所有管理对象都会延迟加载。默认false
aggressiveLazyLoading
:当开启时(true),任何方法的调用都会加载该对象的所有属性。否则(false),每个属性会按需加载,此时就可以实现按需加载,获取的数据是什么,就会执行相应的sql语句。默认false,此时可以通过association
和 collection
中的fetchType="lazy(延迟加载) |eager(立即加载)"
属性设置当前的分步查询是否使用延迟加载(默认fetchType=“lazy(延迟加载)”)
4. 一对多映射处理
没有级联方式的查询,只有集合collection和分步查询
查询一个部门,以及该部门中的多个员工的信息(一个部门中的多个员工信息)
collection
- sql语句
SELECT * FROM dept LEFT JOIN emp ON dept.dept_id=emp.dept_id
WHERE dept.dept_id=#{deptId}
- dept接口
/**
* 查询部门以及部门中的员工信息
* @param deptId
* @return
*/
Dept getDeptAndEmpByDeptId(@Param("deptId") Integer deptId);
- dept映射文件中
<!--Dept getDeptAndEmpByDeptId(@Param("deptId") Integer deptId);-->
<select id="getDeptAndEmpByDeptId" resultMap="deptAndEmpResultMap">
SELECT *
FROM dept
LEFT JOIN emp ON dept.dept_id=emp.dept_id
WHERE dept.dept_id=#{deptId}
</select>
<resultMap id="deptAndEmpResultMap" type="dept">
<id property="deptId" column="dept_id"/>
<result property="deptName" column="dept_name"/>
<!--ofType:设置集合类型的属性中存储的数据的类型-->
<collection property="emps" ofType="emp">
<id property="empId" column="emp_id"/>
<result property="empName" column="emp_name"/>
<result property="age" column="age"/>
<result property="sex" column="sex"/>
</collection>
</resultMap>
- 测试
@Test
public void testGetDeptAndEmpByDeptId() {
Dept dept = deptMapper.getDeptAndEmpByDeptId(2);
System.out.println(dept);
}
分步查询
第一步
根据dept_id查询部门信息
- dept接口
/**
* 通过分步查询进行查询部门及部门中的员工信息的第一步:查询部门信息
* @param deptId
* @return
*/
Dept getDeptAndEmpBystepOne(@Param("deptId") Integer deptId);
- dept映射文件
<!--Dept getDeptAndEmpBystepOne(@Param("deptId") Integer deptId);-->
<select id="getDeptAndEmpBystepOne" resultMap="deptAndEmpResultMapByStep">
SELECT * FROM dept WHERE dept_id=#{deptId}
</select>
<resultMap id="deptAndEmpResultMapByStep" type="dept">
<id property="deptId" column="dept_id"/>
<result property="deptName" column="dept_name"/>
<collection property="emps" column="dept_id"
select="com.qcby.mapper.EmpMapper.getDeptAndEmpBystepTwo">
</collection>
</resultMap>
第二步
根据dept_id查询员工信息
- emp接口
/**
* 通过分步查询来查询部门以及所对应员工信息的第二步:查询员工信息
* @param empId
* @return
*/
Emp getDeptAndEmpBystepTwo(@Param("empId") Integer empId);
- emp映射文件
<!--Emp getDeptAndEmpBystepTwo(@Param("empId") Integer empId);-->
<select id="getDeptAndEmpBystepTwo" resultType="emp">
SELECT * FROM emp WHERE dept_id=#{empId}
</select>
- 测试类
@Test
public void testGetDeptAndEmpBystep() {
Dept dept = deptMapper.getDeptAndEmpBystepOne(2);
System.out.println(dept);
}
5. 多对多映射处理
以商品和订单为例(同一种商品可能在多个订单里,一个订单里可以有很多个商品)
多对多需要创建中间表
分步查询
- sql语句
第一步
- orders接口
/**
* 通过分步查询进行查询订单以及订单中的商品信息的第一步:查询订单
* @param id
* @return
*/
List<Orders> findOrdersWithProductByStep1(@Param("id") Integer id);
第二步
- product接口
/**
* 通过分步查询进行查询订单以及订单中的商品信息的第二步:查询商品
* @param id
* @return
*/
List<Product> findOrdersWithProductByStep2(@Param("id") Integer id);
- orders映射文件
<!--List<Orders> findOrdersWithProductByStep1(@Param("id") Integer id);-->
<select id="findOrdersWithProductByStep1" resultMap="OrdersWithProductByStep">
SELECT * FROM orders WHERE id=#{id}
</select>
<resultMap id="OrdersWithProductByStep" type="orders">
<id property="id" column="id"/>
<result property="number" column="number"/>
<collection property="productList" column="id" ofType="product"
select="com.qcby.mapper.ProductMapper.findOrdersWithProductByStep2">
</collection>
</resultMap>
- product映射文件
<!--List<Product> findOrdersWithProductByStep2(@Param("id") Integer id);-->
<select id="findOrdersWithProductByStep2" resultType="product">
SELECT * FROM product WHERE id in(
SELECT product_id FROM ordersitem WHERE orders_id=#{id}
)
</select>
- 测试类
@Test
public void testFindOrdersWithProductByStep() {
List<Orders> orders = ordersMapper.findOrdersWithProductByStep1(1);
for (Orders order : orders) {
System.out.println(order);
}
}