07 Mybatis的多表查询1----1对多和多对1---@Results注解用法总结
1.表与表之间的关系及其举例
表之间的关系有4种:一对多、多对一、一对一、多对多。
举例:
(1)用户和订单就是一对多
一个用户可以下多个订单
(2)订单和用户就是多对一
多个订单属于同一个用户
(3)人和身份证号就是一对一
一个人只能有一个身份证号
一个身份证号只能属于一个人
(4)老师和学生之间就是多对多
一个学生可以被多个老师教过
一个老师可以交多个学生
2.mybatis中的多表查询
示例:用户和账户
一个用户可以有多个账户
一个账户只能属于一个用户(多个账户也可以属于同一个用户)
步骤:
1、建立两张表:用户表,账户表
让用户表和账户表之间具备一对多的关系:需要使用外键在账户表中添加
2、建立两个实体类:用户实体类和账户实体类
让用户和账户的实体类能体现出来一对多的关系
3、建立两个配置文件
用户的配置文件
账户的配置文件
4、实现配置:
当我们查询用户时,可以同时得到用户下所包含的账户信息
当我们查询账户时,可以同时得到账户的所属用户信息
3.@Results注解用法总结:
MyBatis中使用@Results注解来映射查询结果集到实体类属性。
(1)@Results的基本用法。当数据库字段名与实体类对应的属性名不一致时,可以使用@Results映射来将其对应起来。column为数据库字段名,porperty为实体类属性名,jdbcType为数据库字段数据类型,id为是否为主键。
@Select({"select id, name, class_id from my_student"})
@Results({
@Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
@Result(column="name", property="name", jdbcType=JdbcType.VARCHAR),
@Result(column="class_id", property="classId", jdbcType=JdbcType.INTEGER)
})
List<Student> selectAll();
如上所示的数据库字段名class_id与实体类属性名classId,就通过这种方式建立了映射关系。
(2)@ResultMap的用法。当这段@Results代码需要在多个方法用到时,为了提高代码复用性,我们可以为这个@Results注解设置id,然后使用@ResultMap注解来复用这段代码。
@Select({"select id, name, class_id from my_student"})
@Results(id="studentMap", value={
@Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
@Result(column="class_id", property="classId", jdbcType=JdbcType.INTEGER)
})
List<Student> selectAll();
@Select({"select id, name, class_id from my_student where id = #{id}"})
@ResultMap(value="studentMap")
Student selectById(integer id);
(3)@One的用法。当我们需要通过查询到的一个字段值作为参数,去执行另外一个方法来查询关联的内容,而且两者是一对一关系时,可以使用@One注解来便捷的实现。比如当我们需要查询学生信息以及其所属班级信息时,需要以查询到的class_id为参数,来执行ClassesMapper中的selectById方法,从而获得学生所属的班级信息。可以使用如下代码。
@Select({"select id, name, class_id from my_student"})
@Results(id="studentMap", value={
@Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
@Result(column="class_id", property="myClass", javaType=MyClass.class,
one=@One(select="com.example.demo.mapper.MyClassMapper.selectById"))
})
List<Student> selectAllAndClassMsg();
(4)@Many的用法。与@One类似,只不过如果使用@One查询到的结果是多行,会抛出TooManyResultException异常,这种时候应该使用的是@Many注解,实现一对多的查询。比如在需要查询学生信息和每次考试的成绩信息时。
@Select({"select id, name, class_id from my_student"})
@Results(id="studentMap", value={
@Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
@Result(column="class_id", property="classId", jdbcType=JdbcType.INTEGER),
@Result(column="id", property="gradeList", javaType=List.class,
many=@Many(select="com.example.demo.mapper.GradeMapper.selectByStudentId"))
})
List<Student> selectAllAndGrade();
参考文献:https://blog.csdn.net/cherlshall/article/details/80950150
4.操作案例
(1)案例1
查询所有查询所有账户,及其用户名和地址信息(逻辑为:账户表account-----》用户信息表user)
<1>对数据库表Account对应的实体类Account.java进行改造,加入User对象作为成员变量
package domain; import java.io.Serializable; /**
* 数据库的account表对应的实体类
*/
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
//从表实体应该包含一个主表实体的对象引用
private User user; public User getUser() {
return user;
} public void setUser(User user) {
this.user = user;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public Integer getUid() {
return uid;
} public void setUid(Integer uid) {
this.uid = uid;
} public Double getMoney() {
return money;
} public void setMoney(Double money) {
this.money = money;
} @Override
public String toString() {
return "Account{" +
"id=" + id +
", uid=" + uid +
", money=" + money +
'}';
}
}
<2>IAccountDao中添加findAll()方法
package dao; import domain.Account;
import domain.AccountUser; import java.util.List; public interface IAccountDao { /**
* 查询所有查询所有账户,及其用户名和地址信息
* @return
*/
List<Account> findAll(); }
<3>映射关系配置
(1)IAccountDao.xml配置
ResultMap标签基本作用:建立SQL查询结果字段与实体属性的映射关系信息
标签属性id和type的含义:
id:该resultMap的标志
type:返回值的类名
子标签含义:
id:用于设置主键字段与领域模型属性的映射关系,此处主键为ID,对应id。
result:用于设置普通字段与领域模型属性的映射关系
association 为关联关系,是实现一对一的关键
1. property 为javabean中容器对应字段名
2. javaType 指定关联的类型,当使用select属性时,无需指定关联的类型
3. select 使用另一个select查询封装的结果
4. column 为数据库中的列名,与select配合使用
<?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="dao.IAccountDao"> <resultMap id="accountUserMap" type="domain.Account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!--配置所外键所关联表user的内容-->
<association property="user" column="uid" javaType="domain.User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
</association>
</resultMap> <!-- 查询所有查询所有账户,及其用户名和地址信息:方法1(更通用) -->
<!--account a 给account表起一个别名-->
<select id="findAll" resultMap="accountUserMap">
select u.*,a.id as aid,a.uid,a.money from account a , user u where u.id = a.uid;
</select> </mapper>
注解配置:IAccountDao.java文件中进行如下配置
/**
* 查询所有查询所有账户,及其用户名和地址信息
* 注解中的第四个result的column为uid(外键),连接到user表,select调用dao.IUserDao.findById()方法
* @return
*/
@Select("select * from account")
@Results(id="accountMap",value = {
@Result(id=true,column = "id",property = "id"),
@Result(column = "uid",property = "uid"),
@Result(column = "money",property = "money"),
@Result(column = "uid",property = "user",one=@One(select = "dao.IUserDao.findById",fetchType = FetchType.EAGER))
})
List<Account> findAll();
<4>测试代码
package test; import dao.IAccountDao;
import dao.IUserDao;
import domain.Account;
import domain.AccountUser;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test; import java.io.InputStream;
import java.util.List; public class MybatisTest01 { private InputStream in;
private SqlSession sqlSession;
private IAccountDao accountDao; /**
* 初始化MyBatis
* @throws Exception
*/
public void initMyBatis() throws Exception{
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); //创建SqlSessionFactory的构建者builder
SqlSessionFactory factory=builder.build(in); //利用构建者builder创建SqlSessionFactory
//3.使用工厂生产SqlSession对象
sqlSession = factory.openSession();
//4.使用SqlSessions对象创建Dao接口的代理对象
accountDao = sqlSession.getMapper(IAccountDao.class);
} /**
* 释放资源
* @throws Exception
*/
public void destroy() throws Exception{
sqlSession.commit();//提交事务
sqlSession.close();
in.close();
} /**
* 查询所有
* @throws Exception
*/
@Test
public void testFindAll()throws Exception{
initMyBatis();
List<Account> accounts = accountDao.findAll();
for (Account account : accounts) {
System.out.println(account);
System.out.println(account.getUser());
}
destroy();
} }
效果图:

