Mybatis进阶(2)——为什么用mybatis?& 多表查询解决 & 延迟加载 & mybatis缓存 【未完待续(2)

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

<resultMap id="ownerHouseList" type="com.tianju.entity.Owner">
    <id property="id" column="id"></id>
    <result property="username" column="username"></result>
    <result property="realname" column="realname"></result>
    <result property="gender" column="gender"></result>
    <result property="password" column="password"></result>
    <result property="tel" column="tel"></result>
    <result property="identity" column="identity"></result>
    <result property="notes" column="notes"></result>
    <result property="createTime" column="create\_time"></result>
    <result property="updateTime" column="update\_time"></result>
    <result property="operator" column="operator"></result>
    <!-- 查询一下业主拥有的房子,房子的list-->
    <collection property="houseList" select="queryHouseByOwnerId" column="id"></collection>
</resultMap>

上面的resultmap对应的实体类如下:



package com.tianju.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

/**
* 业主的实体类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Owner implements Serializable { // 序列化
//public class Owner { // TODO:序列化,序列化是一级缓存和二级缓冲的事情
private Integer id;
private String username;
private String realname; // 真实姓名
private String gender;
private String password;
private String tel;
private String identity;
private String notes; // 备注信息
@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”,timezone = “GMT+8”)
private Date createTime;
@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”,timezone = “GMT+8”)
private Date updateTime;
private String operator;
// 用户拥有的房间的list
private List houseList;
// 拥有房子的数量
private Integer houseNum;
}


House的实体类如下:



package com.tianju.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
* 房屋的实体类,对应4表联查的实体类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor

public class House {
private Integer id; // 对应的是c_owner_house里面的id TODO:应该是房屋表的id
private Integer buildingId; // 楼栋号
private Integer floors; // 楼层总数
private String num; // 楼栋,1栋,2栋…
private String unit; // 单元,1单元,2单元…
private Integer storey; // 楼层
private String roomNum; // 房间号
private Double area; // 房屋面积
@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”,timezone = “GMT+8”)
private Date intoDate; // 入住时间
private Integer status;// 入住状态,0未入住,1入住
private Integer ownerId; // 业主的id;
private String ownerName; // 业主的真实姓名;
}


#### (2)查询的java接口和xml的SQL语句



/\*\*

* TODO:延迟加载
* 查询业主信息,同时获取业主名下所有的房屋信息
* @param id
* @return
*/
Owner queryOwnerById(Integer id);


对应的xml文件



<?xml version="1.0" encoding="UTF-8" ?>
<resultMap id="ownerHouseList" type="com.tianju.entity.Owner">
    <id property="id" column="id"></id>
    <result property="username" column="username"></result>
    <result property="realname" column="realname"></result>
    <result property="gender" column="gender"></result>
    <result property="password" column="password"></result>
    <result property="tel" column="tel"></result>
    <result property="identity" column="identity"></result>
    <result property="notes" column="notes"></result>
    <result property="createTime" column="create\_time"></result>
    <result property="updateTime" column="update\_time"></result>
    <result property="operator" column="operator"></result>
    <!-- 查询一下业主拥有的房子,房子的list-->
    <collection property="houseList" select="queryHouseByOwnerId" column="id"></collection>
</resultMap>
<select id="queryOwnerById" resultMap="ownerHouseList">
    SELECT * FROM user_owner WHERE id = #{id}
</select>

<!-- TODO:尝试用一下延迟加载,注意这里是resultType-->
<select id="queryHouseByOwnerId" resultType="house">
    SELECT
    c_owner_house.*,
    c_building.num,c_building.floors,c_building.unit,
    c_house.storey,c_house.roomNum,c_house.area,c_house.into_date,c_house.status,c_house.building_id,
    user_owner.realname AS ownerName
    FROM c_house
    LEFT JOIN c_building ON c_building.id = c_house.building_id
    LEFT JOIN c_owner_house ON c_owner_house.houseId = c_house.id
    LEFT JOIN user_owner ON user_owner.id = c_owner_house.ownerId
    WHERE ownerId = #{ownerId}
</select>

#### (3)对应关系分析


