learn from:http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html#Result_Maps

关联查询

  1. 准备
  2. 关联结果查询(一对一)
  3. resultMap复用
  4. 集合(一对多)

3 关联查询

做查询之前,先修改几个配置。mapper.xml是在mybatis-config.xml中指定,那么我们每增加一个mapper都要增加一个配置,很麻烦。为了简化配置。需要将mapper接口和mapper.xml放到同一个文件下,并且接口和xml文件命名一致。使用mybatis的自动扫描:.这样,当我们新增接口的时候,直接创建接口和对应xml文件就可以了:

<mappers>
<!--<mapper resource="com.test.mapper.dao/AuthorMapper.xml"/>-->
<package name="com.test.mapper.dao"/>
</mappers>

  

3.1 prepare

增加一个表blog :

CREATE TABLE `blog` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`author_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; 

创建实体类com.test.mapper.model.Blog:

package com.test.mapper.model;

/**
* Created by miaorf on 2016/7/20.
*/
public class Blog {
private Integer id;
private String name;
private Author author; public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Author getAuthor() {
return author;
} public void setAuthor(Author author) {
this.author = author;
} @Override
public String toString() {
return "Blog{" +
"id=" + id +
", name='" + name + '\'' +
", author=" + author +
'}';
}
}

创建接口:com/test/mapper/dao/BlogMapper.xml

package com.test.mapper.dao;

import com.test.mapper.model.Blog;

import java.util.List;

/**
* Created by miaorf on 2016/7/20.
*/
public interface BlogMapper { List<Blog> selectBlog(Integer id);
}

创建xml:com/test/mapper/dao/BlogMapper.xml

<?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.test.mapper.dao.BlogMapper">
<resultMap id="blogResult" type="blog">
<association property="author" column="author_id" javaType="author" select="com.test.mapper.dao.AuthorMapper.selectAuthorById"/>
</resultMap>
<select id="selectBlog" resultMap="blogResult">
select * from blog where id = #{id}
</select> </mapper>

 

3.2含义

首先,修改了mybatis配置文件中mapper扫描配置,因此可以直接在扫描包下添加接口和xml文件。

其次,关于mybatis的命名空间namespace的用法,这个是唯一的,可以代表这个xml文件本身。因此,当我想要引用Author的查询的时候,我可以直接使用AuthorMapper.xml的命名空间点select的id来唯一确定select片段。即:

select="com.test.mapper.dao.AuthorMapper.selectAuthorById"

然后,关联查询,blog的author_id字段和author的id字段关联。因此,将Author作为Blog的一个属性,先查询blog,然后根据author_id字段去查询author。也就是说,查询了两次。这里也是我困惑的地方,为什么mybatis要这样处理,命名可以一次查询取得数据非要两次查询。

注意:association节点中

  • column字段是author_id,是当做参数传递给查询Author的查询语句的,如果查询语句的参数有多个则:column= ” {prop1=col1,prop2=col2} ” 这种语法来传递给嵌套查询语 句。这会引起 prop1 和 prop2 以参数对象形式来设置给目标嵌套查询语句。
  • property则表示在Blog类中对应的属性。

我们有两个查询语句:一个来加载博客,另外一个来加载作者,而且博客的结果映射描 述了“selectAuthor”语句应该被用来加载它的 author 属性。

其他所有的属性将会被自动加载,假设它们的列和属性名相匹配。

这种方式很简单, 但是对于大型数据集合和列表将不会表现很好。 问题就是我们熟知的 “N+1 查询问题”。概括地讲,N+1 查询问题可以是这样引起的:

  • 你执行了一个单独的 SQL 语句来获取结果列表(就是“+1”)。
  • 对返回的每条记录,你执行了一个查询语句来为每个加载细节(就是“N”)。

这个问题会导致成百上千的 SQL 语句被执行。这通常不是期望的。

MyBatis 能延迟加载这样的查询就是一个好处,因此你可以分散这些语句同时运行的消 耗。然而,如果你加载一个列表,之后迅速迭代来访问嵌套的数据,你会调用所有的延迟加 载,这样的行为可能是很糟糕的。

所以还有另外一种方法

3.3 关联查询结果

上述关联查询主要是为了延迟加载,做缓存用的,如果你不调用blog.getAuthor()来获取author,那么mybatis就不会去查询Author。也就是说,mybatis把博客和作者的查询当做两步来执行。实现信息分段加载,在某些场合是有用的。然而在博客这里,显然不太合适,因为我们看到博客的同时都会看到作者,那么必然会导致查询数据库两次。下面,来测试另一种方式,像sql关联查询一样,一次查出结果。

<select id="selectBlogWithAuthor" resultMap="blogResultWithAuthor">
SELECT
b.id as blog_id,
b.name as blog_title,
b.author_id as blog_author_id,
a.id as author_id,
a.username as author_username,
a.password as author_password,
a.email as author_email,
a.bio as author_bio
FROM blog b LEFT OUTER JOIN author a ON b.author_id=a.id
WHERE b.id = #{id}
</select>
<resultMap id="blogResultWithAuthor" type="blog">
<id property="id" column="blog_id"/>
<result property="name" column="blog_title"/>
<association property="author" column="blog_author_id" javaType="author" resultMap="authorResult"/>
</resultMap>
<resultMap id="authorResult" type="author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
</resultMap>

和3.1里的查询一样,都是查询出blog和Author。但这个只查询数据库一次,也就是说实现了我们的关联查询。这几行代码乍一看有点复杂,仔细分析一下就很明了了。

1> 首先看到的是select标签,这个表示查询。其中id表示对应的接口的方法名;resultMap的值是一个resultMap节点的id,这个表示select查询结果的映射方式。

2> select中间就是我熟悉的关联查询语句,这里不做赘述

3> 然后就是resultMap所指向的节点blogResultWithAuthor。

  • resultMap节点的id就是唯一标识这个节点的,type表示这个resultMap最终映射成的实体类Blog。
  • id是主键映射,这个和mybatis缓存有关。
  • result:
  • association:
  • authorResult同理。

可以看到控制台打印的日志:

2016-07-22 23:01:00,148 DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Opening JDBC Connection
2016-07-22 23:01:11,017 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - Created connection 1893960929.
2016-07-22 23:01:19,281 DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@70e38ce1]
2016-07-22 23:01:27,018 DEBUG [com.test.mapper.dao.BlogMapper.selectBlogWithAuthor] - ==> Preparing: SELECT b.id as blog_id, b.name as blog_title, b.author_id as blog_author_id, a.id as author_id, a.username as author_username, a.password as author_password, a.email as author_email, a.bio as author_bio FROM blog b LEFT OUTER JOIN author a ON b.author_id=a.id WHERE b.id = ?
2016-07-22 23:01:29,697 DEBUG [com.test.mapper.dao.BlogMapper.selectBlogWithAuthor] - ==> Parameters: 1(Integer)
2016-07-22 23:01:30,091 DEBUG [com.test.mapper.dao.BlogMapper.selectBlogWithAuthor] - <== Total: 1

3.4 resultMap复用

只要引用resultMap的id就可以引用这个resultMap。然而,有时候resultMap的结构相同,但字段不同。比如,Blog有两个作者Author、co-Athour。这时候的查询语句是这样的:

SELECT
b.id as blog_id,
b.name as blog_title,
b.author_id as blog_author_id,
a.id as author_id,
a.username as author_username,
a.password as author_password,
a.email as author_email,
a.bio as author_bio,
ca.id as co_author_id,
ca.username as co_author_username,
ca.password as co_author_password,
ca.email as co_author_email,
ca.bio as co_author_bio
FROM blog b
LEFT OUTER JOIN author a ON b.author_id=a.id
LEFT OUTER JOIN author ca ON b.co_author_id=ca.id
WHERE b.id = #{id}

显然合作者和作者的结构式相同的,但是我们给了不同的别名(alias)。这时候可以使用resultMap的columnPrefix属性:

<!-- association 2 -->
<select id="selectBlogWithAuthor2" resultMap="blogResultWithAuthor2">
SELECT
b.id as blog_id,
b.name as blog_title,
b.author_id as blog_author_id,
a.id as author_id,
a.username as author_username,
a.password as author_password,
a.email as author_email,
a.bio as author_bio,
ca.id as co_author_id,
ca.username as co_author_username,
ca.password as co_author_password,
ca.email as co_author_email,
ca.bio as co_author_bio
FROM blog b
LEFT OUTER JOIN author a ON b.author_id=a.id
LEFT OUTER JOIN author ca ON b.co_author_id=ca.id
WHERE b.id = #{id}
</select>
<resultMap id="blogResultWithAuthor2" type="Blog">
<id property="id" column="blog_id"/>
<result property="name" column="blog_title"/>
<association property="author" resultMap="authorResult"/>
<association property="coAuthor" resultMap="authorResult"
columnPrefix="co_"/>
</resultMap>
<resultMap id="authorResult" type="author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
</resultMap>

3.4集合

上述关联查询是has a关系,一对一的。一对多的,需要使用collection。

比如,一个博客有一个作者,但有很多博文。

创建博文表post:

/*
Navicat MySQL Data Transfer Source Server : localhost
Source Server Version : 50605
Source Host : localhost:3306
Source Database : mybatis Target Server Type : MYSQL
Target Server Version : 50605
File Encoding : 65001 Date: 2016-07-24 22:39:35
*/ SET FOREIGN_KEY_CHECKS=0; -- ----------------------------
-- Table structure for post
-- ----------------------------
DROP TABLE IF EXISTS `post`;
CREATE TABLE `post` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`subject` varchar(255) DEFAULT NULL,
`body` varchar(255) DEFAULT NULL,
`blog_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; -- ----------------------------
-- Records of post
-- ----------------------------
INSERT INTO `post` VALUES ('', 'test', 'test-body', '');

