MyBatis进阶 动态sql 分页插件 缓存小知识 多表操作

一. 动态sql语句

2.1、静态sql的缺陷

-- 如果传入的Student中age没有赋值,那么会将原纪录中的age更新为NULL
UPDATE student set name=#{name},age=#{age} WHERE id=#{id}
-- 如果传入的Student中name没有赋值,查询条件会变为id=xx AND name=NULL
SELECT * FROM student where id=#{id} AND name=#{name}

https://mybatis.org/mybatis-3/zh/index.html 这里可自主学习

2.2、动态sql常用标签

  • if
-- 原语句
SELECT * FROM student where id=#{id} AND name=#{name}
-- if改造
SELECT * FROM student
<where>
	<if test="id != null">
		AND id=#{id}
	</if>
	<if test="name != null">
		AND name=#{name}
	</if>
</where>

当查询条件id和username都存在时,控制台打印的sql语句如下:

     … … …
     //获得MyBatis框架生成的StudentMapper接口的实现类
    StudentMapper mapper = sqlSession.getMapper( StudentMapper.class);
    Student condition = new Student();
    condition.setId(1);
    condition.setUsername("lucy");
    Student student = mapper.findByCondition(condition);
    … … …

在这里插入图片描述
当查询条件只有id存在时,控制台打印的sql语句如下:

 … … …
 //获得MyBatis框架生成的UserMapper接口的实现类
 StudentMapper mapper = sqlSession.getMapper( StudentMapper.class);
    Student condition = new Student();
    condition.setId(1);
    Student student = mapper.findByCondition(condition);
… … …

在这里插入图片描述

在这里插入图片描述

注意1if判断表达式内的变量为实体对象的成员变量,而非数据库字段
注意2:条件中SQL语句需要以and开头,第一个可以省略
  • foreach
-- 原语句
SELECT * FROM student where id in (1,2,3)
-- foreach改造  (list代表集合,array代表数组)
SELECT * FROM student
<where>
	<foreach collection="list" open="id in (" close=")" item="id" seperator=",">
		#{id}
	</foreach>
</where>

在这里插入图片描述

属性
collection为要被遍历的参数名。
数组默认值为array;集合默认为list。
也可以通过@Param("参数名")在接口形参位置指定参数名,statement中取值的时候就必须使用指定的参数名
open	遍历开始的时候拼接的字符串
close	遍历结束的时候拼接的字符串
item	相当于本次遍历的元素命名为id,所以下面去的时候写成#{id}
separator	拼接时元素间使用的分割符
  • choose
    -在这里插入图片描述

2.3 SQL片段抽取

Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的
注意:改动的时候一定要小心,因为会影响结果的封装。

  • sql
-- 对重复sql进行提取,使用 <include refid=""> 引用
-- 原语句
SELECT * FROM student
<!--抽取sql片段简化编写-->
<sql id="selectStudent" select * from student</sql>
<select id="findById" parameterType="int" resultType="student">
    <include refid="selectStudent"></include> where id=#{id}
</select>
<select id="findByIds" parameterType="list" resultType="student">
    <include refid="selectStudent"></include>
    <where>
        <foreach collection="array" open="id in(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </where>
</select>

我们可以将一些重复性的 SQL 语句进行抽取,以达到复用的效果。

-  <sql>:抽取 SQL 语句标签。 
-  <include>:引入 SQL 片段标签。 
   <sql id=“片段唯一标识”>抽取的 SQL 语句</sql> <include refid=“片段唯一标识”/>
 

2.5 知识小结

MyBatis映射文件配置:

<select>:查询

<insert>:插入

<update>:修改

<delete>:删除

<where>:where条件

<if>if判断

<foreach>:循环

<sql>:sql片段抽取

二.分页插件

2.1 分页插件介绍

在这里插入图片描述

  • 分页可以将很多条结果进行分页显示。
  • 如果当前在第一页,则没有上一页。如果当前在最后一页,则没有下一页。
  • 需要明确当前是第几页,这一页中显示多少条结果。
  • MyBatis分页插件总结
    1. 在企业级开发中,分页也是一种常见的技术。而目前使用的 MyBatis 是不带分页功能的,如果想实现分页的 功能,需要我们手动编写 LIMIT 语句。但是不同的数据库实现分页的 SQL 语句也是不同的,所以手写分页 成本较高。这个时候就可以借助分页插件来帮助我们实现分页功能。
    2. PageHelper:第三方分页助手。将复杂的分页操作进行封装,从而让分页功能变得非常简单。

