Mybatis多表管理查询

多表关联关系分析:

多表关联:至少两个表关联.分析多表关系的经验技巧:从一条记录出发,不要从表整体去分析,比如分析A表和B表关系,A表中的一条记录对应B表中的几条记录,如果是一条,那么A到B就是一对一;如果是多条,那么A到B就是一对多.

  一对一:例如从订单表出发,到用户表,是一对一的关系.

  一对多,例如从用户表出发,到订单表,一条用户数据可以在订单表中存在多条记录,这就是一对多.通过主外键来体现一对多的表结构.一的一方是主表,多的一方是从表,从表中有一个字段是外键,指向了主表的主键.

  多对多,用户和角色表,一个用户可以有很多角色,一个角色有很多用户,多对多通过中间表来表现.

多表关联的sql语句表达分析

  笛卡尔积
    SELECT *FROM USER,orders

  关联查询

  内关联 inner join on

    SELECT * FROM USER u,orders o WHERE u.id=o.user_id

    SELECT * FROM USER u INNER JOIN orders o ON u.id=o.user_id;

  左外连接

    SELECT * FROM USER u LEFT JOIN orders o ON u.id=o.user_id

    右外连接 

    SELECT * FROM USER u RIGHT JOIN orders o ON u.id=o.user_id

一对一查询

需求:查询订单表全部数据,关联查询出订单对应的用户数据(username address)。

Sql语句自己来写,Mybatis只是帮我们执行sql语句同时封装结果集。

SQL语句:

SELECT o.id,o.user_id,o.number,o.note,u.address,u.username 
FROM orders
o LEFT JOIN USER u ON o.user_id=u.id

对于查询结果集,没有对应的pojo对象,因此MyBatis框架也不能封装结果集,但是可以将User对象放在Orders对象中,因为一对一关系,一个Orders对象可以对应一个User对象。需要手动映射方式,实现查询结果集封装。
pojo对象

public class Orders {
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
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 getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Date getCreatetime() {
return createtime;
}
public void setCreatetime(Date createtime) {
this.createtime = createtime;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
@Override
public String toString() {
return "Orders{" +
"id=" + id +
", userId=" + userId +
", number='" + number + '\'' +
", createtime=" + createtime +
", note='" + note + '\'' +
", user=" + user +
'}';
}
}

association(联合)标签,实现手动映射

  propery属性:封装的pojo对象
  javaType属性:封装的pojo对象类型

 <mapper namespace="com.itheima.mapper.OrdersMapper">
<select id="queryOrdersUser" resultMap="ordersUserResultType" >
SELECT o.id,o.user_id,o.number,o.note,u.address,u.username FROM
orders o LEFT JOIN USER u
ON o.user_id=u.id
</select>
<resultMap id="ordersUserResultType" type="orders">
<id column="id" property="id"></id>
<result column="user_id" property="userId"></result>
<result column="number" property="number"></result>
<result column="createtime" property="createtime"></result>
<result column="note" property="note"></result>
<!-- 手动映射,配置User对象-->
<association property="user" javaType="user">
<id column="user_id" property="id"></id>
<result column="address" property="address"></result>
<result column="username" property="username"></result>
</association>
</resultMap>
</mapper>

@Test
public void queryOrdersUser(){
SqlSession sqlSession = sqlSessionFactory.openSession();
OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);
List<Orders> list = mapper.queryOrdersUser();
if(list != null && list.size() > 0){
for (Orders orders : list){
System.out.println(orders);
}
}
sqlSession.close();
}

往往还有另外一种方式:新创建一个pojo,这个pojo里面增加两个字段username和address,然后使用resultType自动映射即可,但是这种方式不推荐,因为pojo多了之后很混乱,不容易做技术管理,而且这点方式也有点low
一对多查询
查询全部用户数据,关联查询出订单数据。

SQL语句:SELECT
u.id,u.username,u.sex,u.birthday,u.address,o.user_id,o.id,o.number,o.createtime,o.note
FROM USER u LEFT JOIN orders o ON u.id=o.user_id

一个用户对应多个订单数据,因此在pojo中,订单对象是存储在集合中,并保存在User对象中

pojo对象

