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.创建表和数据 创建一张教师表和班级表,这里我们假设一个老师只负责教一个班,那么老师和班级之间的关系就是一种一对一的关 ...
随机推荐
- 分享STM32 FLASH 擦除(以及防止误擦除程序代码)、写入
编译环境:我用的是(Keil)MDK4.7.2 stm32库版本:我用的是3.5.0一.本文不对FLASH的基础知识做详细的介绍,不懂得地方请查阅有关资料. 对STM32 内部FLASH进行编程操 ...
- Django ContentTypes框架使用场景
Django contenttypes是一个非常有用的框架,主要用来创建模型间的通用关系(generic relation).不过由于其非常抽象, 理解起来并不容易.当你创建一个django项目的时候 ...
- Goexit
package main import ( "fmt" "runtime" ) func test() { defer fmt.Println("cc ...
- 移动端touch触摸事件(滑动效果和手势操作)
一.定义 ①touch是移动端的触摸事件,而且是一组事件,主要有以下事件: touchstart 事件:当手指触摸屏幕的时候触发 touchmove 事件:当手指在屏幕来回滑动的时候触发 touche ...
- 控制论模型&心流模型&波模型
1.控制论模型 这是对设定的目标,通过多次输入和输出,反馈调节,最终达成目标的方法.广泛运用于自然科学与社会科学中.反馈的周期长短决定了调节精度的大小以及达到目标的速度.反馈结果与目标背离的立即纠正, ...
- 洛谷 P3884 [JLOI2009]二叉树问题
目录 题目 思路 \(Code\) 题目 P3884 [JLOI2009]二叉树问题 思路 深搜统计深度,倍增\(\text{LCA}\)求边数 \(Code\) #include<iostre ...
- 57、Spark Streaming: window滑动窗口以及热点搜索词滑动统计案例
一.window滑动窗口 1.概述 Spark Streaming提供了滑动窗口操作的支持,从而让我们可以对一个滑动窗口内的数据执行计算操作.每次掉落在窗口内的RDD的数据, 会被聚合起来执行计算操作 ...
- CSS — BEM 命名规范
推荐阅读: https://juejin.im/post/5b925e616fb9a05cdd2ce70d 1 什么是 BEM 命名规范 Bem 是块(block).元素(element).修饰符(m ...
- 开机启动类似于Tencent Upd的弹窗解决方法
1.开机启动的程序,后台启动自动升级的exe,每次开机都弹出弹窗,一不小心就点错了,神烦. 解决方式:直接在windows系统 [ 本地安全策略>软件限制策略>其他规则 ] 里面把弹出的 ...
- 除法运算时的一个常见异常之java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
一.背景 今天在计算库存消耗百分比(消耗的库存/总库存)的时候遇到了一个错误,java.lang.ArithmeticException: Non-terminating decimal expans ...