2.2 分页插件的使用

MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据

开发步骤:

①导入与PageHelper的jar包

②在mybatis核心配置文件中配置PageHelper插件

<!-- 注意:分页助手的插件  配置在通用mapper之前 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
    <!-- 指定方言 -->
    <property name="dialect" value="mysql"/>
</plugin>

③测试分页数据获取

@Test
public void testPageHelper(){
    //设置分页参数
    PageHelper.startPage(1,2);

    List<User> select = userMapper2.select(null);
    for(User user : select){
        System.out.println(user);
    }
}

3.3 分页插件的参数获取

获得分页相关的其他参数

//其他分页的数据
PageInfo<User> pageInfo = new PageInfo<User>(select);
System.out.println("总条数:"+pageInfo.getTotal());
System.out.println("总页数:"+pageInfo.getPages());
System.out.println("当前页:"+pageInfo.getPageNum());
System.out.println("每页显示长度:"+pageInfo.getPageSize());
System.out.println("是否第一页:"+pageInfo.isIsFirstPage());
System.out.println("是否最后一页:"+pageInfo.isIsLastPage());

3.4 分页插件知识小结

​ 分页:可以将很多条结果进行分页显示。

  • 分页插件 jar 包: pagehelper-5.1.10.jar jsqlparser-3.1.jar

  • :集成插件标签。

  • 分页助手相关 API

    ​ 1.PageHelper:分页助手功能类。

    1. startPage():设置分页参数
    2. PageInfo:分页相关参数功能类。
    3. getTotal():获取总条数
    4. getPages():获取总页数
    5. getPageNum():获取当前页
    6. getPageSize():获取每页显示条数
    7. getPrePage():获取上一页
    8. getNextPage():获取下一页
    9. isIsFirstPage():获取是否是第一页
    10. isIsLastPage():获取是否是最后一页

三.缓存小知识

3.1概念

对于不经常发生变化的不是很重要的数据,我们可以从数据库查询出来之后存入内存;
之后使用时直接从内存中获取,而不需要再次查询数据库。
是一种一空间换时间的做法,提高整体响应速度和体验。
	Mybatis缓存分为两类:一级缓存和二级缓存
	一级缓存就是SQLSession级别(连接)的缓存,默认开启的。
	二级缓存就是SQLSessionFactory级别(连接池)的缓存,需要手动开启。

一级缓存

Mybatis一级缓存默认开始,多次相同查询就不会响应数据库查询,而是直接从缓存中获取。
不会走一级缓存的情况
* 2.1 同一个方法,传递不同参数的时候
* 2.2 同一个方法传递相同参数,但是不在同一个连接(会话)的时候
* 2.3 同一个表中记录如有修改,该表所有的缓存都会被清除
* 2.4 在调用SQLSession对象的clearCache()方法后,该连接所有缓存会被清除
工作中用法
Redis - 支持分布式的缓存

二级缓存

二级缓存是SQLSessionFactory级别的缓存
缓存的作用范围越大,越要小心的使用。确保数据真的不会被修改,才能存入二级缓存。
所有需要手动开启。
1.修改配置文件mybatis-config.xml加入<setting name="cacheEnabled"value=“true”/>,全局配置参数,需要时再设置
在这里插入图片描述
cacheEnabled 介绍

描述 : cacheEnabled

允许值: 对在此配置文件下的所有cache 进行全局性开/关设置。

默认值 (true/false): true

2.在mapper.xml中开启二缓存,mapper.xml下的sql执行完成会存储到它的缓存区,如:
在这里插入图片描述
开启缓存后,第一次查询会执行sql,第二次及以后的查询都会从缓存中读取数据
在这里插入图片描述

四.MyBatis的多表操作(重点)

4.1 多表模型介绍