创建博文实体类com.test.mapper.model.Post:

package com.test.mapper.model;

/**
* Created by miaorf on 2016/7/24.
*/
public class Post {
private Integer id;
private String subject;
private String body; public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getSubject() {
return subject;
} public void setSubject(String subject) {
this.subject = subject;
} public String getBody() {
return body;
} public void setBody(String body) {
this.body = body;
}
}

在Blog里添加属性字段

private List<Post> posts;

编写查询如下:

<!-- collection -->
<select id="selectBlogWithPost" resultMap="blogResultWithPost">
SELECT
b.id AS blog_id,
b.name AS blog_name,
b.author_id AS blog_author_id,
p.id AS post_id,
p.subject AS post_subject,
p.body AS post_body
FROM blog b
LEFT OUTER JOIN post p ON b.id=p.blog_id
WHERE b.id=#{id}
</select>
<resultMap id="blogResultWithPost" type="Blog">
<id property="id" column="blog_id"/>
<result property="name" column="blog_name"/>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<result property="body" column="post_body"/>
</collection>
</resultMap>

很容易看出来,集合和关联几乎完全一样,只不过一个是association一个是collection,一个是一对一,一个是一对多。到这里基本就可以满足所有的sql关联查询了。关于collection的参数,还是要注释一下:

同样的,集合collection和关联association一样,都属于resultMap类型,即结果集映射规则。那么,collection也可以抽出来复用:

<resultMap id="blogResultWithPost" type="Blog">
<id property="id" column="blog_id"/>
<result property="name" column="blog_name"/>
<collection property="posts" ofType="Post" resultMap="postResult" columnPrefix="post_"/>
</resultMap>
<resultMap id="postResult" type="Post">
<id property="id" column="id"/>
<result property="subject" column="subject"/>
<result property="body" column="body"/>
</resultMap>

注意 

这个对你所映射的内容没有深度,广度或关联和集合相联合的限制。当映射它们 时你应该在大脑中保留它们的表现。 你的应用在找到最佳方法前要一直进行的单元测试和性 能测试。好在 myBatis 让你后来可以改变想法,而不对你的代码造成很小(或任何)影响。

高级关联和集合映射是一个深度的主题。文档只能给你介绍到这了。加上一点联系,你 会很快清楚它们的用法

Mybatis高级查询之关联查询的更多相关文章

  1. MyBatis:学习笔记(3)——关联查询

    MyBatis:学习笔记(3)--关联查询 关联查询 理解联结 SQL最强大的功能之一在于我们可以在数据查询的执行中可以使用联结,来将多个表中的数据作为整体进行筛选. 模拟一个简单的在线商品购物系统, ...

  2. 【Mybatis】MyBatis之表的关联查询(五)

    本章介绍Mybatis之表的关联查询 一对一关联 查询员工信息以及员工的部门信息 1.准备表employee员工表,department部门表 CREATE TABLE `employee` ( `i ...

  3. MyBatis 中两表关联查询MYSQL (14)

    MyBatis 中两表关联查询MYSQL 1.创建数据库表语句 2.插入测试数据 3.pom文件内容 <?xml version="1.0" encoding="U ...

  4. Mongoose如何实现统计查询、关联查询

    [问题]Mongoose如何实现统计查询.关联查询  发布于 4 年前  作者 a272121742  13025 次浏览 最近业务上提出一个需求,要求能做统计,我们设计的文档集,统计可能跨越的文档会 ...

  5. MySQL查询(关联查询)

    一.mysql查询与权限 (一)数据库关联查询 **内连接查询(inner join)** 查询两个表共有的数据,交集 SELECT * FROM tb1 INNER JOIN tb2 ON 条件 所 ...

  6. MyBatis 实践 -动态SQL/关联查询

    MyBatis 实践 标签: Java与存储 动态SQL 动态SQL提供了对SQL语句的灵活操作,通过表达式进行判断,对SQL进行拼接/组装. if 对查询条件进行判断,如果输入参数不为空才进行查询条 ...

  7. MyBatis之多表关联查询

    1使用resultType.ResultMap处理返回结果 处理返回结果 resultType:指定返回值结果的完全限定名,处理多表查询的结果. 多表查询需要定义vo封装查询的结果. 需求:查询部门和 ...

  8. mybatis mapper.xml 写关联查询 运用 resultmap 结果集中 用 association 关联其他表 并且 用 association 的 select 查询值 报错 java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for mybatis.map

    用mybaits 写一个关联查询 查询商品表关联商品规格表,并查询规格表中的数量.价格等,为了sql重用性,利用 association 节点 查询 结果并赋值报错 商品表的mapper文件为Gooo ...

  9. Mybatis框架六:关联查询

    这里搞一个测试场景: 用户和订单,一位用户可以有多个订单,而每个订单只属于一位用户 以用户为中心,相对于订单:一对多 以订单为中心,当对于用户:一对一 两张表结构: 订单表: 用户表: 对应的POJO ...

随机推荐

  1. USACO翻译:USACO 2012 JAN三题(2)

    USACO 2012 JAN(题目二) 一.题目概览 中文题目名称 叠干草 分干草 奶牛联盟 英文题目名称 stacking baleshare cowrun 可执行文件名 stacking bale ...

  2. sql编写将时间转换年月日 时分格式

    SELECT SUBSTRING(CONVERT(varchar(100),时间字段, 22),0,15) AS aa  FROM  表名

  3. NLP常用工具

    1.统计类工具:可参见[统计学习常用Python扩展包] 2.linux自带工具:可参见[[整理]Linux常用文本处理命令] 3.简繁转换工具:opencc Open Chinese Convert ...

  4. Mpale 在汽车底盘悬架系统公差分析应用

    汽车底盘的作用是接受发动机的动力,使车轮转动,并保证汽车按驾驶员的操纵正常行驶.底盘包括传动系统.行驶系统.转向系统和制动系统这四大部分,通常,这四大系统也简称为传动系.行驶系.转向系和制动系.悬架是 ...

  5. JS简介,如何快熟JS。打下结实基础。

    JS决定网页的行为,有行为就有逻辑,而计算机只是人工智能,你给它什么样的指令,它就会按照你的指令去运行. 有人会问,既然是给出指令,那还需要什么逻辑? 这里我举一个简单的小例子来说明一下! 到你想输入 ...

  6. FFT时域与频域的关系,以及采样速率与采样点的影响

    首先对于FFT来说,输入的信号是一个按一定采样频率获得的信号序列,而输出是每个采样点对应的频率的幅度(能量). 下面详细分析: 在FFT的输出数据中,第一个值是直流分量的振幅(这样对应周期有无穷的可能 ...

  7. tcpdump的简单使用

    tcpdump可以将网络中传送的数据包的“头”完全截获下来提供分析 1.tcpdump host 192.168.8.49         获取主机192.168.8.49接收到和发出的所有分组 2. ...

  8. 非常郁闷的 .NET中程序集的动态加载

    记载这篇文章的原因是我自己遇到了动态加载程序集的问题,而困扰了一天之久. 最终看到了这篇博客:http://www.cnblogs.com/brucebi/archive/2013/05/22/Ass ...

  9. Guava库介绍之集合(Collection)相关的API

    作者:Jack47 转载请保留作者和原文出处 欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 本文是我写的Google开源的Java编程库Guava系列之一,主要介 ...

  10. 垃圾回收机制GC知识再总结兼谈如何用好GC

    一.为什么需要GC 应用程序对资源操作,通常简单分为以下几个步骤: 1.为对应的资源分配内存 2.初始化内存 3.使用资源 4.清理资源 5.释放内存 应用程序对资源(内存使用)管理的方式,常见的一般 ...