public class User {
  
private int id;
  
private String username;
  
private String sex;
  
private Date birthday;
  
private String address;
  
private List<Orders> ordersList;
   public List<Orders> getOrdersList() {
    return ordersList;
 }
   public void setOrdersList(List<Orders> ordersList) {
    this.ordersList = ordersList;
 }
   public int getId() {
    return id;
 }
   public void setId(int id) {
    this.id = id;
 }
   public String getUsername() {
    return username;
 }
   public void setUsername(String username) {
    this.username = username;
 }
   public String getSex() {
    return sex;
 }
   public void setSex(String sex) {
    this.sex = sex;
 }
   public Date getBirthday() {
    return birthday;
 }
   public void setBirthday(Date birthday) {
    this.birthday = birthday;
 }
   public String getAddress() {
    return address;
 }
   public void setAddress(String address) {
    this.address = address;
 }
  
@Override
   public String toString() {
    return "User{" +
        "id=" + id +
        ", username='" + username + '\'' +
        ", sex='" + sex + '\'' +
        ", birthday=" + birthday +
        ", address='" + address + '\'' +
collection标签:
property属性:封装的对应的属性名
ofType属性:已经指定为一个集合List,需要指定集合中的泛型
        ", ordersList=" + ordersList +
        '}';
 }
} 
    <select id="queryUserOrders" resultMap="userOrdersResultMap">
 SELECT u.id,u.username,u.sex,u.birthday,u.address,o.user_id,o.id,
 o.number,o.createtime,o.note
 FROM USER u LEFT JOIN orders o
 ON u.id=o.user_id
</select>
    <resultMap id="userOrdersResultMap" type="user">
 <id column="id" property="id"></id>
 <result column="username" property="username"></result>
 <result column="sex" property="sex"></result>
 <result column="birthday" property="birthday"></result>
 <result column="address" property="address"></result>
 <collection property="ordersList" ofType="orders">
   <id column="id" property="id"></id>
   <result column="user_id" property="userId"></result>
   <result column="number" property="number"></result>
   <result column="createtime" property="createtime"></result>
   <result column="note" property="note"></result>
 </collection>
</resultMap>
    @Test
public void queryUserOrders(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list = mapper.queryUserOrders();
if(list != null && list.size() > 0){
for(User user : list){
System.out.println(user);
}
}
sqlSession.close();
}

多对多查询

多对多:双向的一对多,从A表到B表出发是一对多的关系,从B表出发到A表也是一对多的关系
SQL语句:

SELECT r.RID,r.RNAME,r.RDESC,u.id,u.username,u.sex,u.birthday,u.address
FROM role r LEFT JOIN user_role ur
ON r.RID=ur.RID LEFT JOIN USER u ON u.id=ur.UID

pojo对象

public class Role {
private Integer rid;
private String rname;
private String rdesc;
private List<User> userList;
public Integer getRid() {
return rid;
}
public void setRid(Integer rid) {
this.rid = rid;
}
public String getRname() {
return rname;
}
public void setRname(String rname) {
this.rname = rname;
}
public String getRdesc() {
return rdesc;
}
public void setRdesc(String rdesc) {
this.rdesc = rdesc;
}
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
@Override
public String toString() {
return "Role{" +
"rid=" + rid +
", rname='" + rname + '\'' +
", rdesc='" + rdesc + '\'' +
", userList=" + userList +
'}';
}
}

手动映射roleMapper.xml

  <mapper namespace="com.itheima.mapper.RoleMapper">
<select id="queryRoleUser" resultMap="roleUserResultMap">
SELECT
r.RID,r.RNAME,r.RDESC,u.id,u.username,u.sex,u.birthday,u.address FROM
role r LEFT JOIN user_role ur ON r.RID=ur.RID LEFT JOIN USER u ON
u.id=ur.UID
</select>
<resultMap id="roleUserResultMap" type="role">
<id column="rid" property="rid"></id>
<result column="rname" property="rname"></result>
<result column="rdesc" property="rdesc"></result>
<result column="" property=""></result>
<collection property="userList" ofType="user">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
<result column="address" property="address"></result>
</collection>
</resultMap>
</mapper>
    @Test
public void queryRoleUser() {
  SqlSession sqlSession = sqlSessionFactory.openSession();
  RoleMapper mapper = sqlSession.getMapper(RoleMapper.class);
  List<Role> roleList = mapper.queryRoleUser();
  if (roleList != null && roleList.size() > 0) {
    for (Role role : roleList) {
      System.out.println(role);
   }
 }
  sqlSession.close();
}

小案例:

用户主键查询用户信息

<!--
 主键查询用户信息
-->
<select id="queryUserById" resultType="user" parameterType="int">
 select * from user where id = #{id}