我们之前学习的都是基于单表操作的,而实际开发中,随着业务难度的加深,肯定需要多表操作的。

  • 多表模型分类 一对一:在任意一方建立外键,关联对方的主键。
  • 一对多:在多的一方建立外键,关联一的一方的主键。
  • 多对多:借助中间表,中间表至少两个字段,分别关联两张表的主键。

4.2 多表模型一对一操作

在这里插入图片描述

  1. 一对一模型: 人和身份证,一个人只有一个身份证

  2. 代码实现

    • 步骤一: sql语句准备
CREATE TABLE person(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(20),
	age INT
);
INSERT INTO person VALUES (NULL,'张三',23);
INSERT INTO person VALUES (NULL,'李四',24);
INSERT INTO person VALUES (NULL,'王五',25);

CREATE TABLE card(
	id INT PRIMARY KEY AUTO_INCREMENT,
	number VARCHAR(30),
	pid INT,
	CONSTRAINT cp_fk FOREIGN KEY (pid) REFERENCES person(id)
);
INSERT INTO card VALUES (NULL,'12345',1);
INSERT INTO card VALUES (NULL,'23456',2);
INSERT INTO card VALUES (NULL,'34567',3);

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
步骤二:配置文件

<?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.itheima.table01.OneToOneMapper">
    <!--配置字段和实体对象属性的映射关系-->
    <resultMap id="oneToOne" type="card">
        <id column="cid" property="id" />
        <result column="number" property="number" />
        <!--
            association:配置被包含对象的映射关系
            property:被包含对象的变量名
            javaType:被包含对象的数据类型
        -->
        <association property="p" javaType="person">
            <id column="pid" property="id" />
            <result column="name" property="name" />
            <result column="age" property="age" />
        </association>
    </resultMap>

    <select id="selectAll" resultMap="oneToOne">
        SELECT c.id cid,number,pid,NAME,age FROM card c,person p WHERE c.pid=p.id
    </select>
</mapper>

步骤三:测试类

 @Test
    public void selectAll() throws Exception{
        //1.加载核心配置文件
        InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");

        //2.获取SqlSession工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

        //3.通过工厂对象获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        //4.获取OneToOneMapper接口的实现类对象
        OneToOneMapper mapper = sqlSession.getMapper(OneToOneMapper.class);

        //5.调用实现类的方法,接收结果
        List<Card> list = mapper.selectAll();

        //6.处理结果
        for (Card c : list) {
            System.out.println(c);
        }

        //7.释放资源
        sqlSession.close();
        is.close();
    }

在这里插入图片描述

4.3 多表模型一对多操作

  1. 一对多模型: 一对多模型:班级和学生,一个班级可以有多个学生。

  2. 代码实现

    • 步骤一: sql语句准备

      CREATE TABLE classes(
      	id INT PRIMARY KEY AUTO_INCREMENT,
      	NAME VARCHAR(20)
      );
      INSERT INTO classes VALUES (NULL,'黑马一班');
      INSERT INTO classes VALUES (NULL,'黑马二班');
      
      
      CREATE TABLE student(
      	id INT PRIMARY KEY AUTO_INCREMENT,
      	NAME VARCHAR(30),
      	age INT,
      	cid INT,
      	CONSTRAINT cs_fk FOREIGN KEY (cid) REFERENCES classes(id)
      );
      INSERT INTO student VALUES (NULL,'张三',23,1);
      INSERT INTO student VALUES (NULL,'李四',24,1);
      INSERT INTO student VALUES (NULL,'王五',25,2);
      INSERT INTO student VALUES (NULL,'赵六',26,2);
      

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 步骤二:配置文件

    <mapper namespace="com.itheima.table02.OneToManyMapper">
        <resultMap id="oneToMany" type="classes">
            <id column="cid" property="id"/>
            <result column="cname" property="name"/>
    
            <!--
                collection:配置被包含的集合对象映射关系
                property:被包含对象的变量名
                ofType:被包含对象的实际数据类型
            -->
            <collection property="students" ofType="student">
                <id column="sid" property="id"/>
                <result column="sname" property="name"/>
                <result column="sage" property="age"/>
            </collection>
        </resultMap>
        <select id="selectAll" resultMap="oneToMany">
            SELECT c.id cid,c.name cname,s.id sid,s.name sname,s.age sage FROM classes c,student s WHERE c.id=s.cid
        </select>
    </mapper>
    
  • 步骤三:测试类

        @Test
        public void selectAll() throws Exception{
            //1.加载核心配置文件
            InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
    
            //2.获取SqlSession工厂对象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    
            //3.通过工厂对象获取SqlSession对象
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
    
            //4.获取OneToManyMapper接口的实现类对象
            OneToManyMapper mapper = sqlSession.getMapper(OneToManyMapper.class);
    
            //5.调用实现类的方法,接收结果
            List<Classes> classes = mapper.selectAll();
    
            //6.处理结果
            for (Classes cls : classes) {
                System.out.println(cls.getId() + "," + cls.getName());
                List<Student> students = cls.getStudents();
                for (Student student : students) {
                    System.out.println("\t" + student);
                }
            }
    
            //7.释放资源
            sqlSession.close();
            is.close();
        }
    

