Mybatis学习笔记2:分页、一对多、多对一、动态SQL和缓存

文章目录

    • 7、分页
      • 7.1、使用Limit分页
      • 7.2、RowBounds分页
      • 7.3、分页插件
        • 7.3.1、mybatis pagehelper
    • 8、使用注解开发
      • 8.1、面向接口编程
      • 8.2、使用注解开发
      • 8.3、CRUD
    • 9、Lombok
    • 10、多对一的处理
      • 10.1、测试环境搭建
      • 10.2、按照子查询嵌套处理
      • 10.3、按照结果嵌套处理
    • 11、一对多的处理
      • 11.1、按结果嵌套处理
      • 11.2、按子查询嵌套处理
      • 11.3、小结
    • 12、动态SQL
      • 12.1、环境搭建
      • 12.2、IF
      • 12.3、choose、when、otherwise
      • 12.4、trim、where、set
        • 12.4.1、where
        • 12.4.2、set
      • 12.5、SQL片段
      • 12.6、Foreach
      • 12.7、总结
    • 13、缓存
      • 13.1、简介
      • 13.2、Mybatis缓存
      • 13.3、一级缓存
        • 13.3.1、测试步骤:
        • 13.3.2、小结
      • 13.4、二级缓存
        • 13.4.1、步骤
        • 13.4.2、小结
      • 13.5、缓存原理
      • 13.6、自定义缓存--Ehcache

7、分页

思考:为什么要分页?

  • 减少数据处理量

7.1、使用Limit分页

语法:SELECT * from user limit startIndex,pageSize
SELECT * from user limit 0,2
1,何锦涛,123456
2,严瑶,123456

使用Mybatis实现分页,核心SQL

package com.atctbu.hjt.dao;

import com.atctbu.hjt.pojo.User;

import java.util.List;
import java.util.Map;

public interface UserMapper {
     
    //根据ID查询用户
    User getUserById(int id);
    //分页
    List<User> getUserByLimit(Map<String, Integer> map);
}


@Test
public void getUserByLimit(){
     
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("startIndex",0);
    map.put("pageSize",2);
    List<User> userList = mapper.getUserByLimit(map);
    for (User user:userList
        ) {
     
        System.out.println(user);
    }
}
<select id="getUserByLimit" parameterType="map" resultType="user" resultMap="UserMap">
    select * from mybatis.user limit #{startIndex},#{pageSize}
select>

7.2、RowBounds分页

不建议使用

接口

package com.atctbu.hjt.dao;
import com.atctbu.hjt.pojo.User;
import java.util.List;
import java.util.Map;
public interface UserMapper {
    //RowBounds分页
    List getUserByRowBounds(Map map);
}

SQL


<resultMap id="UserMap" type="User">
    
    
    
    <result column="pwd" property="password"/>
resultMap>   
<select id="getUserByRowBounds" parameterType="map" resultType="user" resultMap="UserMap">
    select * from mybatis.user
select>

测试类

@Test
public void getUserByRowBounds(){
     
    //RoolBounds实现
    RowBounds rowBounds = new RowBounds(1, 2);
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    List<User> userList = sqlSession.selectList("com.atctbu.hjt.dao.UserMapper.getUserByRowBounds",null,rowBounds);
    for (User user: userList
        ) {
     
        System.out.println(user);
    }
    sqlSession.close();
}

7.3、分页插件

7.3.1、mybatis pagehelper

8、使用注解开发

8.1、面向接口编程

​ 大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程
根本原因: 解耦 ,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得
开发变得容易,规范性更好

​ 在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;
​ 而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。

关于接口的理解

  • 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
  • 接口的本身反映了系统设计人员对系统的抽象理解。
  • 接口应有两类:
  • 第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);
  • 第二类是对一个个体某-方面的抽象,即形成一个抽象面(interface) ;
  • 一个体有可能有多个抽象面。抽象体与抽象面是有区别的。

三个面向区别

  • 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法.
  • 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现.
  • 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题.更多的体现就是对系统整体的架构

8.2、使用注解开发

这些东西不需要了:

  • UserMapper.xml

  • <mappers>
        <package name="com.atctbu.hjt.dao"/>
    mappers>
    