</select>
/**
* 主键查询用户信息
*/
@Test
public void queryUserById(){
  SqlSession sqlSession = sqlSessionFactory.openSession();
  UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
  User user = userMapper.queryUserById(1);
  System.out.println(user);
  sqlSession.close();
}

订单查询用户信息 一对一

<!--
 订单,查询订单对应的用户
-->
<select id="queryOrdersUser" resultMap="queryOrdersUserResultMap">
 SELECT
o.id,o.user_id,o.number,o.createtime,o.note,u.username,u.address FROM
orders o LEFT JOIN USER u
 ON o.user_id = u.id
</select>
用户查询订单信息 一对多
<resultMap id="queryOrdersUserResultMap" type="orders">
  <id column="id" property="id"></id>
  <result column="user_id" property="userId"></result>
  <result column="number" property="number"></result>
  <result column="createtime" property="createtime"></result>
  <result column="note" property="note"></result>
  <association property="user" javaType="user">
    <id column="user_id" property="id"></id>
    <result column="username" property="username"></result>
    <result column="address" property="address"></result>
  </association>
</resultMap>
/**
* 订单查询用户信息,一对一
*/
@Test
public void queryOrdersUser(){
  System.out.println();
  SqlSession sqlSession = sqlSessionFactory.openSession();
  OrdersMapper ordersMapper =
sqlSession.getMapper(OrdersMapper.class);
  List<Orders> ordersList = ordersMapper.queryOrdersUser();
  if(ordersList != null && ordersList.size() > 0){
    for (Orders orders : ordersList){
      System.out.println(orders);
   }
 }
  sqlSession.close();
}

用户查询订单信息 一对多

<!--
 根据用户查询订单,一对多
-->
<select id="queryUserOrders" resultMap="queryUserOrdersResultMap">
 SELECT u.id,u.username,u.sex,u.birthday,u.address,o.id
oid,o.number,o.createtime FROM USER u LEFT JOIN orders o
一对一延迟加载
针对的是关联对象
用户和订单从面相对象的角度来说就是关联对象,当只需要订单数据,尚不需要用户数据的时
候,就不应该去查询用户表,啥时候用到用户数据,啥时候查询
 ON u.id = o.user_id
</select>
<resultMap id="queryUserOrdersResultMap" type="user">
  <id column="id" property="id"></id>
  <result column="username" property="username"></result>
  <result column="sex" property="sex"></result>
  <result column="birthday" property="birthday"></result>
  <result column="address" property="address"></result>
  <collection property="ordersList" ofType="orders">
    <id column="oid" property="id"></id>
    <result column="number" property="number"></result>
    <result column="createtime" property="createtime"></result>
  </collection>
</resultMap>
/**
* 用户查询订单,一对多
*/
@Test
public void queryUserOrders(){
  System.out.println();
  SqlSession sqlSession = sqlSessionFactory.openSession();
  UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  List<User> list = mapper.queryUserOrders();
  if(list != null && list.size() > 0){
    for(User user : list){
      System.out.println(user);
   }
 }
  sqlSession.close();
}

一对一延迟加载

针对的是关联对象

用户和订单从面相对象的角度来说就是关联对象,当只需要订单数据,尚不需要用户数据的时候,就不应该去查询用户表,啥时候用到用户数据,啥时候查询
       关联的sql语句肯定要拆分了

association标签
column属性:关联查询条件的属性
select属性:指定另一个查询语句

<!--
 订单,查询订单对应的用户
 拆分SQL语句,实现延迟加载
-->
<select id="queryOrdersUser" resultMap="queryOrdersUserResultMap">
 SELECT o.id,o.user_id,o.number,o.createtime,o.note FROM orders o
</select>
<resultMap id="queryOrdersUserResultMap" type="orders">
  <id column="id" property="id"></id>
  <result column="user_id" property="userId"></result>
  <result column="number" property="number"></result>
  <result column="createtime" property="createtime"></result>
  <result column="note" property="note"></result>
  <!--
   column指定查询的参数
   select关联其他查询语句
  -->
  <association property="user" javaType="user" column="user_id"
select="queryUserByUserId">
   <!-- <id column="user_id" property="id"></id>
    <result column="username" property="username"></result>
    <result column="address" property="address"></result>-->
  </association>