![在这里插入图片描述](https://img-blog.csdnimg.cn/71faf0e1e1c24a3fa8a337f78d62e53a.png)


一个简化版本的例子,新闻和新闻的评论


![在这里插入图片描述](https://img-blog.csdnimg.cn/962546a917634110b16955dc71650b47.png)


#### (4)进阶版本,根据业主id查询房屋的sql不在当前的mapper中


则原来的collection改成:




在houseMapper.xml文件中



<select id="queryHouseByOwnerId" parameterType="integer" resultType="house">
    SELECT
    c_owner_house.*,
    c_building.num,c_building.floors,c_building.unit,
    c_house.storey,c_house.roomNum,c_house.area,c_house.into_date,c_house.status,c_house.building_id,
    user_owner.realname AS ownerName

    FROM c_house
    LEFT JOIN c_building ON c_building.id = c_house.building_id
    LEFT JOIN c_owner_house ON c_owner_house.houseId = c_house.id
    LEFT JOIN user_owner ON user_owner.id = c_owner_house.ownerId

    WHERE ownerId = #{ownerId}
</select>

### 方案二:延迟加载 fetchType=“lazy”


在查看业主的基础信息时,其实并不需要知道业主名下的房屋信息,就比如看新闻的时候并不需要把评论也放出来,因此,可以采用需要的时候再查询房屋信息来实现一定程度的减少和数据库频繁交互;


延迟加载其实很简单,只需要加上在xml文件中的resultMap加上fetchType="lazy"即可



<resultMap id="ownerHouseList" type="com.tianju.entity.Owner">
    <id property="id" column="id"></id>
    <result property="username" column="username"></result>
    <result property="realname" column="realname"></result>
    <result property="gender" column="gender"></result>
    <result property="password" column="password"></result>
    <result property="tel" column="tel"></result>
    <result property="identity" column="identity"></result>
    <result property="notes" column="notes"></result>
    <result property="createTime" column="create\_time"></result>
    <result property="updateTime" column="update\_time"></result>
    <result property="operator" column="operator"></result>
    <!-- 查询一下业主拥有的房子,房子的list-->
    <collection property="houseList" select="com.tianju.dao.HouseMapper.queryHouseByOwnerId" column="id" fetchType="lazy"></collection>
</resultMap>

对应的接口java



/\*\*

* TODO:延迟加载
* 查询业主信息,同时获取业主名下所有的房屋信息
* @param id
* @return
*/
Owner queryLazyOwnerById(Integer id);


进行延迟加载的测试,只有用到houseList的时候,mybatis才会去查业主名下的房屋信息


(1)没有用到业主房屋的list



@Test
public void test(){
    // TODO:延迟加载的测试
    // 1.根据id查询业主,没有用到业主房屋的list,看执行了什么sql
    Owner ownerNoUseHouse = ownerMapper.queryLazyOwnerById(1);
    System.out.println(ownerNoUseHouse.getRealname());


    // 2.如果用到了业主房屋的list,看执行了什么sql
}

![在这里插入图片描述](https://img-blog.csdnimg.cn/36fd08fd07c549488afd87e70f43a07f.png)


(2)用到业主的房屋信息



@Test
public void lazy(){
    // TODO:延迟加载的测试
    // 1.根据id查询业主,没有用到业主房屋的list,看执行了什么sql
    Owner ownerNoUseHouse = ownerMapper.queryLazyOwnerById(1);
    // 2.如果用到了业主房屋的list,看执行了什么sql
    System.out.println("####################");
    System.out.println(ownerNoUseHouse);
}

![在这里插入图片描述](https://img-blog.csdnimg.cn/39c8fb723a6742e5bf088393ef7dbca8.png)


完整的SpringBootTest代码



package com.tianju;

import com.tianju.dao.HouseMapper;
import com.tianju.dao.OwnerMapper;
import com.tianju.entity.House;
import com.tianju.entity.Owner;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
* spring的测试类 @SpringBootTest
*/
@SpringBootTest
public class SpringTest {
@Autowired
private OwnerMapper ownerMapper;

@Autowired
private HouseMapper houseMapper;

@Test
public void test(){
    // TODO:延迟加载的测试
    // 1.根据id查询业主,没有用到业主房屋的list,看执行了什么sql
    Owner ownerNoUseHouse = ownerMapper.queryLazyOwnerById(1);
    System.out.println(ownerNoUseHouse.getRealname());


    // 2.如果用到了业主房屋的list,看执行了什么sql
}

@Test
public void lazy(){
    // TODO:延迟加载的测试
    // 1.根据id查询业主,没有用到业主房屋的list,看执行了什么sql
    Owner ownerNoUseHouse = ownerMapper.queryLazyOwnerById(1);
    // 2.如果用到了业主房屋的list,看执行了什么sql
    System.out.println("####################");
    System.out.println(ownerNoUseHouse);
}

}


### 方案三:业务拆解,查两次【建议】


从业务的角度,以及速度的角度,应该如下操作:


* 1.一般情况下,只需要根据id查询出业主的基础信息,此时只执行一条SQL语句;
* 2.如果需要用到业主名下的房屋信息,就再执行一条SQL,根据id查询业主名下的房屋数据;


![在这里插入图片描述](https://img-blog.csdnimg.cn/3d2717cab1c14cc690e1c022529de871.png)


第一种情况:只需要业主的基础信息,此时不需要查询房屋信息


![在这里插入图片描述](https://img-blog.csdnimg.cn/61f139ca49c14fbd9adfaf909a8f3d08.png)


第二种情况,如果点击查看详情,此时既要业主的基础信息,也要查询房屋信息,就执行两条SQL


![在这里插入图片描述](https://img-blog.csdnimg.cn/59c4a46fc3e348e3ab0348246d22526d.png)


#### (1)根据id查询业主



/\*\*

* 上面这种延迟加载的方式不建议,应该这样做
* TODO:从业务角度,和速度角度
* 1.一般情况下,只需要根据id查询出业主;
* 2.如果需要用到业主名下的房屋,再根据业主id查方法
*/
Owner queryById(Integer id);



<select id="queryById" resultType="owner">
    SELECT * FROM user_owner WHERE id = #{id}
</select>

#### (2)根据业主id查询房子的list



/\*\*

* 根据业主的id,查询该业主名下有哪些房子
* @param ownerId 业主的id
* @return 名下房子的list
*/
List queryHouseByOwnerId(Integer ownerId);



<select id="queryHouseByOwnerId" parameterType="integer" resultType="house">
    SELECT
    c_owner_house.*,
    c_building.num,c_building.floors,c_building.unit,
    c_house.storey,c_house.roomNum,c_house.area,c_house.into_date,c_house.status,c_house.building_id,
    user_owner.realname AS ownerName

    FROM c_house
    LEFT JOIN c_building ON c_building.id = c_house.building_id
    LEFT JOIN c_owner_house ON c_owner_house.houseId = c_house.id
    LEFT JOIN user_owner ON user_owner.id = c_owner_house.ownerId

    WHERE ownerId = #{ownerId}
</select>

#### (3)在service层【可变长度参数】



package com.tianju.service;

import com.tianju.entity.Owner;

public interface IOwnerService {
/**
* 默认情况下只查询这个用户的基础信息,
* 如果需要房屋信息,就再查一下房屋信息
* @param id 查询的业主的id
* @param isGetHouse 是否获取房屋信息,默认不获取,true表示获取
* @return
*/
Owner queryById(Integer id,Boolean… isGetHouse);

/\*\*

* 新增一条用户信息
*/
Integer add(Owner owner);
}



package com.tianju.service.impl;

import com.tianju.dao.HouseMapper;
import com.tianju.dao.OwnerMapper;
import com.tianju.entity.House;
import com.tianju.entity.Owner;
import com.tianju.service.IOwnerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional
public class OwnerServiceImpl implements IOwnerService {
@Autowired
private OwnerMapper ownerMapper;

@Autowired
private HouseMapper houseMapper;

@Override
public Owner queryById(Integer id,Boolean... isGetHouse) {
    // TODO:尽量减少和数据库的交互
    Owner owner = ownerMapper.queryById(id);
    if (isGetHouse!=null && isGetHouse[0]){
        // 表示既要业主的信息,也要业主名下的房屋的信息
        List<House> houseList = houseMapper.queryHouseByOwnerId(id);
        owner.setHouseList(houseList);
        // 既然查询了一下该业主的房屋数量,那就更新一下拥有房间数量
        ownerMapper.updateOwnerHouseNum(id, houseList.size());
        return owner;
    }
    return owner;
}

@Override
public Integer add(Owner owner) {
    // 如果新用户只是意向客户
    if (owner.getHouseList().size()<1){
        owner.setHouseNum(0);
        return ownerMapper.add(owner);
    }
    // 买房的客户变成了业主
    Integer num = 0; //影响数据库的数据的行数
    ownerMapper.add(owner);
    for (House house:owner.getHouseList()){
        // 房间状态改为入住的状态
        houseMapper.intoHouse(house.getId());
        // 房屋-业主对应表新增一条数据
        houseMapper.addOwnerHouse(owner.getId(), house.getId());
        num +=2;
    }
    return num;
}

}


#### (4)在controller层调用service



// 查询出用户的详细信息,需要查用户名下房子的信息
@RequestMapping("/detail")
@ResponseBody
public ResData detail(Integer id){
    if (StringUtils.isBlank(id)){
        return new ResData(1001, "id为空", null);
    }
    Owner owner = ownerService.queryById(id, true);
    if (owner==null){
        return new ResData(1002,"未找到",null);
    }
    return new ResData(200, "ok", owner);
}

## 三、延迟加载


实际开发经验中,很多时候可能只是需要用户信息,然后通过用户去查询角色时,也就是说,加载用户信息时不一定要同时加载角色信息。此时就是延迟加载。


### 为什么会有延迟加载



select * from account where id=1 //先查询出这个账户信息,包含uid
select * from user where uid=1 //再查询用户信息




![img](https://img-blog.csdnimg.cn/img_convert/daf6d27d1355948daea9e1e8d18c25bd.png)
![img](https://img-blog.csdnimg.cn/img_convert/49deb158faf4fba72fbe360813223e92.png)
![img](https://img-blog.csdnimg.cn/img_convert/29319777818a303044464b058afe12b7.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618631832)**

"ok", owner);
    }

三、延迟加载

实际开发经验中,很多时候可能只是需要用户信息,然后通过用户去查询角色时,也就是说,加载用户信息时不一定要同时加载角色信息。此时就是延迟加载。

为什么会有延迟加载

select \* from account where id=1  //先查询出这个账户信息,包含uid
select \* from user where uid=1     //再查询用户信息

[外链图片转存中…(img-SqGTdwZU-1715809237173)]
[外链图片转存中…(img-LL7iZ3qN-1715809237173)]
[外链图片转存中…(img-Nku7jGyw-1715809237173)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值