接口类:

package com.atctbu.hjt.dao;

import com.atctbu.hjt.pojo.User;
import org.apache.ibatis.annotations.Select;
import java.util.List;

public interface UserMapper {
     
    @Select("select * from user")
    List<User> getUsers();
}

绑定接口


<mappers>
    <mapper class="com.atctbu.hjt.dao.UserMapper"/>
mappers>

测试类

public class UserMapperTest {
     
    @Test
    public void Test(){
     
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //底层主要应用反射
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.getUsers();
        for (User user:users
             ) {
     
            System.out.println(user);
        }
        sqlSession.close();
    }
}
User{
     id=1, name='何锦涛', password='null'}
User{
     id=2, name='严瑶', password='null'}
User{
     id=3, name='李四', password='null'}

password为null

​ 使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。

本质:反射机制实现

底层:动态代理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ABb60DfP-1616654175262)(https://m.qpic.cn/psc?/V11Mzdsz1sKJyi/ruAMsa53pVQWN7FLK88i5prTFvIfciN5g5dWsG.MM2bql8rwSmkNAkY9sChUYJ1pFZyasfUXRsL69Vp0Zh8rZya8SCfxXodiBM.h*3.PxwM!/b&bo=RQIzAQAAAAADB1c!&rf=viewer_4)]

Mybatis详细的执行流程! P16,看不大懂,有机会回去补!

8.3、CRUD

我们可以在工具类创建的时候自动提交事务

public static SqlSession getSqlSession(){
     
    //        SqlSession sqlSession = sqlSessionFactory.openSession();
    //        return sqlSession;
    return sqlSessionFactory.openSession(true);	//这里设置为true(autocommit)
}

增删改查

package com.atctbu.hjt.dao;

import com.atctbu.hjt.pojo.User;
import org.apache.ibatis.annotations.*;

import java.util.List;
import java.util.Map;

public interface UserMapper {
     
    @Select("select * from user")
    List<User> getUsers();
    //方法存在多个参数,所有的参数前必须加上@Param("id")注解,默认按照@Param里的查找,如@Delete方法里面那个一样
    @Select("select * from user where id = #{id}")
    User getUserById(@Param("id") int id);

    @Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})")
    int addUser(User user);

    @Update("update user set name=#{name},pwd=#{password} where id=#{id}")
    int updateUser(User user);

    @Delete("delete from user where id = #{uid}")
    int deleteUser(@Param("uid") int id);
}

测试类

【注意,我们必须将接口注册绑定到我们的核心配置文件中】


<mappers>
    <mapper class="com.atctbu.hjt.dao.UserMapper"/>
mappers>
import com.atctbu.hjt.dao.UserMapper;
import com.atctbu.hjt.pojo.User;
import com.atctbu.hjt.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserMapperTest {
     
    @Test
    public void Test(){
     
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        /*
        List users = mapper.getUsers();
        for (User user:users
             ) {
            System.out.println(user);
        }
        User userById = mapper.getUserById(1);
        System.out.println(userById);
        mapper.addUser(new User(4,"芜湖","123456"));  //设置了自动提交事务,所以不用手动提交
        mapper.updateUser(new User(4,"起飞","123456"));
        */
        mapper.deleteUser(4);
        sqlSession.close();
    }
}

关于@Param()注解

  • 基本类型的参数或者String类型,需要加上
  • 引用类型不需要加
  • 如果只有一个基本类型的话,可以忽略,建议大家都加上!
  • 我们在SQL中引用的就是我们这里的@Param()设定的属性名
  • #{}和 区 别 ; {}区别; {}无法防止SQL注入,会将语句拼接(面试可能会问)

9、Lombok

Lombok项目是一个Java库,它会自动插入您的编辑器和构建工具中,从而使您的Java更加有趣。
永远不要再写另一个getter或equals方法,带有一个注释的您的类有一个功能全面的生成器,自动化您的日志记录变量等等。
  • java库
  • plugs
  • build tools
  • 不太建议使用,用了的话别人也需要下载这个插件才能运行项目
  • 用法:
    • settings ----> Plugins---->lombok(找不到去下面的Browse找)
    • 在项目中导入lombok的jar包(下载完自动导入好像,maven仓库找也可以)