(2)案例2
查询所有查询用户信息,及其拥有的账户信息(逻辑为:用户信息表user-----》账户表account)
<1>对数据库表user对应的实体类User.java进行改造,加入List<Account>作为成员变量
package domain; import java.io.Serializable;
import java.util.Date;
import java.util.List; /**
* 数据库表对应的实体类
*/
public class User implements Serializable {
//实体类的成员变量名称应该与数据库中的列名一致
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<Account> accounts; public List<Account> getAccounts() {
return accounts;
} public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
} 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 Date getBirthday() {
return birthday;
} public void setBirthday(Date birthday) {
this.birthday = birthday;
} public String getSex() {
return sex;
} public void setSex(String sex) {
this.sex = sex;
} public String getAddress() {
return address;
} public void setAddress(String address) {
this.address = address;
} @Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
<2>IUserDao中添加findAll()方法
package dao; import domain.User; import java.util.List; /**
*
*/
public interface IUserDao {
/**
* 查询所有
* @return
*/
List<User> findAll(); }
<3>IUserDao.xml
ResultMap标签基本作用:建立SQL查询结果字段与实体属性的映射关系信息
标签属性id和type的含义:
id:该resultMap的标志
type:返回值的类名
子标签含义:
id:用于设置主键字段与领域模型属性的映射关系,此处主键为ID,对应id。
result:用于设置普通字段与领域模型属性的映射关系
association 为关联关系,是实现一对一的关键
1. property 为javabean中容器对应字段名
2. javaType 指定关联的类型,当使用select属性时,无需指定关联的类型
3. select 使用另一个select查询封装的结果
4. column 为数据库中的列名
collection 为关联关系,是实现一对多的关键
1. property 为javabean中容器对应字段名
2. ofType 指定集合中元素的对象类型
3. select 使用另一个查询封装的结果
4. column 为数据库中的列名,与select配合使用
<?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="dao.IUserDao"> <resultMap id="userAccountMap" type="domain.User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<!--配置user对象中accounts集合的映射-->
<collection property="accounts" ofType="domain.Account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
</collection>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="userAccountMap">
select * from user u left outer join account a on u.id = a.uid
</select> </mapper>
注解配置:IUserDao.java文件中进行如下配置
/**
* 查询所有
* @return
*/
@Select("select * from user")
@Results(id="userMap",value = {
@Result(id=true,column = "id",property = "id"),
@Result(column = "username",property = "username"),
@Result(column = "address",property = "address"),
@Result(column = "sex",property = "sex"),
@Result(column = "birthday",property = "birthday"),
@Result(column = "id",property ="accounts",many =@Many(select = "dao.IAccountDao.findAccountById",fetchType = FetchType.EAGER))
})
List<User> findAll();
<4>测试代码
package test; import dao.IAccountDao;
import dao.IUserDao;
import domain.Account;
import domain.AccountUser;
import domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test; import java.io.InputStream;
import java.util.List; public class MybatisTest02_User { private InputStream in;
private SqlSession sqlSession;
private IUserDao userDao; /**
* 初始化MyBatis
* @throws Exception
*/
public void initMyBatis() throws Exception{
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder(); //创建SqlSessionFactory的构建者builder
SqlSessionFactory factory=builder.build(in); //利用构建者builder创建SqlSessionFactory
//3.使用工厂生产SqlSession对象
sqlSession = factory.openSession();
//4.使用SqlSessions对象创建Dao接口的代理对象
userDao = sqlSession.getMapper(IUserDao.class);
} /**
* 释放资源
* @throws Exception
*/
public void destroy() throws Exception{
sqlSession.commit();//提交事务
sqlSession.close();
in.close();
} /**
* 查询所有
* @throws Exception
*/
@Test
public void testFindAll()throws Exception{
initMyBatis();
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
System.out.println(user.getAccounts());
}
destroy();
} }
效果图:

4.Mybatis中的缓存
(1)什么是缓存
存在于内存中的临时数据。
(2)为什么使用缓存
减少和数据库的交互次数,提高执行效率。
(3)什么样的数据能使用缓存,什么样的数据不能使用
<1>适用于缓存:
经常查询并且不经常改变的。
数据的正确与否对最终结果影响不大的。
<2>不适用于缓存:
经常改变的数据
数据的正确与否对最终结果影响很大的。
例如:商品的库存,银行的汇率,股市的牌价。
5.Mybatis中的一级缓存和二级缓存
(1)一级缓存:
它指的是Mybatis中SqlSession对象的缓存。
当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。
该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中
查询是否有,有的话直接拿出来用。
当SqlSession对象消失时,mybatis的一级缓存也就消失了。
(2)二级缓存:
它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存的使用步骤:
第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
第三步:让当前的操作支持二级缓存(在select标签中配置)
07 Mybatis的多表查询1----1对多和多对1---@Results注解用法总结的更多相关文章
- 使用Mybatis进行连表查询、left join---https://blog.csdn.net/jinzhencs/article/details/51980518
使用Mybatis进行连表查询.left join https://blog.csdn.net/jinzhencs/article/details/51980518
- SpringBoot集成Mybatis实现多表查询的两种方式(基于xml)
下面将在用户和账户进行一对一查询的基础上进行介绍SpringBoot集成Mybatis实现多表查询的基于xml的两种方式. 首先我们先创建两个数据库表,分别是user用户表和account账户表 ...
- MyBatis的多表查询笔记
MyBatis的多表查询 随着学习的进步,需求的提高,我们在实际开发中用的最多的还是多表查询,就让我们一起学习MyBatis中的多表查询. 数据库准备 Class表 Student表 项目结构 这次使 ...
- MyBatis框架——多表查询
MyBatis多表查询, 从表中映射主表,使用 association 标签,通过设置 javaType 属性关联实体类: 主表映射从表,使用 collection 标签,通过 ofType 属性关联 ...
- MyBatis实现关联表查询
一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 创建一张教师表和班级表,这里我们假设一个老师只负责教一个班,那么老师和班级之间的关系就是一种一对一的关 ...
- MyBatis——实现关联表查询
原文:http://www.cnblogs.com/xdp-gacl/p/4264440.html 一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 创 ...
- Mybatis系列(三):Mybatis实现关联表查询
原文链接:http://www.cnblogs.com/xdp-gacl/p/4264440.html 一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 ...
- Mybatis多表查询(一对一、一对多、多对多)
Mybatis的多表级联查询 . 一对一可以通过<association>实现,一对多和多对多通过<collection>实现. <discriminator> 元 ...
- MyBatis—实现关联表查询
一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 创建一张教师表和班级表,这里我们假设一个老师只负责教一个班,那么老师和班级之间的关系就是一种一对一的关 ...
随机推荐
- Top 20 IoT Platforms in 2018
https://internetofthingswiki.com/top-20-iot-platforms/634/ After learning what is the internet of th ...
- Chirp信号及其生成
Chirp信号是一个典型的非平稳信号,在通信.声纳.雷达等领域具有广泛的应用. 简介 Chirp译名:啁啾(读音:“周纠”),是通信技术有关编码脉冲技术中的一种术语,是指对脉冲进行编码时,其载频在脉冲 ...
- ssh集成
导入pom依赖 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w ...
- Bzoj 1927: [Sdoi2010]星际竞速(网络流)
1927: [Sdoi2010]星际竞速 Time Limit: 20 Sec Memory Limit: 259 MB Description 10年一度的银河系赛车大赛又要开始了.作为全银河最盛大 ...
- css文字两行或者几行显示省略号
做这个省略的问题,突然发现显示省略号是有中英文区分的 我做两行的时候用的是以下代码,在是中文的情况下是么得问题,到了英文下发现不起作用了 width: 250px; overflow: hidden; ...
- java操作数据库,以页面显示学生信息为例
该部分内容实现的功能主要是:从数据库中查询数据并展示到页面,在页面中新增数据信息,在页面中修改数据信息,在页面中删除数据信息. =================stuList.jsp======== ...
- Jmeter(四十一)_图片爬虫
今天教大家用元件组合,做一个网页图片爬虫. 需要用到的元件:循环控制器+计数器+xpath提前器+函数嵌套+beanshell代码 首先我们确定一下要爬取的图片网站:https://dp.pconli ...
- CodeMirror在线代码编辑器使用
CodeMirror官网地址为:https://codemirror.net/ CodeMirror作为一款代码编辑器,其应用场景主要是在线网站写代码.如现在的leetcode.洛谷.code-vs等 ...
- 基于Hadoop爬虫网易云歌曲评论
作业要求来自于:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/3339 本次选取的是爬取歌曲<大碗宽面>的歌评数据 1.将 ...
- 使用CAS实现单点登录功能
目录 单点登录 简介 CAS服务器部署 上传tomcat服务器压缩到文件夹/usr/local/cas目录下,解压,修改tomcat文件夹名为tomcat 修改tomcat配置文件的端口号 关闭tom ...