MyBatis 实战-使用 maven 构建(4)-实现关联查询
实现需求:根据 userId 查询该 userId 写了多少 article。
映射文件配置片段:
注意:使用 association 节点的意思是,只关联一个对象。这里 Article 和 User 的对应关系是多对一,即多个 Article 对象对应一个 User 对象。
<resultMap id="articleResultMap" type="Article">
<id column="id" property="id"></id>
<result column="title" property="title"></result>
<result column="content" property="content"></result>
<association property="user" column="userid" javaType="User">
<id column="userid" property="id"></id>
<result column="userName" property="userName"></result>
<result column="userAge" property="userAge"></result>
<result column="userAddress" property="userAddress"></result>
</association>
</resultMap>
测试代码:
/**
* 关联查询
*/
@Test
public void testAssociation(){
SqlSession session = sqlSessionFactory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
List<Article> articles = userMapper.getUserArticles(1);
System.out.println("查询出的文章数量:" + articles.size());
for(Article article: articles){
System.out.println(article);
}
} finally{
session.close();
}
}
Mybatis 实战教程(mybatis in action)之四:实现关联数据的查询
有了前面几章的基础,对一些简单的应用是可以处理的,但在实际项目中,经常是关联表的查询,比如最常见到的多对一,一对多等。这些查询是如何处理的呢,这一讲就讲这个问题。我们首先创建一个 Article 这个表,并初始化数据。
SQL 语句:
# 测试表关联查询时候新增的表
drop table if exists `article`;
create table `article` (
`id` int(11) not null auto_increment,
`userid` int(11) not null,
`title` varchar(100) not null,
`content` text not null,
primary key (`id`)
) engine=innodb auto_increment=5 default charset=utf8;
-- ----------------------------
-- 添加几条测试数据
-- ----------------------------
insert into `article` values ('1', '1', 'test_title', 'test_content');
insert into `article` values ('2', '1', 'test_title_2', 'test_content_2');
insert into `article` values ('3', '1', 'test_title_3', 'test_content_3');
insert into `article` values ('4', '1', 'test_title_4', 'test_content_4');
你应该发现了,这几个文章对应的 userid 都是1,所以需要用户表 user 里面有 id=1 的数据。可以修改成满足自己条件的数据。按照 ORM 的规则,表已经创建了,那么肯定须要一个对象与之对应,所以我们增加一个 Article 的 class 。
package com.liwei.mybatis.model;
public class Article {
private int id;
private User user;
private String title;
private String content;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "Article [id=" + id + ", user=" + user + ", title=" + title
+ ", content=" + content + "]";
}
}
注意:这里文章的用户是怎么定义的,是直接定义的一个User对象。而不是int类型。(这句话的理解非常关键。)
多对一的实现
场景:在读取某个用户发表的所有文章。当然还是需要在 User.xml 里面配置 select 语句,但重点是这个 select 的 resultMap 对应什么样的数据呢。这是重点,这里要引入 association,看定义如下:
多对一的实现:
核心 xml 配置片段:
<!-- 联合查询 -->
<!-- User 联合文章进行查询的配置(多对一的方式) -->
<resultMap type="Article" id="resultUserArticleList">
<id property="id" column="aid"/>
<result property="title" column="title"/>
<result property="content" column="content"/>
<association property="user" javaType="User">
<id column="id" property="id"/>
<result column="userName" property="userName" />
<result column="userAge" property="userAge" />
<result column="userAddress" property="userAddress" />
</association>
</resultMap>
<select id="getUserArticles" parameterType="int" resultMap="resultUserArticleList">
SELECT
user.id,
user.userName,
user.userAddress,
article.id aid,
article.title,
article.content
FROM USER,
article
WHERE user.id = article.userid
AND user.id =#{id}
</select>
在接口里面写方法:
Article getUserArticles(int id);
编写测试类代码:
/**
* 测试关联查询(多对一)
*/
@Test
public void testAssociation(){
SqlSession session = sqlSessionFactory.openSession();
try {
IUserOperation userOperation = session.getMapper(IUserOperation.class);
Article articles = userOperation.getUserArticles(1);
System.out.println(articles);
} catch (Exception e) {
e.printStackTrace();
} finally{
session.close();
}
}
控制台输出:
Article [id=1, user=User [id=1, userName=summer, userAge=null, userAddress=shanghai,pudong], title=test_title, content=test_content]
大功告成。
上面还有一个小细节,没有介绍,当你在测试的时候就会发现,还须要在 Mybatis 的核心配置文件里面设置一下 Article 这个类的别名。还为 User 类和 Article 类添加了 toString 方法。
这样配置之后就可以了,将 select 语句与 resultMap 对应的映射结合起来看,简单明了。使用 association 来得到关联的用户,这是多对一的情况,因为所有的文章都是同一个用户的。
还有另外一种处理方式,可以复用我们前面已经定义好的 resultMap ,前面我们定义过一个 resultListUser,这第二种方法就是直接引用它就行了。实现:
<!-- 联合查询 -->
<!-- User 联合文章进行查询的配置(多对一的方式) -->
<resultMap type="Article" id="resultUserArticleList">
<id property="id" column="id"/>
<result property="title" column="title"/>
<result property="content" column="content"/>
<association property="user" javaType="User" resultMap="resultListUser"/>
</resultMap>
<select id="getUserArticles" parameterType="int" resultMap="resultUserArticleList">
SELECT
user.id,
user.userName,
user.userAddress,
article.id aid,
article.title,
article.content
FROM USER,
article
WHERE user.id = article.userid
AND user.id =#{id}
</select>
小结:将 association 中对应的映射独立抽取出来,可以达到复用的目的。
这个图片上显示的是有错误的,我后来才发现。因为写的 SQL 语句,对
article.id 起了别名 aid(这是为了区别 user.id,这是有必要的,因为 SQL 语句查询出来的字段如果名字一样, Mybatis 就不会知道应该如何区分了)。
所以,正确的配置片段应该是这样的:
<!-- 联合查询 -->
<!-- User 联合文章进行查询的配置(多对一的方式) -->
<resultMap type="Article" id="resultUserArticleList">
<id property="id" column="aid"/>
<result property="title" column="title"/>
<result property="content" column="content"/>
<association property="user" javaType="User" resultMap="resultListUser"/>
</resultMap>