<dependency>
    <groupId>org.projectlombokgroupId>
    <artifactId>lombokartifactId>
    <version>1.18.12version>
dependency>
  • 里面的注解
@Getter and @Setter
@FieldNameConstants		//字段属性常量
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows

@Data:无参构造、get、set、toString、hashcode、equal

import lombok.Data;

@Data
public class User {
     
    private int id;
    private String name;
    private String password;
}

@AllArgsConstructor:有参构造

@NoArgsConstructor无参构造

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
     
    private int id;
    private String name;
    private String password;
}

10、多对一的处理

多对一:

  • 多个学生:对应一个老师
  • 对于学生这边而言,关联:多个学生,关联一个老师【多对一】
  • 对于老师而言,集合:一个老师有很多学生【一对多】

association – 一个复杂类型的关联;许多结果将包装成这种类型

​ 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用

collection – 一个复杂类型的集合

​ 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用

SQL:

CREATE TABLE `teacher` (
 `id` INT(10) NOT NULL,
 `name` VARCHAR(30) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师'); 

CREATE TABLE `student` (
 `id` INT(10) NOT NULL,
 `name` VARCHAR(30) DEFAULT NULL,
 `tid` INT(10) DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `fktid` (`tid`),
 CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');

10.1、测试环境搭建

  1. 导入lombok
  2. 新建实体类teacher,student
  3. 新建Mapper接口
  4. 在resources下面的同包名(File文件用"/"建立多级目录com/atctbu/hjt/dao)建立Mapper.xml文件
  5. 在核心配置文件中绑定注册我们的Mapper接口或文件【方法很多】

<mappers>
    
    
    <mapper resource="com/atctbu/hjt/dao/StudentMapper.xml"/>
    <mapper resource="com/atctbu/hjt/dao/TeacherMapper.xml"/>
mappers>

​ 6.测试查询是否成功

学生Mapper

package com.atctbu.hjt.dao;

import com.atctbu.hjt.pojo.Student;

import java.util.List;

public interface StudentMapper {
     
    //按照查询嵌套处理:查询所有的学生信息以及所有的老师信息!
    public List<Student> getStudent();
    //按照结果嵌套处理
    public List<Student> getStudent2();
}

Student类

public class Student {
     
    private int id;
    private String name;
    //学生需要关联一个老师
    private Teacher teacher;

10.2、按照子查询嵌套处理

<mapper namespace="com.atctbu.hjt.dao.StudentMapper">
    
    <select id="getStudent" resultMap="StudentAndTeacher">
        select * from student
    select>
    
    <resultMap id="StudentAndTeacher" type="Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    resultMap>

    <select id="getTeacher" resultType="Teacher">
        select * from teacher where id = #{tid}		
    select>
mapper>
Student{id=1, name='小何', teacher=Teacher{id=1, name='何老师'}}
Student{id=2, name='小瑶', teacher=Teacher{id=1, name='何老师'}}
Student{id=3, name='小张', teacher=Teacher{id=1, name='何老师'}}
Student{id=4, name='小王', teacher=Teacher{id=1, name='何老师'}}
Student{id=5, name='小米', teacher=Teacher{id=1, name='何老师'}}

10.3、按照结果嵌套处理




<mapper namespace="com.atctbu.hjt.dao.StudentMapper">
    
    <select id="getStudent2" resultMap="StudentAndTeacher2">
        select s.id sid,s.name sname,t.name tname
        from student s,teacher t
        where s.tid=t.id
    select>
    
    <resultMap id="StudentAndTeacher2" type="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="Teacher">
            <result property="name" column="tname"/>
        association>
    resultMap>
mapper>
Student{id=1, name='小何', teacher=Teacher{id=0, name='何老师'}}
Student{id=2, name='小瑶', teacher=Teacher{id=0, name='何老师'}}
Student{id=3, name='小张', teacher=Teacher{id=0, name='何老师'}}
Student{id=4, name='小王', teacher=Teacher{id=0, name='何老师'}}
Student{id=5, name='小米', teacher=Teacher{id=0, name='何老师'}}
因为没有查id,id没设置就是默认值0

回顾Mysql多对一查询方式:

  • 子查询
  • 联表查询

11、一对多的处理

比如:一个老师拥有多个学生

对于老师而言,就是一对多的关系!

  1. 环境搭建

实体类Student

public class Student {
     
    private int id;
    private String name;
    private int tid;

实体类Teacher

public class Teacher {
     
    private int id;
    private String name;
    //老师拥有多个学生
    private List<Student> students;

接口TeacherMapper

package com.atctbu.hjt.dao;

import com.atctbu.hjt.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface TeacherMapper {
     
    //获取老师
    //List getTeacher();
    //获取老师的信息和老师下的所有学生信息
    Teacher getTeacher(@Param("tid")int id);
    //按子查询
    Teacher getTeacher2(@Param("tid")int id);
}

11.1、按结果嵌套处理




<mapper namespace="com.atctbu.hjt.dao.TeacherMapper">
    <select id="getTeacher" resultMap="TeacherAndStudent">
        select s.id sid, s.name sname, t.name tname, t.id tid
        from student s,teacher t
        where s.tid = t.id and t.id = #{tid}
    select>
    <resultMap id="TeacherAndStudent" type="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        
        <collection property="Students" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        collection>
    resultMap>
mapper>

测试类

@Test
public void getTeacher(){
     
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
    Teacher teacher = mapper.getTeacher(1);
    System.out.println(teacher);
    /*
        查询结果:
        Teacher{id=1, name='何老师', students=[Student{id=1, name='小何', tid=1}, Student{id=2, name='小瑶', tid=1}, Student{id=3, name='小张', tid=1}, Student{id=4, name='小王', tid=1}, Student{id=5, name='小米', tid=1}]}
        */
    sqlSession.close();
}

11.2、按子查询嵌套处理




<mapper namespace="com.atctbu.hjt.dao.TeacherMapper">
    <select id="getTeacher2" resultMap="TeacherAndStudent2">
        select * from mybatis.teacher where id =#{tid}
    select>
    <resultMap id="TeacherAndStudent2" type="Teacher">
        
        <collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId"/>
    resultMap>
    <select id="getStudentByTeacherId" resultType="Student">
        select * from mybatis.student where tid = #{id} 
    select>
mapper>

测试类

@Test
public void getTeacher2(){
     
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
    Teacher teacher = mapper.getTeacher2(1);
    System.out.println(teacher);
    /*
        查询结果:没查teacherID所以为0
        Teacher{id=0, name='何老师', students=[Student{id=1, name='小何', tid=1}, Student{id=2, name='小瑶', tid=1}, Student{id=3, name='小张', tid=1}, Student{id=4, name='小王', tid=1}, Student{id=5, name='小米', tid=1}]}
        */
    sqlSession.close();
}

11.3、小结

  1. 关联–association:【多对一】
  2. 集合–collection:【一对多】
  3. javaType & ofType
    1. javaType:用来指定实体类中属性的类型
    2. ofType:用来指定映射到List或者集合中的POJO类型,泛型中的约束类型!

注意点:

  • 保证SQL的可读性,尽量通俗易懂
  • 注意一对多和多对一中,属性名和字段的问题
  • 如果问题不好排查错误,可以使用日志,Log4j

面试高频

  • Mysql引擎
  • InnoDB底层原理
  • 索引
  • 索引优化!

12、动态SQL

什么是动态 SQL:动态SQL是指根据不同的条件生成不同的SQL语句

12.1、环境搭建

CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '博客id',
`title` VARCHAR(100) NOT NULL COMMENT '博客标题',
`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
`create_time` DATETIME NOT NULL COMMENT '创建时间',
`views` INT(30) NOT NULL COMMENT '浏览量'
)ENGINE=INNODB DEFAULT CHARSET=utf8

创建一个基础工程

  1. 导包

  2. 编写配置文件

    mybatis-config.xml

    
    <settings>
        
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    settings>
    

    IDUtils

    package com.atctbu.hjt.utils;
    
    import org.junit.Test;
    import java.util.UUID;
    
    public class IDUtils {
           
        public static String getId(){
           
            return UUID.randomUUID().toString().replaceAll("-","");
        }
        @Test
        public void test(){
           
            System.out.println(IDUtils.getId());
        }
    }
    //4abb3e9f14d54e6ba1b0c30035f408ad
    
  3. 编写实体类

    public class Blog {
           
        private String id;
        private String title;
        private String author;
        private Date createtime;    //属性名和字段名不一致
        private int views;
    
  4. 编写实体类对应Mapper接口和Mapper.xml文件

  5. 插入数据

    @Test
    public void Test01(){
           
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        Blog blog = new Blog();
        blog.setId(IDUtils.getId());
        blog.setTitle("Mybatis从入门到放弃");
        blog.setAuthor("何锦涛");
        blog.setCreatetime(new Date());
        blog.setViews(9999);
    
        mapper.addBlog(blog);
    
        blog.setId(IDUtils.getId());
        blog.setTitle("Java从入门到入土");
        mapper.addBlog(blog);
    
        blog.setId(IDUtils.getId());
        blog.setTitle("Spring从入门到放弃");
        mapper.addBlog(blog);
    
        blog.setId(IDUtils.getId());
        blog.setTitle("微服务从入门到入土");
        mapper.addBlog(blog);
    
        sqlSession.close();
    }
    

12.2、IF

例子:

<select id="findActiveBlogWithTitleLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  if>
select>

这条语句提供了可选的查找文本功能。如果不传入 “title”,那么所有处于 “ACTIVE” 状态的 BLOG 都会返回;如果传入了 “title” 参数,那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果(细心的读者可能会发现,“title” 的参数值需要包含查找掩码或通配符字符)。

如果希望通过 “title” 和 “author” 两个参数进行可选搜索该怎么办呢?首先,我想先将语句名称修改成更名副其实的名称;接下来,只需要加入另一个条件即可。

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  if>
select>

接口类:

package com.atctbu.hjt.dao;

import com.atctbu.hjt.pojo.Blog;

import java.util.List;
import java.util.Map;

public interface BlogMapper {
     
    //查询博客
    List<Blog> queryBlogIF(Map map);
}

BlogMapper.xml

<mapper namespace="com.atctbu.hjt.dao.BlogMapper">
    <insert id="addBlog" parameterType="blog">
        insert into mybatis.blog (id,title,author,create_time,views)
        value (#{id},#{title},#{author},#{createtime},#{views})
    insert>
    
    <select id="queryBlogIF" resultType="blog" parameterType="map" >
        select * from mybatis.blog
        where 1=1	
        <if test="title != null">
            and title = #{title}
        if>
        <if test="author != null">
            and author = #{author}
        if>
    select>
mapper>

where标签解决where 1=1


<select id="queryBlogIF" resultType="blog" parameterType="map" >
    select * from mybatis.blog
    <where>
        <if test="title != null">
            title = #{title}
        if>
        <if test="author != null">
            and author = #{author}
        if>
    where>
select>

测试代码

@Test
public void queryBlogIF(){
     
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    map.put("title","Java从入门到入土");
    map.put("title","Mybatis从入门到放弃");
    List<Blog> blogs = mapper.queryBlogIF(map);
    for (Blog blog:blogs
        ) {
     
        System.out.println(blog);
    }
    /*
    select * from mybatis.blog WHERE title = ? 
        * 不加参数,全查出来了
        * map.put("title","Java从入门到入土"):Blog{id=d0d1d39093ca458c84ed00bb7bc350f1, title='Java从入门到入土', author='何锦涛', createtime=Tue Nov 24 15:48:39 CST 2020, views=1000}
        * title不为空了,就执行后面的and了
        * put两个title好像只能查到最后面那个
        *  */
    sqlSession.close();
}

12.3、choose、when、otherwise

​ 有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

​ 还是上面的例子,但是策略变为:传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回标记为 featured 的 BLOG(这可能是管理员认为,与其返回大量的无意义随机 Blog,还不如返回一些由管理员精选的 Blog)。

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    when>
    <otherwise>
      AND featured = 1
    otherwise>
  choose>
select>

接口类BlogMapper:

package com.atctbu.hjt.dao;

import com.atctbu.hjt.pojo.Blog;

import java.util.List;
import java.util.Map;

public interface BlogMapper {
     
    //Choose查询博客
    List<Blog> queryBlogChoose(Map map);
}

BlogMapper.xml

<select id="queryBlogChoose" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <choose>
            <when test="title != null">
                title = #{title}
            when>
            <when test="author != null">
                and author = #{author}		
            when>
            <otherwise>
                and views = #{views}
            otherwise>
        choose>
    where>
select>

测试类:

@Test
public void queryBlogChoose(){
     
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    map.put("title","Java从入门到入土");
    map.put("author","何锦涛");
    map.put("views",9999);
    List<Blog> blogs = mapper.queryBlogChoose(map);
    for (Blog blog:blogs
        ) {
     
        System.out.println(blog);
    }
    /*
    select * from mybatis.blog WHERE title = ? 
	* 只会选择其中满足的一个去实现,类似switch
	**/
    sqlSession.close();
}

12.4、trim、where、set

12.4.1、where

​ 如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:

SELECT * FROM BLOG
WHERE

​ 这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:

SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’

​ 这个查询也会失败。这个问题不能简单地用条件元素来解决。这个问题是如此的难以解决,以至于解决过的人不会再想碰到这种问题。

​ MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动:

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
         state = #{state}
    if>
    <if test="title != null">
        AND title like #{title}
    if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    if>
  where>
select>

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

12.4.2、set

​ 用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:

<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},if>
      <if test="password != null">password=#{password},if>
      <if test="email != null">email=#{email},if>
      <if test="bio != null">bio=#{bio}if>
    set>
  where id=#{id}
update>

这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

来看看与 set 元素等价的自定义 trim 元素吧:

<trim prefix="SET" suffixOverrides=",">
  ...
trim>

例子:

接口

package com.atctbu.hjt.dao;

import com.atctbu.hjt.pojo.Blog;

import java.util.List;
import java.util.Map;

public interface BlogMapper {
     
    //更新博客
    int updateBlog(Map map);
}

BlogMapper.xml

<update id="updateBlog" parameterType="map">
    update mybatis.blog
    <set>
        <if test="title != null">
            title = #{title},		
        if>
        <if test="author != null">
            author = #{author}
        if>
    set>
    where id = #{id}
update>

测试类

@Test
public void updateBlog(){
     
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    //        map.put("title","Java从入门到入土2");
    map.put("author","何锦涛2");
    map.put("id", "2");
    mapper.updateBlog(map);
    /*
         update mybatis.blog SET author = ? where id = ? 
 	*/
    sqlSession.close();
}

所谓的动态SQL,本质还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码

  • IF
  • where、set、choose、when

12.5、SQL片段

有的时候,我们可能会将一些公共的部分抽取出来,方便复用

  1. 使用SQL标签抽取公共的部分
  2. 在需要的地方使用include标签引用即可
<sql id="if-title-author">
    <if test="title != null">
        title = #{title}
    if>
    <if test="author != null">
        and author = #{author}
    if>
sql>


<select id="queryBlogIF" resultType="blog" parameterType="map" >
    select * from mybatis.blog
    <where>
        <include refid="if-title-author">include>
    where>
select>

注意事项:

  • 最好基于单表来定义SQL片段!
  • 不要存在where标签

12.6、Foreach

select * from user where 1=1 and

  <foreach item="id" collection="ids"
      open="(" separator="or" close=")">
        #{id}
  </foreach>
  
(id=1 or id=2 or id=3)

​ 动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  foreach>
select>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tvxMtMDR-1616654175267)(http://m.qpic.cn/psc?/V11Mzdsz1sKJyi/45NBuzDIW489QBoVep5mcRf7qlGoXUECFojtHnIozPMZg.RgO7oIKdOIVm73ckiiX10uKMDU3Yb3zxKnsk56nibm5gR7irQ1O1JpAJUJ8!/b&bo=pgOIAQAAAAADJy4!&rf=viewer_4)]

例子:

接口类:

package com.atctbu.hjt.dao;

import com.atctbu.hjt.pojo.Blog;
import java.util.List;
import java.util.Map;

public interface BlogMapper {
     
    //查询1-2-3号记录的博客
    List<Blog> queryBlogForeach(Map map);
}

BlogMapper.xml


<select id="queryBlogForeach" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <foreach collection="ids" item="id" open="and (" close=")" separator="or">
            id = #{id}
        foreach>
    where>
select>

测试类:

@Test
public void queryBlogForeach(){
     
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    ArrayList<Integer> ids = new ArrayList<Integer>();
    ids.add(1);     //select * from mybatis.blog WHERE ( id = ? )
    ids.add(2);     //select * from mybatis.blog WHERE ( id = ? or id = ? )
    ids.add(3);     //select * from mybatis.blog WHERE ( id = ? or id = ? or id = ? )
    map.put("ids",ids);     //不加参数时全查出来 select * from mybatis.blog
    List<Blog> blogs = mapper.queryBlogForeach(map);
    for (Blog blog:blogs
        ) {
     
        System.out.println(blog);
    }
    sqlSession.close();
}

12.7、总结

​ 动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就好了

建议:

  • 先在Mysql中写出完整的SQL,再去对应的去修改成为我们的动态SQL实现即可

13、缓存

13.1、简介

查询		连接数据库		耗资源
一次查询的结果给它暂存在一个可以直接取到的地方!---》内存	:	缓存
我们再次查询相同数据的时候,直接走缓存,就不用走数据库了
  1. 什么是缓存[ Cache ]?

    • 存在内存中的临时数据。
    • 将用户经常查询的数据放在缓存(内存) 中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
  2. 为什么使用缓存?

    • 减少和数据库的交互次数,减少系统开销,提高系统效率。
  3. 什么样的数据能使用缓存?

    • 经常查询并且不经常改变的数据。【可以使用缓存】

13.2、Mybatis缓存

  • MyBatis包含一个非常强大的查询缓存特性, 它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
  • MyBatis系统中默认定义了两级缓存:一级缓存二级缓存
    • 默认情况下,只有一级缓存开启。 (SqISession级别的缓存, 也称为本地缓存)
    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
    • 为了提高打展性, MyBatis定义了缓存接口Cache. 我们可以通过实现Cache接口来自定义二级缓存

13.3、一级缓存

  • 一级缓存也叫本地缓存
    • 与数据库同一次会话期间查询到的数据会放在本地缓存中。
    • 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库。

13.3.1、测试步骤:

1、开启日志

<settings>
    
    <setting name="logImpl" value="STDOUT_LOGGING"/>
settings>

2、测试在一个Session中查询两次记录

public void queryUserById(){
     
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.queryUserById(1);
    System.out.println(user);
    User user2 = mapper.queryUserById(1);      //只走一次查询select * from mybatis.user where id=? 
    System.out.println(user2);
    System.out.println(user==user2);        //true
    sqlSession.close();
}
//查询两个ID相同:只走一次查询select * from mybatis.user where id=? 
//查询两个ID不同:有两个SQL语句,并且为false

3、测试在一个Session中查询一个记录再更新一个记录再查询一个记录

public void queryUserById(){
     
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.queryUserById(1);
    System.out.println(user);
    mapper.updateUser(new User(3,"王大拿","12121212"));
    User user2 = mapper.queryUserById(3);      //select * from mybatis.user where id=?
    System.out.println(user2);
    System.out.println(user==user2);        //false
    sqlSession.close();
}
//select * from mybatis.user where id=? 
//update mybatis.user set name=?,pwd=? where id=? 
//select * from mybatis.user where id=? 	又执行了一次查询,缓存没了

4、缓存失效的情况:

  1. 增删改操作,可能会改变原来的数据,所以必定会刷新缓存

  2. 查询不同的东西

  3. 查询不同的Mapper

  4. 手动清除缓存

    1. sqlSession.clearCache();//手动清理缓存
      
    2. @Test
      public void queryUserById(){
               
          SqlSession sqlSession = MybatisUtils.getSqlSession();
          UserMapper mapper = sqlSession.getMapper(UserMapper.class);
          User user = mapper.queryUserById(1);
          System.out.println(user);
          //        mapper.updateUser(new User(3,"王大拿","12121212"));
          sqlSession.clearCache();//手动清理缓存
          User user2 = mapper.queryUserById(1);      //select * from mybatis.user where id=?
          System.out.println(user2);
          System.out.println(user==user2);        //false
          sqlSession.close();
      }
      //手动清理缓存后,即使查询相同的用户还是有两句SQL语句
      

13.3.2、小结

​ 一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段!

​ 一级缓存相当于一个Map

13.4、二级缓存

  • 二级缓存也叫全局缓存,一 级缓存作用域太低了,所以诞生了二级缓存
  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
  • 工作机制:
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    • 如果当前会话关闭了,这个会话对应的一-级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
    • 新的会话查询信息,就可以从二级缓存中获取内容;
    • 不同的mapper查出的数据会放在自己对应的缓存(map) 中;

​ 默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

<cache/>

这些属性可以通过 cache 元素的属性来修改。比如:

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

​ 这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

13.4.1、步骤

步骤:

1. 开启全局缓存

设置名 描述 有效值
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false
<settings>
    
    <setting name="logImpl" value="STDOUT_LOGGING"/>
    
    <setting name="cacheEnabled" value="true"/>
settings>
2. **开启二级缓存**
<mapper namespace="com.atctbu.hjt.dao.UserMapper">
	
    <cache/>

    <select id="queryUserById" resultType="User" parameterType="int">
        select * from mybatis.user where id=#{id}
    select>
    <update id="updateUser" parameterType="user">
        update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id}
    update>
mapper>

​ (可以手动设置useCache为true|false)

<mapper namespace="com.atctbu.hjt.dao.UserMapper">
	
    <cache/>
    <select id="queryUserById" resultType="User" parameterType="int"	useCache="true">
        select * from mybatis.user where id=#{id}
    select>
mapper>

​ 也可以自定义参数

<cache eviction="FIFO"
       flushInterval="60000"
       size="512"
       readOnly="true"/>
3. **测试**
public void queryUserById(){
     
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    SqlSession sqlSession2 = MybatisUtils.getSqlSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);

    User user = mapper.queryUserById(1);
    System.out.println(user);
    sqlSession.close();     //结束后会把缓存丢到二级去

    User user2 = mapper2.queryUserById(1);      //select * from mybatis.user where id=?
    System.out.println(user2);
    System.out.println(user==user2);        //false
    sqlSession2.close();
}
/*
两个不同的sqlSession,一级缓存会查询两次
开启二级缓存后
二级缓存在一级缓存close()死掉后被丢到二级去
只执行了一次查询SQL操作
而却user=user2
*/

1、问题:我们需要将实体类序列化,否则就会报错

Caused by: java.io.NotSerializableException: com.kuang.pojo.User

13.4.2、小结

小结

  • 只要开启了二级缓存,在同一个Mpper下就有效
  • 所有的数据都会先放在一级缓存中:
  • 只有当会话提交,或者关闭的时候,才会提交到二级缓存中!

13.5、缓存原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I1Q38201-1616654175271)(http://m.qpic.cn/psc?/V11Mzdsz1sKJyi/45NBuzDIW489QBoVep5mcQgmwGHT7hjSanKdwxGBiWFizv.f1PPMO33dJns3m7Vm3dA6vrj2TVA0tCEtQ5EPuYokyc2Yyub99ijgG.f.Vps!/b&bo=awRIAgAAAAADJyc!&rf=viewer_4)]

13.6、自定义缓存–Ehcache

​ Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存

​ 要在程序中使用Ehcache,先导包!


<dependency>
    <groupId>org.mybatis.cachesgroupId>
    <artifactId>mybatis-ehcacheartifactId>
    <version>1.1.0version>
dependency>

使用自定义缓存

​ 除了上述自定义缓存的方式,你也可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。

<cache type="com.domain.something.MyCustomCache"/>

使用Ehcache

<cache type="org.mybatis.caches.ehcache.EhBlockingCache"/>

自定义配置Ehcache.xml


<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">

    <diskStore path="./tmpdir/Tmp_EhCache"/>

    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>

    <cache
            name="cloud_user"
            eternal="false"
            maxElementsInMemory="5000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="1800"
            memoryStoreEvictionPolicy="LRU"/>
ehcache>

​ 之后会使用Ridis数据库来做缓存

你可能感兴趣的:(mybatis,mysql,数据库,mybatis)