SpringBoot+MyBatis多表联合查询
SpringBoot+MyBatis多表联合查询写在前面联合查询在实际工作中用的并不多,因为很多表的数据比较大,或者说未来比较大的表,都要谨慎使用联合查询数据准备建表语句create table m_user(idbigintnot nullprimary key,usernamevarchar(64)null,avatarvarchar(255) null,emailvar
SpringBoot+MyBatis多表联合查询
写在前面
联合查询在实际工作中用的并不多,因为很多表的数据比较大,或者说未来比较大的表,都要谨慎使用联合查询
数据准备
建表语句
create table m_user
(
id bigint not null
primary key,
username varchar(64) null,
avatar varchar(255) null,
email varchar(64) null,
password varchar(64) null,
status int(5) not null,
created datetime null,
last_login datetime null
);
create index UK_USERNAME
on m_user (username);
create table m_blog
(
id bigint not null
primary key,
user_id bigint not null,
title varchar(255) not null,
description varchar(255) not null,
content longtext null,
created datetime not null on update CURRENT_TIMESTAMP,
status tinyint null
)
charset = utf8mb4;
插入数据
插入数据(数据很多都是没用的,那都是我平时自己测试用的,但是不影响使用
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (1, '文章一', 'https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg', 'zhangxi@qq.com', '96e79218965eb72c92a549dd5a330112', 0, '2021-06-02 17:52:01', null);
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (2, '张liu', 'new', 'zhangliu@qq.com', '123456', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (3, '王wu', 'old', 'wangwu@qq.com', '2222222', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (4, 'si', 'sisi', 'sisisi', 'sisisisi', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (5, 'wu', 'wuwu', 'wuwuwu', 'wuwuwuwuwu', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (6, 'liu', 'liuliu', 'liuliuliu', 'liuliuliuliu', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (7, 'qi', 'qiqi', 'qiqiqi', 'qiqiqiqi', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (8, 'ba', 'baba', 'bababa', 'babababa', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (9, 'jiu', 'jiujiu', 'jiujiujiu', 'jiujiujiujiu', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (10, 'shi', 'shishi', 'shishishi', 'shishishishi', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (11, 'shiyi', 'shiyiyi', 'shiyiyiyi', 'shiyiyiyiyi', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (12, 'shier', 'shierer', 'shiererer', 'shierererer', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (13, 'shisan', 'shisansan', 'shisansansan', 'shisansansansan', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (14, 'shisi', 'shisisi', 'shisisisi', 'shisisisisi', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (15, 'shiwu', 'shiwuwu', 'shiwuwuwu', 'shiwuwuwuwu', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (16, 'shiliu', 'shiliuyliu', 'shiliuliuliu', 'shiliuliuliuliu', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (17, 'shiqi', 'shiqiqi', 'shiqiqiqi', 'shiqiqiqiqi', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (18, 'shiba', 'shibaba', 'shibabababa', 'shibababababa', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (19, 'shijiu', 'shijiujiu', 'shijiujiujiu', 'shijiujiujiujiu', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (21, 'ershi', 'ershishi', 'ershishishi', 'ershishishishi', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_user (id, username, avatar, email, password, status, created, last_login) VALUES (22, 'ershiyi', 'ershiyiyi', 'ershiyiyiyi', 'ershiyiyiyiyi', 0, '2021-06-02 17:52:01', '2021-06-02 17:52:01');
INSERT INTO testgroup.m_blog (id, user_id, title, description, content, created, status) VALUES (1, 1, '文章一', '第一篇文章', '第一篇文章第一篇文章第一篇文章', '2021-06-17 17:36:11', 0);
INSERT INTO testgroup.m_blog (id, user_id, title, description, content, created, status) VALUES (2, 1, '文章一2', '第一篇文章', '第一篇文章第一篇文章第一篇文章', '2021-06-17 17:36:11', 0);
INSERT INTO testgroup.m_blog (id, user_id, title, description, content, created, status) VALUES (3, 2, '文章一3', '第一篇文章', '第一篇文章第一篇文章第一篇文章', '2021-06-18 11:38:56', 0);
INSERT INTO testgroup.m_blog (id, user_id, title, description, content, created, status) VALUES (4, 3, '文章一4', '第一篇文章', '第一篇文章第一篇文章第一篇文章', '2021-06-18 11:38:56', 0);
INSERT INTO testgroup.m_blog (id, user_id, title, description, content, created, status) VALUES (5, 2, '文章一5', '第一篇文章', '第一篇文章第一篇文章第一篇文章', '2021-06-18 11:38:56', 0);
INSERT INTO testgroup.m_blog (id, user_id, title, description, content, created, status) VALUES (6, 4, '文章一6', '第一篇文章', '第一篇文章第一篇文章第一篇文章', '2021-06-18 11:38:56', 0);
INSERT INTO testgroup.m_blog (id, user_id, title, description, content, created, status) VALUES (7, 4, '文章一7', '第一篇文章', '第一篇文章第一篇文章第一篇文章', '2021-09-29 17:45:29', 0);
INSERT INTO testgroup.m_blog (id, user_id, title, description, content, created, status) VALUES (8, 5, '文章一8', '第一篇文章', '第一篇文章第一篇文章第一篇文章', '2021-11-14 14:12:37', 0);
INSERT INTO testgroup.m_blog (id, user_id, title, description, content, created, status) VALUES (9, 5, '文章一8', '第一篇文章', '第一篇文章第一篇文章第一篇文章', '2021-11-14 14:12:37', 0);
INSERT INTO testgroup.m_blog (id, user_id, title, description, content, created, status) VALUES (11, 6, '文章一8', '第一篇文章', '第一篇文章第一篇文章第一篇文章', '2021-11-14 14:12:37', 0);
entity层
package com.newcrud.entity;
import lombok.Data;
@Data
public class Blog {
private Integer id;
private Integer user_id;
private String title;
private String description;
private String content;
private String created;
private Integer status;
}
package com.newcrud.entity;
import lombok.Data;
@Data
public class User {
private Integer id;
private String username;
private String avatar;
private String email;
private String password;
private Integer status;
private String created;
private String last_login;
}
多表联合查询的思路
首先我们来举一个例子,就像上面两张表
1、作者和多条博客为一对多的关系,也就是一条m_user对应了多条m_blog的数据
2、一条博客和作者为一对一的关系,也就是一条m_blog对应了一条m_user的数据
对于一对一,我们可以在m_blog的entity层加入一个User实例,但是对于一对多,我们就需要在m_user的entity层加入一个List,里面的内容为User。然后我们再去编写mybatis的xml文件,对于一对一,我们在xml文件中需要用到一个关键词为:association,意思为关联,对于一对多,我们在xml文件中需要用到一个关键词为:collection,意思为集合。
一对一
一篇博客对应一个作者
entity
首先,我们在m_bloh的entity里加入User
package com.newcrud.entity;
import lombok.Data;
@Data
public class Blog {
private Integer id;
private Integer user_id;
private String title;
private String description;
private String content;
private String created;
private Integer status;
private User user;
}
mapper
package com.newcrud.mapper;
import com.newcrud.entity.Blog;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface BlogMapper {
Blog getBlogById(Integer id);
}
service
package com.newcrud.service;
import com.newcrud.entity.Blog;
import java.util.List;
public interface BlogService {
Blog getBlogById(Integer id);
}
impl
package com.newcrud.service.impl;
import com.newcrud.entity.Blog;
import com.newcrud.mapper.BlogMapper;
import com.newcrud.service.BlogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BlogServiceImpl implements BlogService {
@Autowired
BlogMapper blogMapper;
@Override
public Blog getBlogById (Integer id){
return blogMapper.getBlogById(id);
}
}
mybatis的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">
<!--这里一定要把namespace写全了,要不然会找不到对应的UserMapper里的每一个方法-->
<mapper namespace="com.newcrud.mapper.BlogMapper">
<resultMap id="blog" type="Blog">
<id column="id" property="id" javaType="Integer"></id>
<result column="user_id" property="user_id" javaType="Integer"></result>
<result column="title" property="title" javaType="String"></result>
<result column="description" property="description" javaType="String"></result>
<result column="content" property="content" javaType="String"></result>
<result column="created" property="created" javaType="String"></result>
<result column="status" property="status" javaType="Integer"></result>
<!--通过association关键字将User的属性引入进来,几乎每一个表对应的xml都有一个关于本表所有字段的resultMap,其实直接把User的全表resultMap内容复制过来即可-->
<association property="user" javaType="User">
<id column="id" property="id" javaType="Integer"></id>
<result column="username" property="username" javaType="String"></result>
<result column="avatar" property="avatar" javaType="String"></result>
<result column="email" property="email" javaType="String"></result>
<result column="password" property="password" javaType="String"></result>
<result column="status" property="status" javaType="Integer"></result>
<result column="created" property="created" javaType="String"></result>
<result column="last_login" property="last_login" javaType="String"></result>
</association>
</resultMap>
<select id="getBlogById" resultType="Blog" resultMap="blog" parameterType="Integer">
select a.* from m_user a,m_blog b
<where>
<trim suffixOverrides="and" prefixOverrides="and">
b.user_id=a.id and b.id= #{id}
</trim>
</where>
</select>
</mapper>
测试类
package com.newcrud.service.impl;
import com.newcrud.entity.Blog;
import com.newcrud.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests;
import org.testng.annotations.Test;
@SpringBootTest
public class BlogServiceImplTest extends AbstractTransactionalTestNGSpringContextTests {
@Autowired
BlogServiceImpl blogService;
@Test
public void getBlogByIdTest(){
Blog blog= blogService.getBlogById(1);
User user=blog.getUser();
System.out.println(user);
}
}
结果
User(id=1, username=文章一, avatar=https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg, email=zhangxi@qq.com, password=96e79218965eb72c92a549dd5a330112, status=0, created=2021-06-02 17:52:01, last_login=null)
如果xml文件不这样写呢
我们把m_user从xml文件中摘出来
<select id="getBlogById" resultType="Blog" resultMap="blog" parameterType="Integer">
select * from m_blog
<where>
<trim suffixOverrides="and" prefixOverrides="and">
id= #{id}
</trim>
</where>
</select>
同样的测试类,结果
User(id=1, username=null, avatar=null, email=null, password=null, status=0, created=2021-06-17 17:36:11, last_login=null)
emmmm,经过分析得知,这个结果是将blog表里和user的表里有相同的字段,直接将blog的字段结果填充到了user的数据里,为什么这样谁能给我解释一下。。。
一对多
entity
关键:private List blogs; 是一个List
package com.newcrud.entity;
import lombok.Data;
import java.util.List;
@Data
public class User {
private Integer id;
private String username;
private String avatar;
private String email;
private String password;
private Integer status;
private String created;
private String last_login;
/**
* 由于一个作者对应了多篇博客,所以这里用List来加入Blog
* */
private List<Blog> blogs;
}
最好呢,将Blog的entity还原成下面这个样子,我们暂时先不还原,看看结果如何。
package com.newcrud.entity;
import lombok.Data;
@Data
public class Blog {
private Integer id;
private Integer user_id;
private String title;
private String description;
private String content;
private String created;
private Integer status;
}
mapper
package com.newcrud.mapper;
import com.newcrud.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 这里有两种方法,一种是像我们一样使用@Mapper,一种是在CrudApplication上面新增了@MapperScan备注。直接扫描了整个包
* https://www.cnblogs.com/JackpotHan/p/10286496.html
* 不过不知道为啥,不加上这个好像是不行,没办法auto,稍微有那么点尴尬
* **/
@Mapper
public interface UserMapper {
/**
* 一对多联合查询
* */
User getUserAndBlog(Integer id);
}
service
package com.newcrud.service;
import com.newcrud.entity.User;
import com.newcrud.outInParam.PageRequest;
import com.newcrud.outInParam.PageResult;
import java.util.List;
public interface UserService {
/**
* 一对多联合查询
* */
User getUserAndBlog(Integer id);
}
impl
package com.newcrud.service.impl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.newcrud.entity.User;
import com.newcrud.mapper.UserMapper;
import com.newcrud.outInParam.PageRequest;
import com.newcrud.outInParam.PageResult;
import com.newcrud.service.UserService;
import com.newcrud.utils.PageUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
/**
* 一对多联合查询
* */
@Override
public User getUserAndBlog(Integer id){
return userMapper.getUserAndBlog(id);
}
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.newcrud.mapper.UserMapper">
<resultMap id="userAndBlog" type="User">
<!--重点1:这里的column对应的值为sql语句中对应字段的别名-->
<id column="uid" property="id" javaType="Integer"></id>
<result column="username" property="username" javaType="String"></result>
<result column="avatar" property="avatar" javaType="String"></result>
<result column="email" property="email" javaType="String"></result>
<result column="password" property="password" javaType="String"></result>
<result column="ustatus" property="status" javaType="Integer"></result>
<result column="ucreated" property="created" javaType="String"></result>
<result column="last_login" property="last_login" javaType="String"></result>
<!--重点2:这里一定要用ofType不能用javaType了,因为对应的Blog是一个List,如果用javaType的话就会报错数据类型不匹配-->
<collection property="blogs" ofType="Blog" >
<id column="bid" property="id" javaType="Integer"></id>
<result column="user_id" property="user_id" javaType="Integer"></result>
<result column="title" property="title" javaType="String"></result>
<result column="description" property="description" javaType="String"></result>
<result column="content" property="content" javaType="String"></result>
<result column="bcreated" property="created" javaType="String"></result>
<result column="bstatus" property="status" javaType="Integer"></result>
</collection>
</resultMap>
<select id="getUserAndBlog" resultMap="userAndBlog" parameterType="Integer">
<!--重点3:我们查询出了一条记录所有的值,但是两个表有很多字段都是重名的怎么办,那就需要为同名字段起一个别名,别忘了修改resultMap对应字段的column值哦-->
select a.id uid,a.username,a.avatar,a.email,a.password,a.status ustatus,a.created ucreated,a.last_login,b.id bid,b.user_id,b.title,b.description,b.content,b.created bcreated,b.status bstatus from m_user a , m_blog b where a.id=b.user_id and a.id=#{id}
</select>
</mapper>
测试类
package com.newcrud.service.impl;
import com.newcrud.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;
import java.util.List;
import static org.testng.Assert.*;
@SpringBootTest
public class UserServiceImplTest extends AbstractTestNGSpringContextTests {
@Autowired
UserServiceImpl userService;
@Test
public void testGetUserById() {
User users = userService.getUserAndBlog(1);
System.out.println(users);
}
结果
User(id=1, username=文章一, avatar=https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg, email=zhangxi@qq.com, password=96e79218965eb72c92a549dd5a330112, status=0, created=2021-06-02 17:52:01, last_login=null, blogs=[Blog(id=1, user_id=1, title=文章一, description=第一篇文章, content=第一篇文章第一篇文章第一篇文章, created=2021-06-17 17:36:11, status=0, user=null), Blog(id=2, user_id=1, title=文章一2, description=第一篇文章, content=第一篇文章第一篇文章第一篇文章, created=2021-06-17 17:36:11, status=0, user=null)])
有没有注意到blog里有一段user=null,我们把entity层的Blog还原就行了
优化
一个小小的优化,一是我们并不需要返回那么多字段信息,二是实际工作中这个xml里的sql,其实更多的是使用union的方式,而不是像我们上面的那种
entity
package com.newcrud.entity;
import lombok.Data;
import java.util.List;
@Data
public class User {
private Integer id;
private String username;
private String avatar;
private String email;
private String password;
private Integer status;
private String created;
private String last_login;
/**
* 由于一个作者对应了多篇博客,所以这里用List来加入Blog
* */
private List<Blog> blogs;
}
mapper
package com.newcrud.mapper;
import com.newcrud.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 这里有两种方法,一种是像我们一样使用@Mapper,一种是在CrudApplication上面新增了@MapperScan备注。直接扫描了整个包
* https://www.cnblogs.com/JackpotHan/p/10286496.html
* 不过不知道为啥,不加上这个好像是不行,没办法auto,稍微有那么点尴尬
* **/
@Mapper
public interface UserMapper {
/**
* 一对多联合查询-union方式
* */
User getAllUserAndBlog(Integer id);
}
service
package com.newcrud.service;
import com.newcrud.entity.User;
import com.newcrud.outInParam.PageRequest;
import com.newcrud.outInParam.PageResult;
import java.util.List;
public interface UserService {
/**
* 一对多联合查询-union方式
* */
User getAllUserAndBlog(Integer id);
}
impl
package com.newcrud.service.impl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.newcrud.entity.User;
import com.newcrud.mapper.UserMapper;
import com.newcrud.outInParam.PageRequest;
import com.newcrud.outInParam.PageResult;
import com.newcrud.service.UserService;
import com.newcrud.utils.PageUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
/**
* 一对多联合查询-union方式
* */
@Override
public User getAllUserAndBlog(Integer id){
return userMapper.getAllUserAndBlog(id);
}
}
mybatis的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.newcrud.mapper.UserMapper">
<resultMap id="AllUserAndBlog" type="User">
<id column="uid" property="id" javaType="Integer"></id>
<result column="username" property="username" javaType="String"></result>
<result column="email" property="email" javaType="String"></result>
<collection property="blogs" ofType="Blog">
<id column="bid" property="id" javaType="Integer"></id>
<result column="title" property="title" javaType="String"></result>
<result column="description" property="description" javaType="String"></result>
</collection>
</resultMap>
<select id="getAllUserAndBlog" resultMap="AllUserAndBlog" parameterType="Integer">
select a.id uid,a.username,a.email,b.id bid,b.title,b.description from m_user a left outer join m_blog b on a.id = b.user_id where a.id=#{id}
</select>
</mapper>
测试类
package com.newcrud.service.impl;
import com.newcrud.entity.Blog;
import com.newcrud.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.Test;
import javax.jws.soap.SOAPBinding;
import java.util.List;
import static org.testng.Assert.*;
@SpringBootTest
public class UserServiceImplTest extends AbstractTestNGSpringContextTests {
@Autowired
UserServiceImpl userService;
@Test
public void testgetAllUserAndBlog(){
User user = userService.getAllUserAndBlog(1);
System.out.println(user);
}
}
结果
User(id=1, username=文章一, avatar=null, email=zhangxi@qq.com, password=null, status=null, created=null, last_login=null, blogs=[Blog(id=1, user_id=null, title=文章一, description=第一篇文章, content=null, created=null, status=null), Blog(id=2, user_id=null, title=文章一2, description=第一篇文章, content=null, created=null, status=null)])
更多推荐
所有评论(0)