pojo对象是我们用来和数据库表进行映射的对象,它的字段应该完全包含了一个数据库表的全部字段。但是有的时候我们查询的结果并不需要显示全部字段,或者我们需要将A表和B表进行联合查询,查询的结果既有A表的字段,又有B表的字段,这个时候再继续用pojo来作为数据库数据传输的对象显然就不太合适了,我们就需要根据查询结果,再创建一个DTO对象来帮助我们进行数据传输。
emp.java
package cn.tulingxueyuan.pojo;
import java.time.LocalDate;
public class Emp {
private Integer id;
private String username;
private LocalDate createDate;
private Integer deptId;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public LocalDate getCreateDate() {
return createDate;
}
public void setCreateDate(LocalDate createDate) {
this.createDate = createDate;
}
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", username='" + username + '\'' +
", createDate=" + createDate +
", deptId=" + deptId+
'}';
}
}
上面是Emp表对应的pojo对象,包含了Emp表的全部字段,但是现在我们需要进行一个Emp表和DEPT表的联合查询,再去使用这个Emp对象显然就不合适了,需要根据这个联合查询显示的字段,再去创建一个DTO对象。
QueryEmpDTO
package cn.tulingxueyuan.pojo;
public class QueryEmpDTO {
private String deptName;
private Integer deptId;
private Integer id;
private String username;
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "QueryEmpDTO{" +
"deptName='" + deptName + '\'' +
", deptId=" + deptId +
", id=" + id +
", username='" + username + '\'' +
'}';
}
}
这个DTO对象就既包含了EMP表的字段,也包含了DEPT表的字段,我们就可以用这个对象进行数据传输。
下面来设置映射关系。
EmpMapper.xml
<!-- 实现表联结查询的方式: 可以映射: DTO -->
<resultMap id="QueryEmp_Map" type="QueryEmpDTO">
<!-- 主键字段的映射关系 -->
<id column="e_id" property="id"></id>
<!-- 其他字段的映射关系 -->
<result column="user_name" property="username"></result>
<result column="d_id" property="deptId"></result>
<result column="dept_name" property="deptName"></result>
</resultMap>
<!-- resultMap要和上面<resultMap>标签中的id属性一致,这样就能将映射关系关联到这个select标签的结果集上 -->
<select id="QueryEmp" resultMap="QueryEmp_Map">
select t1.id as e_id,t1.user_name,t2.id as d_id,t2.dept_name from emp t1
INNER JOIN dept t2 on t1.dept_id=t2.id
where t1.id=#{id}
</select>
上面这段配置,就是用了我们建立的DTO对象来获取从数据库中查询的数据。
如果不想用DTO对象,也可以使用Map对象来接收查询结果。MyBatis会自动将查询出来的列名存到Key中,列值存到value里。
<!-- 实现表联结查询的方式: 可以映射map -->
<resultMap id="QueryEmp_Map" type="map">
<id column="e_id" property="id"></id>
<result column="user_name" property="username"></result>
<result column="d_id" property="deptId"></result>
<result column="dept_name" property="deptName"></result>
</resultMap>
<select id="QueryEmp" resultMap="QueryEmp_Map">
select t1.id as e_id,t1.user_name,t2.id as d_id,t2.dept_name from emp t1
INNER JOIN dept t2 on t1.dept_id=t2.id
where t1.id=#{id}
</select>
Test
@Test
public void test01() {
try(SqlSession sqlSession = sqlSessionFactory.openSession()){
// MyBatis在getMapper就会给我们创建jdk动态代理
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
QueryEmpDTO dto = mapper.QueryEmp(4);
System.out.println(dto);
}
}
上面是联合查询时最简单的写法,下面我们讲一下关联查询一些更复杂的情况。
在关系型数据库中,我们经常要处理一对一 、 一对多的关系 。 例如, 一辆汽车需要有一个引擎,这是一对一的关系。 一辆汽车有 4 个或更多个轮子,这是一对多的关系 。关联元素就是专门用来处理关联关系的;
关联元素:
关联方式:
下面我们分别讲解嵌套结果和嵌套查询。
有的时候查询结果的对象中还会包含着另一个对象,这个时候就需要用嵌套映射来解决这个问题,避免映射配置的冗余。
Emp.java
package cn.tulingxueyuan.pojo;
import java.time.LocalDate;
public class Emp {
private Integer id;
private String username;
private LocalDate createDate;
// Emp对象中还包含了一个Dept类型的属性
private Dept dept;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public LocalDate getCreateDate() {
return createDate;
}
public void setCreateDate(LocalDate createDate) {
this.createDate = createDate;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", username='" + username + '\'' +
", createDate=" + createDate +
", dept=" + dept +
'}';
}
}
EmpMapper.xml
<!--嵌套结果 多 对 一 -->
<resultMap id="QueryEmp_Map2" type="Emp">
<id column="e_id" property="id"></id>
<result column="user_name" property="username"></result>
<!--
association 实现多对一中的 “一”
property 指定对象中的嵌套对象属性
-->
<association property="dept">
<id column="d_id" property="id"></id>
<id column="dept_name" property="deptName"></id>
</association>
</resultMap>
<select id="QueryEmp2" resultMap="QueryEmp_Map2">
select t1.id as e_id,t1.user_name,t2.id as d_id,t2.dept_name from emp t1
INNER JOIN dept t2 on t1.dept_id=t2.id
where t1.id=#{id}
</select>
EmpMapper.java
package cn.tulingxueyuan.mapper;
import cn.tulingxueyuan.pojo.Emp;
import cn.tulingxueyuan.pojo.QueryEmpDTO;
import java.util.Map;
public interface EmpMapper {
/*徐庶老师实际开发中的实现方式*/
QueryEmpDTO QueryEmp(Integer id);
/*实用嵌套结果实现联合查询 多对一 */
Emp QueryEmp2(Integer id);
/*实用嵌套查询实现联合查询 多对一 */
Emp QueryEmp3(Integer id);
}
association标签 嵌套结果方式常用属性:
Tips:
Dept.java
package cn.tulingxueyuan.pojo;
import java.util.List;
public class Dept {
private Integer id;
private String deptName;
// Dept中包含了一个Emp集合
private List<Emp> emps;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public List<Emp> getEmps() {
return emps;
}
public void setEmps(List<Emp> emps) {
this.emps = emps;
}
@Override
public String toString() {
return "Dept{" +
"id=" + id +
", deptName='" + deptName + '\'' +
", emps=" + emps +
'}';
}
}
DeptMapper.xml
<!-- 嵌套结果: 一对多 查询部门及所有员工 -->
<resultMap id="SelectDeptAndEmpsMap" type="Dept">
<id column="d_id" property="id"></id>
<id column="dept_name" property="deptName"></id>
<!--
<collection> 映射一对多中的 “多”
property 指定需要映射的“多”的属性,一般声明为List
ofType 需要指定list的类型
-->
<collection property="emps" ofType="Emp" >
<id column="e_id" property="id"></id>
<result column="user_name" property="username"></result>
<result column="create_date" property="createDate"></result>
</collection>
</resultMap>
<select id="SelectDeptAndEmps" resultMap="SelectDeptAndEmpsMap">
select t1.id as d_id,t1.dept_name,t2.id e_id,t2.user_name,t2.create_date from dept t1
LEFT JOIN emp t2 on t1.id=t2.dept_id
where t1.id=#{id}
</select>
DeptMapper.java
package cn.tulingxueyuan.mapper;
import cn.tulingxueyuan.pojo.Dept;
public interface DeptMapper {
//嵌套查询: 一对多 使用部门id查询员工
Dept SelectDeptAndEmps(Integer id);
// 嵌套查询(异步查询): 一对多 查询部门及所有员工
Dept SelectDeptAndEmps2(Integer id);
}
Tips:
如果要配置一个相当复杂的映射,一定要从基础映射开始配置,每增加一些配置就进行对应的测试,在循序渐进的过程中更容易发现和解决问题 。
在上述逻辑的查询中,是由我们自己来完成sql语句的关联查询的,那么我们能让MyBatis帮我们实现自动的关联查询吗?
EmpMapper.xml
<!--嵌套查询(分步查询) 多 对 一
联合查询和分步查询区别: 性能区别不大
分部查询支持 懒加载(延迟加载)
需要设置懒加载,一定要使用嵌套查询的。
要启动懒加载可以在全局配置文件中设置 lazyLoadingEnabled=true
还可以单独为某个分步查询设置立即加载 <association fetchType="eager"
-->
<resultMap id="QueryEmp_Map3" type="Emp">
<id column="id" property="id"></id>
<result column="user_name" property="username"></result>
<!-- association 实现多对一中的 “一”
property 指定对象中的嵌套对象属性
column 指定将哪个字段传到分步查询中
select 指定分步查询的 命名空间+ID
以上3个属性是实现分步查询必须的属性
fetchType 可选, eager|lazy eager立即加载 lazy跟随全局配置文件中的lazyLoadingEnabled
-->
<association property="dept" column="dept_id" select="cn.tulingxueyuan.mapper.DeptMapper.SelectDept">
</association>
</resultMap>
<select id="QueryEmp3" resultMap="QueryEmp_Map3">
select * from emp where id=#{id}
</select>
DeptMapper.xml
<!-- 根据部门id查询部门-->
<select id="SelectDept" resultType="dept">
SELECT * FROM dept where id=#{id}
</select>
association标签 嵌套查询方式常用属性:
Tips:“N+1 查询问题”
概括地讲,N+1 查询问题可以是这样引起的:
这个问题会导致成百上千的 SQL 语句被执行。这通常不是期望的。
解决办法:使用“fetchType=lazy”并且全局setting进行改善:
<setting name="aggressiveLazyLoading" value="false"/>
延迟加载会在下一章节会详细讲解:四、延迟查询
DeptMapper.xml
<!-- 嵌套查询(异步查询): 一对多 查询部门及所有员工 -->
<resultMap id="SelectDeptAndEmpsMap2" type="Dept">
<id column="d_id" property="id"></id>
<id column="dept_name" property="deptName"></id>
<!--
<collection 映射一对多中的 “多”
property 指定需要映射的“多”的属性,一般声明为List
ofType 需要指定list的类型
column 需要将当前查询的字段传递到异步查询的参数
select 指定异步查询
-->
<collection property="emps" ofType="Emp" column="id" select="cn.tulingxueyuan.mapper.EmpMapper.SelectEmpByDeptId" >
</collection>
</resultMap>
<select id="SelectDeptAndEmps2" resultMap="SelectDeptAndEmpsMap2">
SELECT * FROM dept where id=#{id}
</select>
EmpMapper.xml
<!-- 根据部门id所有员工 -->
<select id="SelectEmpByDeptId" resultType="emp">
select * from emp where dept_id=#{id}
</select>
Emp.java
package cn.tulingxueyuan.pojo;
import java.time.LocalDate;
public class Emp {
private Integer id;
private String username;
private LocalDate createDate;
private Dept dept;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public LocalDate getCreateDate() {
return createDate;
}
public void setCreateDate(LocalDate createDate) {
this.createDate = createDate;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", username='" + username + '\'' +
", createDate=" + createDate +
", dept=" + dept +
'}';
}
}
Dept.java:
package cn.tulingxueyuan.pojo;
import java.util.List;
public class Dept {
private Integer id;
private String deptName;
private List<Emp> emps;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public List<Emp> getEmps() {
return emps;
}
public void setEmps(List<Emp> emps) {
this.emps = emps;
}
@Override
public String toString() {
return "Dept{" +
"id=" + id +
", deptName='" + deptName + '\'' +
", emps=" + emps +
'}';
}
}
EmpMapper.java
package cn.tulingxueyuan.mapper;
import cn.tulingxueyuan.pojo.Emp;
import cn.tulingxueyuan.pojo.QueryEmpDTO;
import java.util.Map;
public interface EmpMapper {
/*徐庶老师实际开发中的实现方式*/
QueryEmpDTO QueryEmp(Integer id);
/*实用嵌套结果实现联合查询 多对一 */
Emp QueryEmp2(Integer id);
/*实用嵌套查询实现联合查询 多对一 */
Emp QueryEmp3(Integer id);
}
DeptMapper.java
package cn.tulingxueyuan.mapper;
import cn.tulingxueyuan.pojo.Dept;
public interface DeptMapper {
//嵌套查询: 一对多 使用部门id查询员工
Dept SelectDeptAndEmps(Integer id);
// 嵌套查询(异步查询): 一对多 查询部门及所有员工
Dept SelectDeptAndEmps2(Integer id);
}
当我们在进行表关联的时候,有可能在查询结果的时候不需要关联对象的属性值,那么此时可以通过延迟加载来实现功能。在全局配置文件中添加如下属性:
mybatis-config.xml
<settings>
<!-- 开启延迟加载主要是前两个配置-->
<!-- 延迟加载,所有分步查询都是懒加载,默认是false(立即加载)-->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 当为true时,所有延迟属性都会立即加载;为false时,调用这些延迟属性时才会查询和加载-->
<setting name="aggressiveLazyLoading" value="false" />
<!--设置对象的哪些方法调用会加载延迟查询 默认:equals,clone,hashCode,toString-->
<!-- 最好配置为空,这样可以最大限度延迟加载-->
<!-- 当前配置的意思为当调用getId时延迟加载-->
<!-- 可以不配置-->
<setting name="lazyLoadTriggerMethods" value="getId"/>
<!-- <setting name="lazyLoadTriggerMethods" value=""/> -->
</settings>
| 设置名 | 描述 | 有效值 | 默认值 |
| lazyLoadingEnabled | 延迟加载的全局开关,当开启时所有关联对象都会延迟加载,特定关联关系可以通过设置fetchType属性来覆盖该项的开关状态。 | true|false | false |
| aggressiveLazyLoading | 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。 | true|false | false(在3.4.1及之前的版本默认值为true) |
| lazyLoadTriggerMethods | 指定对象的哪些方法触发一次延迟加载。 | 用逗号分隔的方法列表。 | equals,clone,hashCode,toString |
如果设置了全局加载,但是希望在某一个sql语句查询的时候不使用延时策略,可以添加fetchType下属性:
<association property="dept" fetchType="eager" column="dept_id" select="cn.tulingxueyuan.mapper.DeptMapper.SelectDept">
</association>
实现班级学生案例:
1、创建实体类 班级类以及学生类
班级类:
public class Clazz {
private int id;
private String name;
private List<Student> students;
@Override
public String toString() {
return "Clazz{" +
"id=" + id +
", name='" + name + '\'' +
", students=" + students +
'}';
}
// get和set方法省略...
}
学生类:
public class Student {
private int id;
private String name;
private String sex;
private int clazz;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", clazz=" + clazz +
'}';
}
// get和set方法省略...
}
2、创建StudentMapper和ClazzMapper接口
ClazzMapper接口:
public interface ClazzMapper {
/**
* 根据班级编号查询班级
* @param id
* @return
*/
Clazz selectById(int id);
}
StudentMapper接口:
public interface StudentMapper {
/**
* 根据学生编号查找学生
* @param id
* @return
*/
Student selectById(int id);
/**
* 根据学生的班级号查找学生
* @param id
* @return
*/
List<Student> selectByClazz(int id);
}
3、创建ClazzMapper.xml和StudentMapper.xml
ClazzMapper.xml:
<!-- 根据班级编号查找班级-->
<select id="selectById" resultMap="rm">
select * from clazz where id=#{id}
</select>
<!-- 配置映射,根据学生班级号查找学生放进班级里-->
<resultMap id="rm" type="clazz" autoMapping="true">
<id column="id" property="id"></id>
<!-- 嵌套查询 懒加载就是对这个字段起作用 -->
<collection property="students" column="id" select="dao.StudentMapper.selectByClazz"></collection>
</resultMap>
StudentMapper.xml:
<!-- 根据学生id查找学生-->
<select id="selectById" resultType="student">
select * from student where id=#{id}
</select>
<!-- 根据学生班级号查找学生-->
<select id="selectByClazz" resultType="student">
select * from student where clazz=#{id}
</select>
4、测试
public class Test {
public static void main(String[] args) {
Clazz clazz = new ClazzService().selectById(1); // 1
System.out.println(clazz.getId()); // 2
System.out.println(clazz.getName()); // 3
System.out.println(clazz.getStudents()); // 4
}
}
当注释掉第4句时,因为没有要求查找学生,按照延迟加载的特性,程序只会查询班级的信息,而不会查询学生的信息,所以只有一条sql语句,就是根据班级编号查找班级的select,并没有查找学生的select:
当放开第四句时,因为clazz.getStudents()需要student数据,所以会执行第二条sql语句将student的信息查询到:
三种关联关系都有两种关联查询的方式:1、嵌套查询 2、嵌套结果
Mybatis的延迟加载配置:
在全局配置文件中加入下面代码
<settings>
<setting name=”lazyLoadingEnabled” value=”true” />
<setting name=”aggressiveLazyLoading” value=”false”/>
</settings>
在映射文件中,元素和元素中都已默认配置了延迟加载属性,即默认属性fetchType=”lazy”(属性fetchType=”eager”表示立即加载),所以在配置文件中开启延迟加载后,无需在映射文件中再做配置。
使用元素进行一对一关联映射非常简单,只需要参考如下两种示例配置即可
<resultMap>元素中,包含了一个子元素,MyBatis就是通过该元素来处理一对多关联关系的:
多对多的关联关系查询,同样可以使用前面介绍的元素进行处理(其用法和一对多关联关系查询语句用法基本相同)
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- cepb.cn 版权所有 湘ICP备2022005869号-7
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务