</resultMap>
<select id="queryUserByUserId" parameterType="int" resultType="user">
 select id,username,sex,birthday,address from user where id=#
{user_id}
</select>
/**
* 订单查询用户信息,一对一
* 延迟加载
*/
@Test
public void queryOrdersUser(){
  System.out.println();
  SqlSession sqlSession = sqlSessionFactory.openSession();
  OrdersMapper ordersMapper =
sqlSession.getMapper(OrdersMapper.class);
  List<Orders> ordersList = ordersMapper.queryOrdersUser();
  if(ordersList != null && ordersList.size() > 0){
    for (Orders orders : ordersList){
      System.out.println(orders);
      System.out.println(orders.getUser());
   }
 }
  sqlSession.close();
}

开启延迟加载配置

<settings>
<!-- 开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 关闭立即加载-->
<setting name="aggressiveLazyLoading" value="false" />
<!-- 设定tostring等方法延迟加载-->
<setting name="lazyLoadTriggerMethods" value="true" />
</settings>

一对多延迟加载

<!--
根据用户查询订单,一对多
延迟加载,拆分SQL语句
-->
<select id="queryUserOrders" resultMap="queryUserOrdersResultMap">
SELECT u.id,u.username,u.sex,u.birthday,u.address FROM USER u
</select>
<resultMap id="queryUserOrdersResultMap" type="user">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
<result column="address" property="address"></result>
<collection property="ordersList" ofType="orders" column="id"
select="queryOrdersByUserId">
<!-- <id column="oid" property="id"></id>
<result column="number" property="number"></result>
<result column="createtime" property="createtime"></result>-->
</collection>
</resultMap>
<select id="queryOrdersByUserId" parameterType="int" resultType="orders">
select id,number,createtime from orders where user_id = #{user_id}
</select>
/**
* 用户查询订单,一对多
* 延迟加载
*/
@Test
public void queryUserOrders(){
System.out.println();
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list = mapper.queryUserOrders();
if(list != null && list.size() > 0){
for(User user : list){
System.out.println(user);
List<Orders> ordersList = user.getOrdersList();
System.out.println(ordersList);
}
}
sqlSession.close();
}

MyBatis一级缓存

一级缓存:是SqlSession级别的,也就是同一个SqlSession内执行相同select语句的时候,不
再去查询数据库,而是从Mybatis内部的缓存内存结构去直接拿到数据。
缓存失效时机:
sqlSession关闭
sqlSession提交事务(意味着可能是一个增删改的动作,需要更新缓存,那么这个时候Mybatis就会把已有的一级缓存给清理掉)
MyBatis二级缓存

开启二级缓存

<setting name="cacheEnabled" value="true" />

<!-- 具体Mapper.xml配置-->
<cache></cache>

执行结果

/**
* 主键查询用户信息
*/
@Test
public void queryUserById(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.queryUserById(1);
System.out.println(user);
sqlSession.close();
SqlSession sqlSession1 = sqlSessionFactory.openSession();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
User user1 = userMapper1.queryUserById(1);
System.out.println(user1);
sqlSession1.close();
}

ratio,两次都会去查询二级缓存,但是第一次的时候缓存中没有数据,第二次才有,所以命中率是0.5

注意:Mybatis的二级缓存在使用的时候有很多限制,在实际开发的时候Mybatis的二级缓存应用很少

比如在其他mapper.xml文件中有对user表的更新,那么UserMapper.xml的二级缓存就可能错误,我们很难要求对一张表的操作全部放到同一个mapper.xml中。