3.一对多配置文件总结:

<resultMap>:配置字段和对象属性的映射关系标签。
    id 属性:唯一标识
    type 属性:实体对象类型
<id>:配置主键映射关系标签。
<result>:配置非主键映射关系标签。
    column 属性:表中字段名称
    property 属性: 实体对象变量名称
<collection>:配置被包含集合对象的映射关系标签。
    property 属性:被包含集合对象的变量名
    ofType 属性:集合中保存的对象数据类型

在这里插入图片描述

4.4 多表模型多对多操作

  1. 多对多模型:学生和课程,一个学生可以选择多门课程、一个课程也可以被多个学生所选择。

  2. 代码实现

    • 步骤一: sql语句准备

      CREATE TABLE course(
      	id INT PRIMARY KEY AUTO_INCREMENT,
      	NAME VARCHAR(20)
      );
      INSERT INTO course VALUES (NULL,'语文');
      INSERT INTO course VALUES (NULL,'数学');
      
      
      CREATE TABLE stu_cr(
      	id INT PRIMARY KEY AUTO_INCREMENT,
      	sid INT,
      	cid INT,
      	CONSTRAINT sc_fk1 FOREIGN KEY (sid) REFERENCES student(id),
      	CONSTRAINT sc_fk2 FOREIGN KEY (cid) REFERENCES course(id)
      );
      INSERT INTO stu_cr VALUES (NULL,1,1);
      INSERT INTO stu_cr VALUES (NULL,1,2);
      INSERT INTO stu_cr VALUES (NULL,2,1);
      INSERT INTO stu_cr VALUES (NULL,2,2);
      

在这里插入图片描述
在这里插入图片描述

  • 步骤二:配置文件

    <?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.itheima.table03.ManyToManyMapper">
        <resultMap id="manyToMany" type="student">
            <id column="sid" property="id"/>
            <result column="sname" property="name"/>
            <result column="sage" property="age"/>
    
            <collection property="courses" ofType="course">
                <id column="cid" property="id"/>
                <result column="cname" property="name"/>
            </collection>
        </resultMap>
        <select id="selectAll" resultMap="manyToMany">
            SELECT sc.sid,s.name sname,s.age sage,sc.cid,c.name cname FROM student s,course c,stu_cr sc WHERE sc.sid=s.id AND sc.cid=c.id
        </select>
    </mapper>
    
  • 步骤三:测试类

     @Test
        public void selectAll() throws Exception{
            //1.加载核心配置文件
            InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
    
            //2.获取SqlSession工厂对象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
    
            //3.通过工厂对象获取SqlSession对象
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
    
            //4.获取ManyToManyMapper接口的实现类对象
            ManyToManyMapper mapper = sqlSession.getMapper(ManyToManyMapper.class);
    
            //5.调用实现类的方法,接收结果
            List<Student> students = mapper.selectAll();
    
            //6.处理结果
            for (Student student : students) {
                System.out.println(student.getId() + "," + student.getName() + "," + student.getAge());
                List<Course> courses = student.getCourses();
                for (Course cours : courses) {
                    System.out.println("\t" + cours);
                }
            }
    
            //7.释放资源
            sqlSession.close();
            is.close();
        }
        
    