myBatis框架之入门(四)的更多相关文章

  1. Mybatis框架基础入门(四)--SqlMapConfig.xml配置文件简介

    SqlMapConfig.xml中配置的内容和顺序如下: properties(属性) settings(全局配置参数) typeAliases(类型别名) typeHandlers(类型处理器) o ...

  2. 用IntelliJ IDEA 开发Spring+SpringMVC+Mybatis框架 分步搭建四:配置springmvc

    在用IntelliJ IDEA 开发Spring+SpringMVC+Mybatis框架 分步搭建三:配置spring并测试的基础上 继续进行springmvc的配置 一:配置完善web.xml文件

  3. MyBatis框架之入门(三)

    使用原始dao层进行开发 UserMapper层接口 public interface UserMapper { /** * 通过id查询用户 * @param id * @return */ Use ...

  4. MyBatis框架——快速入门

    主流的ORM框架(帮助开发者实现数据持久化工作的框架): 1.MyBatis: 半自动化ORM框架,半自动:指框架只完成一部分功能,剩下的工作仍需开发者手动完成. MyBatis 框架没有实现 POJ ...

  5. Mybatis框架基础入门(三)--Mapper动态代理方式开发

    使用MyBatis开发Dao,通常有两个方法,即原始Dao开发方法和Mapper动态代理开发方法. 原始Dao开发方法需要程序员编写Dao接口和Dao实现类,此方式开发Dao,存在以下问题: Dao方 ...

  6. myBatis框架之入门(一)

    什么是框架 框架就是一个架子,表演节目,舞台已经搭建好,表演什么节目,看自己的需求了. 框架是一个半成品,对于Java语言来说,框架就是封装了别人的代码.在框架的基础上我们在进一步开发,拿来主义. 框 ...

  7. (转)MyBatis框架的学习(四)——Mapper.xml文件中的输入和输出映射以及动态sql

    http://blog.csdn.net/yerenyuan_pku/article/details/71893689 前面对MyBatis框架的学习中,我们对Mapper.xml映射文件多少有些了解 ...

  8. mybatis框架快速入门

    通过快速入门示例,我们发现使用mybatis 是非常容易的一件事情,因为只需要编写 Dao 接口并且按照 mybatis要求编写两个配置文件,就可以实现功能.远比我们之前的jdbc方便多了.(我们使用 ...

  9. Mybatis框架基础入门(一)--简介及优势

    一.什么是Mybatis 这里借用官网的一句话介绍什么是mybatis:MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC ...

随机推荐

  1. Mac下 homebrew 安装mysql

    操作步骤 安装homebrew brew install mysql 安装mysql 安装完成之后,可以运行命令启动mysql服务 mysql.server start 然后输入命令设置密码 mysq ...

  2. 【Spring Boot学习之十二】mybatis3 分页打印sql日志

    环境 eclipse 4.7 jdk 1.8 Spring Boot 1.5.2 参考: mybatis手册 Mybatis的插件 PageHelper 分页查询使用方法MyBatis中Like语句使 ...

  3. 【Spring Boot学习之三】Spring Boot整合数据源

    环境 eclipse 4.7 jdk 1.8 Spring Boot 1.5.2 一.Spring Boot整合Spring JDBC 1.pom.xml <project xmlns=&quo ...

  4. 在ensp上配置RIPv2的认证

    原理 实验模拟 实验拓扑 测试连通性 搭建rip网络 查看路由表 R3模拟攻击首先把在自己的的ip通告网段 配置完成后查看一下路由表 可以看到已经非法获取R1R2的网段信息,那么现在我们R3可以向两个 ...

  5. (语法基础)浅谈面向切面编程(AOP)

    一:前言 面向切面编程是一个很重要的编程思想,想要写出一个便于维护的程序,理解AOP并且能熟练的在实际编程中合理的运用AOP思想是很有必要的 二:AOP的基本概念 基础概念:AOP中文翻译面向切面编程 ...

  6. 嵌入式02 STM32 实验09 独立/窗口看门狗

    一.独立看门狗和窗口看门狗 看门狗:单片机系统在外界的干扰下会出现程序跑飞的现象导致死循环,或者崩溃,看门狗电路就是为了避免这种情况的发生,看门狗的作用就是在一定的事件内(通过计数器实现)若没有收到喂 ...

  7. RWMutex:共享/专有的递归互斥锁

    具有共享/独占访问权限,且具有升级/降级功能的互斥锁 介绍 我的目标是创建可以充当读/写锁定机制的对象.任何线程都可以锁定它以进行读取,但是只有一个线程可以锁定它以进行写入.在写入线程释放它之前,所有 ...

  8. 第4课,python 条件语句if用法

    主题: 智能对话程序的设计 前言: 在编程中存在三大逻辑结构:顺序结构,分支结构(用条件语句if构成),循环结构.其中循环结构能完成,重复次数多,庞大的工作: 分支结构优势不在完成的多,但占有重要位置 ...

  9. Spring Security的RBAC数据模型嵌入

    1.简介 ​ 基于角色的权限访问控制(Role-Based Access Control)作为传统访问控制(自主访问,强制访问)的有前景的代替受到广泛的关注.在RBAC中,权限与角色相关联,用户通过成 ...

  10. SpringBoot使用mybatis,发生:Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured

    最近,配置项目,使用SpringBoot2.2.1,配置mybatis访问db,配好后,使用自定义的数据源.启动发生: APPLICATION FAILED TO START ************ ...