3.多对多配置文件总结:

<resultMap>:配置字段和对象属性的映射关系标签。
	id 属性:唯一标识
	type 属性:实体对象类型
 <id>:配置主键映射关系标签。
 <result>:配置非主键映射关系标签。
	column 属性:表中字段名称
	property 属性: 实体对象变量名称
<collection>:配置被包含集合对象的映射关系标签。
	property 属性:被包含集合对象的变量名
	ofType 属性:集合中保存的对象数据类型

4.5多表操作总结

数据库中表关系及建表原则

    一对一
		1. 主键对应(两个表的主键保持一致)
		2. 外键唯一(在任意一张表中添加一个外键,指向另一个表的主键,保证外键唯一)
	一对多/多对一
		在多的一方(学生)维护一个一的一方(班级)的主键作为外键
	多对多
		创建一个中间表,中间表中至少有两个外键字段,分别指向另外两张表的主键

Java实体中如何维护关系
	通过属性引用确定两个实体之间的关系
	人和身份证
		在身份证对象中保存一个人类型的对象引用,以维护关系
		在身份证列中定义一个人类型的成员变量
		反之亦可
	班级和学生
		在一的一方(班级)中维护一个多(学员)的一方的集合引用
	学生和课程
		从学生角度来说:一个学生可以选多个课程,在学生类中定义一个课程的集合
		从课程角度来说:一个课程可以被多个同学选,在课程类中定义一个学生的集合
		将多对多拆分成多个一对多
		思路:简化关系模型,方便处理
		从任意一个角度拆分即可。拆出来多个一对多。

Mybatis中如何处理上述关系
	处理结果集字段名和实体类中属性的映射关系
	方式1:使用resultMap被引用对象的属性可以通过,对象名.属性来设置值
	方式2:使用resultMap + association在resultMap中只负责映射外层对象的成员
association负责内层(被引用)对象成员的赋值
	方式3:使用resultMap + Collection解决一对多关系的方式 结果集字段和属性名一致,会自动映射匹配,不需要手动匹配


一对一
	从数据库查询出数据后封装到POJO实体对象中的方式:
		方式一:写一个新POJO,包含结果集中所有字段
		方式二:使用resultMap,配合user.usernname、user.id完成封装
		方式三:使用resultMap,配合<association property javatype>
一对多
	使用resultMap,配合<colleciont property oftype>
多对多
	相当于多个一对多
多表查询SQL查询方式
	建议使用左外连接查询,避免部分数据查询不到

查询出来的结果封装方式
student表中的字段名name与Student类中的username属性不对应
mybatis自动封装的原理是结果集字段和类中属性名字一样即可完成封装
解决方案
1、修改结果集中name为username,让mybatis完成自动封装。
2、使用resultMap,手动指定字段name和属性username映射关系
注意
POJO实体中的其他POJO的属性也可以使用resultMap的方式解决
resultMap并不是为了多表查询而产生的,但是可以解决多表查询中的数据封装问题

五.总结

动态sql
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
多表操作
数据库中三种关系
一对一:在任意一张表中添加一个外键,指向另一个表的主键
一对多/多对一:在多的一方维护一个一的一方的主键作为外键
多对多:创建中间表,至少包含两个外键字段,分别指向两张表的主键

Java实体中
通过属性引用确定两个实体之间的关系
一对一:任意一个对象保存对方类型的引用
一对多/多对多(本质多个一对多):在一的一方维护多的一方的集合引用

Mybatis中如何处理
处理结果集字段名和实体类中属性的映射关系
Mybatis中如何处理上述关系
处理结果集字段名和实体类中属性的映射关系
方式1:使用resultMap
被引用对象的属性可以通过,对象名.属性来设置值
方式2:使用resultMap + association
在resultMap中只负责映射外层对象的成员
association负责内层(被引用)对象成员的赋值
方式3:使用resultMap + Collection
解决一对多关系的方式
结果集字段和属性名一致,会自动映射匹配,不需要手动匹配
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
建议使用左外连接查询,避免部分数据查询不到

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值