3-Mybatis

news2024/11/19 15:40:23

文章目录

  • Mybatis概述
    • 什么是Mybatis?
    • Mybatis导入
    • 知识补充
      • 数据持久化
      • 持久层
  • 第一个Mybatis程序:数据的增删改查查
    • 创建环境
    • 编写代码
      • 1、目录结构
      • 2、核心配置文件:resources/mybatis-config.xml
      • 3、mybatis工具类:Utils/MybatisUtils
      • 4、Pojo:User
      • 5、Dao接口:UserDao
      • 6、Mapper配置文件:UserMapper.xml
    • 测试
    • 常见问题汇总
  • 第一个Mybatis程序:完善
    • 优化后的整体目录结构
    • 1、通过db.properties文件配置数据库连接的基本设置
    • 2、配置多套环境
    • 3、起别名
    • 4、映射器 mappers
  • 第一个Mybatis程序:进阶
    • resultMap
      • 问题描述
      • 解决1:改变Sql语句,在sql中起别名
      • 解决2:使用resultMap
      • 注意事项!!!
    • 模糊查询
    • 日志
      • 日志工厂
      • STDOUT_LOGGING使用
      • LOG4J使用
    • 分页
      • Limit分页
      • RowBounds分页
      • 分页插件:PageHelper
    • 注解开发实现
      • 实现
      • 关于 @Param(“”) 注解
      • #{} 和 ${} 的区别
    • Lombok
  • 第二个Mybatis程序:基础,多对一处理
    • 案例背景
    • 编写代码
      • 1、Pojo
      • 2、Utils
      • 3、Dao:按照查询嵌套处理
      • 4、Dao:按照结果嵌套处理
      • 5、结果
    • 总结:association
  • 第二个Mybatis程序:进阶,一对多处理
    • 案例背景
    • 编写代码
      • 1、Pojo
      • 2、Utils
      • 3、Dao:按照查询嵌套处理
      • 4、Dao:按照结果嵌套处理
      • 5、结果
    • 总结:collection
  • 动态SQL
    • 什么是动态SQL?
    • 环境搭建
    • 动态SQL-IF
    • 动态SQL-choose、when、otherwise
    • 动态SQL-Set
    • 动态SQL-trim、sql片段
    • 动态SQL-foreach
  • 缓存
    • 简介
    • Mybatis缓存
      • 一级缓存
      • 二级缓存
      • Mybatis调用顺序
    • 自定义缓存-ehcache
      • 介绍

Mybatis概述

什么是Mybatis?

  • MyBatis 是一款优秀的持久层框架
  • 它支持自定义 SQL、存储过程以及高级映射
  • MyBatis 免除了几乎所有的 JDBC代码以及设置参数和获取结果集
  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录

Mybatis导入

  • Maven仓库:https://mvnrepository.com/artifact/org.mybatis/mybatis

  • 官方文档(中文):https://mybatis.org/mybatis-3/zh/index.html

知识补充

数据持久化

  • 持久化就是将程序的数据从瞬时状态转化为持久状态的过程
    • 生活结合:冰箱冷藏
  • 内存中的数据:断电即失
  • 可以持久化存储数据的媒介:数据库(jdbc)、io文件

持久层

  • 完成持久化工作的代码块
  • Dao层,Service层,Controller层…

第一个Mybatis程序:数据的增删改查查

创建环境

1、Maven项目创建

2、新建一个模块

3、导入相关依赖

4、建立数据库环境

-- 建表
CREATE TABLE `user`(
`id` INT(20) NOT NULL PRIMARY KEY,
`name` VARCHAR(30) DEFAULT NULL,
`pwd` VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;

-- 插入语句
INSERT INTO `user`(`id`, `name`, `pwd`) VALUES
(1, "狂神", "123456"),
(2, "张三", "1234567"),
(3, "李四", "1234568")

编写代码

1、目录结构

后续按代码编写顺序,贴出相关代码

image-20231123184114305

2、核心配置文件:resources/mybatis-config.xml

主要完成对数据库的配置连接和Dao层xml文件的注册

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<!--核心配置文件-->
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/learn_mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <!--    每一个Mapper.xml都需要在MyBatis核心配置文件中注册-->
    <mappers>
        <mapper resource="GSF/Example/Dao/UserMapper.xml"/>
    </mappers>

</configuration>
  • mappers标签下的内容是在写了Dao层代码后,写入的

3、mybatis工具类:Utils/MybatisUtils

每次连接数据库之前,都需要通过sqlSessionFactory来获取sqlSession对象,从而进行后续的数据库连接、sql执行、关闭操作

将该获取过程提取抽象,写为工具类,方便每次调用使用

package GSF.Example.Utils;

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 java.io.IOException;
import java.io.InputStream;

public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;

    static {
        String resources = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resources);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static SqlSession getSqlsession(){
        return sqlSessionFactory.openSession();
    }
}

4、Pojo:User

和数据库中数据表相对应的实体类对象

  • 字段和数据表字段基本一一对应
  • 如果不对应,也可以通过一个resultMap映射,完成不对应字段之间的关联

set、get方法很关键,是执行sql语句完毕后,将查询结果写入实体类对象的关键

package GSF.Example.Pojo;

public class User {
    private int id;
    private String name;
    private String pwd;
    public User(){

    }
    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}

5、Dao接口:UserDao

定义获取数据库对象的接口方法

package GSF.Example.Dao;

import GSF.Example.Pojo.User;

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

public interface UserDao {
    List<User> getUserList();
    // 根据id查询用户
    User getUserById(int id);
    //插入一个用户
    int addUser(User user);
    // 用map的方式插入用户
    int addUserByMap(Map<String, Object> map);
    //修改用户
    int updateUser(User user);
    //删除一个用户
    int deleteUser(int id);
}

6、Mapper配置文件:UserMapper.xml

写入接口方法的具体实现,需要写入相应的sql语句

  • 代替的是原来UserDaoImpl中的相关实现
  • 写完此配置文件,需要向 “2” 的核心配置文件中注册该mapper
<?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">
<!--        namespace=绑定一个Dao/Mapper接口-->
<mapper namespace="GSF.Example.Dao.UserDao">
    <!--    select查询语句,ID对应方法名-->
    <select id="getUserList"  resultType="GSF.Example.Pojo.User">
        select * from learn_mybatis.user
    </select>

    <select id="getUserById" parameterType="int" resultType="GSF.Example.Pojo.User">
        select * from learn_mybatis.user where id=#{id}
    </select>


    <insert id="addUser" parameterType="GSF.Example.Pojo.User">
        insert into learn_mybatis.user(id, name, pwd) values (#{id}, #{name}, #{pwd});
    </insert>

    <insert id="addUserByMap" parameterType="map">
        insert into learn_mybatis.user(id, name, pwd) values (#{user_id}, #{user_name}, #{user_pwd});
    </insert>

    <update id="updateUser" parameterType="GSF.Example.Pojo.User">
        update learn_mybatis.user set name=#{name}, pwd=#{pwd} where id=#{id};
    </update>

    <delete id="deleteUser" parameterType="int">
        delete from learn_mybatis.user where id=#{id};
    </delete>
</mapper>

测试

1、测试:UserTest

package GSF.Example.Dao;

import GSF.Example.Pojo.User;
import GSF.Example.Utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;

public class UserTest {
    @Test
    public void GetUserList(){
        // 官方建议用try...catch包裹,可以不用

        SqlSession sqlsession = MybatisUtils.getSqlsession();
        try {
            UserDao mapper = sqlsession.getMapper(UserDao.class);
            List<User> userList = mapper.getUserList();
            for (User user : userList) {
                System.out.println(user);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            sqlsession.close();
        }

    }

    @Test
    public void GetUserById(){
        SqlSession sqlsession = MybatisUtils.getSqlsession();

        UserDao mapper = sqlsession.getMapper(UserDao.class);
        User user = mapper.getUserById(2);
        System.out.println(user);

        sqlsession.close();
    }

    @Test
    public void AddUser(){
        SqlSession sqlsession = MybatisUtils.getSqlsession();

        UserDao mapper = sqlsession.getMapper(UserDao.class);
        // 插入成功,返回值为1
        int flag = mapper.addUser(new User(10, "GSF", "qwerty"));
        System.out.println(flag);

        sqlsession.commit();
        sqlsession.close();

    }

    @Test
    public void AddUserByMap(){
        SqlSession sqlsession = MybatisUtils.getSqlsession();

        UserDao mapper = sqlsession.getMapper(UserDao.class);
        // 插入成功,返回值为1
        HashMap<String, Object> map_obj = new HashMap<>();
        map_obj.put("user_id", 12);
        map_obj.put("user_name", "121212");
        map_obj.put("user_pwd", "zxcxvz");
        int flag = mapper.addUserByMap(map_obj);
        System.out.println(flag);

        sqlsession.commit();
        sqlsession.close();

    }
    @Test
    public void UpdateUser(){
        SqlSession sqlsession = MybatisUtils.getSqlsession();

        UserDao mapper = sqlsession.getMapper(UserDao.class);
        int flag = mapper.updateUser(new User(1, "123", "12345678"));
        System.out.println(flag);

        sqlsession.commit();
        sqlsession.close();
    }

    @Test
    public void DeleteUser(){
        SqlSession sqlsession = MybatisUtils.getSqlsession();

        UserDao mapper = sqlsession.getMapper(UserDao.class);
        int flag = mapper.deleteUser(1);
        System.out.println(flag);

        sqlsession.commit();
        sqlsession.close();

    }
}

常见问题汇总

第一次写程序,可能遇到各种各样错误,基本都涵盖如下:

  • Dao层Mapper.xml配置文件没有在mybatis-config.xml中注册
  • maven导出资源问题【资源不放在resources文件夹下,正常情况下没法输出到target文件夹中】
    • 通过配置资源导出的目录解决该问题
<!-- 在项目的pom.xml文件中加入此配置 -->

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
        </resource>
    </resources>
</build>
  • src/main/resources和src/main/java目录下的所有.xml文件和.properties文件,都会被识别为资源,从而在构建的时候输出到target目录

第一个Mybatis程序:完善

优化后的整体目录结构

image-20231208111915039

1、通过db.properties文件配置数据库连接的基本设置

db.properties

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/learn_mybatis?useUnicode=true&characterEncoding=utf-8
username=root
password=123456
  • url中不需要转义符了
    • 原url内容:jdbc:mysql://localhost:3306/learn_mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<!--核心配置文件-->
<configuration>
	<!-- properties标签的位置有严格的限制,不能随意位置更改-->
    <properties resource="db.properties"/>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>

    </environments>
</configuration>
  • 删除其他配置,方便理解当前改动

2、配置多套环境

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<!-- 核心配置文件 -->
<configuration>
<!-- properties标签的位置有严格的限制,不能随意位置更改 -->

    <properties resource="db.properties"/>
	<!-- 通过更改default的值,来实现不同环境的切换 -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>

        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/learn_mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>

    </environments>
</configuration>

3、起别名

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<!--核心配置文件-->
<configuration>
	<!--properties标签的位置有严格的限制,不能随意位置更改-->
    <properties resource="db.properties"/>

    <typeAliases>
		<!-- 给特定类名起别名,给出类的全路径标识,给出别名-->
        <typeAlias type="GSF.Example.Pojo.User" alias="User"/>
		<!--导入包名,在类名上没有注解的情况下,包内所有类的别名默认为类名的首字母小写-->
        <package name="GSF.Example.Pojo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

4、映射器 mappers

方式1:将xml文件注册进核心配置文件(mybatis-config.xml)【强烈推荐】

  • 不需要注意xml配置文件和对应的Dao层java代码的名字是否一致
<mappers>
    <mapper resource="GSF/Example/Dao/UserDao.xml"/>
</mappers>

方式2:将接口类的完全限定名注册进核心配置文件(mybatis-config.xml)

<mappers>
  <mapper class="GSF.Example.Dao.UserDao"/>
</mappers>
  • 需要保证:接口和其mapper配置文件同名
  • 需要保证:接口和其mapper配置文件在同一个文件夹下

image-20231123192121045

方式3:将包注册进核心配置文件(mybatis-config.xml)

<mappers>
  <package name="GSF.Example.Dao"/>
</mappers>
  • 需要保证的内容跟方法2一样

第一个Mybatis程序:进阶

resultMap

解决pojo实体类中对应属性名和数据库中对应字段名不一致的问题

问题描述

当数据库的字段和实体类的字段不一致的时候,进行访问,会出现不一致的字段为空

数据库字段

image-20231123192619522

实体类字段

image-20231123192645402

获取id=1的数据库数据

image-20231123192659620

解决1:改变Sql语句,在sql中起别名

<select id="getUserById" parameterType="int" resultType="GSF.Example.Pojo.User">
    select id,name,pwd as password from mybatis.user where id =#{id}
</select>

解决2:使用resultMap

<?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">

<!-- namespace=绑定一个Dao/Mapper接口-->
<mapper namespace="GSF.Example.Dao.UserDao">
    <resultMap id="UserMap" type="GSF.Example.Pojo.User">
		<!-- column:数据库中的列名;property:实体类中的属性名,一样的就无需再配置进来(配置了也不影响)-->
        <result column="pwd" property="password"/>
    </resultMap>

    <!--select查询语句,ID对应方法名-->
    <select id="getUserList" resultMap="UserMap">
        select id, name, pwd from learn_mybatis.user
    </select>

    <select id="getUserById" parameterType="int" resultType="user">
        select id, name, pwd as password from learn_mybatis.user where id=#{id}
    </select>
</mapper>
  • resultMap标签下的result标签定义不一致字段之间的对应关系
  • 对应的select标签:需要将resultType改为resultMap

注意事项!!!

  • 当实体类没有空参构造,即使有字段不一致,Mybatis会强制将实体类中的字段和数据库中的字段进行属性匹配
    • 可能出现字段不一致,但是并没有 字段=null 的情况出现

模糊查询

等到了项目,理解的才能更深刻

现在暂时参考如下文章:https://blog.csdn.net/Adobe_java/article/details/118460709

日志

日志工厂

有代码出问题了,日志就是最好的排错手段

mybatis官方支持的7种实现日志的方式:

image-20231123194424229

  • 掌握:LOG4J(第三方jar包)、STDOUT_LOGGING(标准日志输出)

STDOUT_LOGGING使用

mybatis-config.xml配置即可:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<!--核心配置文件-->
<configuration>
	<!-- properties标签的位置有严格的限制,不能随意位置更改 -->
    <properties resource="db.properties"/>

    <!-- settings标签依旧有严格的位置限制 -->
    <settings>
        <!-- name和value的一个字都不能和官方规定的不一致:不要字符不一致,不要有多余的空格 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <environments default="development">
	......
    </environments>

</configuration>

设置了日志后的控制台输出:

image-20231123194722180

LOG4J使用

什么是log4j

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
  • 我们也可以控制每一条日志的输出格式
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
  • 通过一个配置文件来灵活地进行配置,而不需要修改应用代码

log4j使用

1、导包

Maven:https://mvnrepository.com/artifact/log4j/log4j

2、log4j.properties配置(固定名字)【放在resources文件夹下】

### 配置根 ###
log4j.rootLogger = debug,console,file


### 配置输出到控制台 ###
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold = debug 
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern =  %d{ABSOLUTE} %5p %c{1}:%L - %m%n

### 配置输出到文件 ###
log4j.appender.file = org.apache.log4j.FileAppender
log4j.appender.file.File = ./log/GSF.log

log4j.appender.file.Append = true
log4j.appender.file.Threshold = debug

log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 配置输出到文件,并且每天都创建一个文件 ###
log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFile.File = logs/log.log
log4j.appender.dailyRollingFile.Append = true
log4j.appender.dailyRollingFile.Threshold = debug
log4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFile.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 设置输出sql的级别,其中logger后面的内容全部为jar包中所包含的包名 ###
log4j.logger.org.mybatis=debug
log4j.logger.java.sql=debug
log4j.logger.java.sql.Connection=debug
log4j.logger.java.sql.Statement=debug
log4j.logger.java.sql.PreparedStatement=debug
log4j.logger.java.sql.ResultSet=debug

3、mybatis-config.xml配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<!--核心配置文件-->
<configuration>
	<!-- properties标签的位置有严格的限制,不能随意位置更改-->
    <properties resource="db.properties"/>

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

    <environments default="development">
	......
    </environments>

</configuration>

前三个步骤完成后,访问数据库相关操作,就可以记录到日志中

4、代码中手动设置日志输出

package GSF.Example.Dao;

import GSF.Example.Pojo.User;
import GSF.Example.Utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;

public class UserTest {
    static Logger logger = Logger.getLogger(UserTest.class);

    @Test
    public void GetUserList(){
        logger.info("info级别的日志,基本信息,该种日志打印出来,表示系统的正常运行痕迹");
        logger.debug("debug级别的日志,用于排查错误内容,该种日志打印出来,用于排除系统的错误");
        logger.error("error级别的日志,用于输出错误信息,该种日志打印出来,往往是异常捕捉后的输出");

        SqlSession sqlsession = MybatisUtils.getSqlsession();
        try {
            UserDao mapper = sqlsession.getMapper(UserDao.class);
            List<User> userList = mapper.getUserList();
            for (User user : userList) {
                System.out.println(user);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            sqlsession.close();
        }
    }
}
  • 类中导入包
  • 定义对象
    • 因为日志中可以记录当前日志在什么文件中写入,所以logger的定义需要绑定当前类的class文件
    • 每个使用log4j的方法,都需要在其类中定义logger对象(自认为,待确认?)

分页

Limit分页

SELECT * FROM user limit startIndex,pageSize;
SELECT * FROM user limit 0,2;
SELECT * FROM user limit 3; #[0,3]

1、接口定义

//分页
List<User> getUserByLimit(Map<String,Integer> map);

2、mapper.xml配置

<!--    分页-->
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
    select * from mybatis.user limit #{startIndex},#{pageSize}
</select>

3、测试

@Test
public void  getUserByLimit(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    HashMap<String, Integer> map = new HashMap<>();
    map.put("startIndex",1);
    map.put("pageSize",2);
    List<User> userList = mapper.getUserByLimit(map);
    for (User user : userList) {
        System.out.println(user);
    }

    sqlSession.close();

RowBounds分页

不建议使用

1、接口定义

//分页2
List<User> getUserByRowBounds();

2、mapper.xml配置

<!--分页2-->
<select id="getUserByRowBounds"  resultMap="UserMap">
    select * from mybatis.user
</select>

3、测试

@Test
public void getUserByRowBounds(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    //RowBounds实现
    RowBounds rowBounds = new RowBounds(1, 2);

    //通过java代码层面实现分页
    List<User> userList = sqlSession.selectList("com.qjd.dao.UserMapper.getUserByRowBounds",null,rowBounds);
    for (User user : userList) {
        System.out.println(user);
    }

    sqlSession.close();
}

分页插件:PageHelper

官方地址:https://pagehelper.github.io/docs/howtouse/

注解开发实现

本质:基于反射和动态代理实现

实现

1、接口上写入注解

package GSF.Example.Dao;

import GSF.Example.Pojo.User;
import org.apache.ibatis.annotations.*;

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

public interface UserMapper {
    @Select("select * from learn_mybatis.user")
    List<User> getUserList();

    //方法存在多个参数,所有的参数前面前面必须加上 @Param("") 注解
    @Select("select * from learn_mybatis.user where id=#{id}")
    User getUserByID(@Param("id") int id);

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

    @Insert("insert into learn_mybatis.user(id,name,pwd) values(#{user_id},#{user_name},#{user_pwd})")
    int addUserByMap(Map<String, Object> map);

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

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

2、mybatis-config.xml核心配置文件的配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<!--核心配置文件-->
<configuration>
	......
    <!--    注册-->
    <mappers>
        <mapper class="GSF.Example.Dao.UserMapper"/>
    </mappers>

</configuration>

3、测试

package GSF.Example.Dao;

import GSF.Example.Pojo.User;
import GSF.Example.Utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;

public class UserTest {
    @Test
    public void GetUserList() {
        // 官方建议用try...catch包裹,可以不用

        SqlSession sqlsession = MybatisUtils.getSqlsession();
        try {
            UserMapper mapper = sqlsession.getMapper(UserMapper.class);
            List<User> userList = mapper.getUserList();
            for (User user : userList) {
                System.out.println(user);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sqlsession.close();
        }

    }


    @Test
    public void GetUserById() {
        SqlSession sqlsession = MybatisUtils.getSqlsession();

        UserMapper mapper = sqlsession.getMapper(UserMapper.class);
        User user = mapper.getUserByID(2);
        System.out.println(user);

        sqlsession.close();
    }

    @Test
    public void AddUser() {
        SqlSession sqlsession = MybatisUtils.getSqlsession();

        UserMapper mapper = sqlsession.getMapper(UserMapper.class);
        // 插入成功,返回值为1
        int flag = mapper.addUser(new User(20, "GSF", "qwerty"));
        System.out.println(flag);

        sqlsession.commit();
        sqlsession.close();

    }

    @Test
    public void AddUserByMap() {
        SqlSession sqlsession = MybatisUtils.getSqlsession();

        UserMapper mapper = sqlsession.getMapper(UserMapper.class);
        // 插入成功,返回值为1
        HashMap<String, Object> map_obj = new HashMap<>();
        map_obj.put("user_id", 21);
        map_obj.put("user_name", "121212");
        map_obj.put("user_pwd", "zxcxvz");
        int flag = mapper.addUserByMap(map_obj);
        System.out.println(flag);

        sqlsession.commit();
        sqlsession.close();

    }

    @Test
    public void UpdateUser() {
        SqlSession sqlsession = MybatisUtils.getSqlsession();

        UserMapper mapper = sqlsession.getMapper(UserMapper.class);
        int flag = mapper.updateUser(new User(1, "123", "12345678"));
        System.out.println(flag);

        sqlsession.commit();
        sqlsession.close();
    }

    @Test
    public void DeleteUser() {
        SqlSession sqlsession = MybatisUtils.getSqlsession();

        UserMapper mapper = sqlsession.getMapper(UserMapper.class);
        int flag = mapper.deleteUser(1);
        System.out.println(flag);

        sqlsession.commit();
        sqlsession.close();
    }

}

关于 @Param(“”) 注解

  • 基本类型的参数或者String需要加上
    • 如果只有一个基本类型的话,可以忽略,但是建议加上
  • 引用类型的参数不需要加该注解,比如:User、Map…
  • 在sql中引用的就是@Param(“”)注解中设定的属性名

#{} 和 ${} 的区别

#{}${}
预编译处理是字符串替换
处理#{}时,会将sql中的#{}整体替换为占位符(即:?),调用PreparedStatement的set方法来赋值在处理 $ { } 时,就是把 ${ } 替换成变量的值
可有效防止SQL注入无法防止SQL注入

预编译的机制:

  • 预编译是提前对SQL语句进行预编译,而后再调用SQL,注入的参数就不会再进行SQL编译
  • 而SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译为SQL时轻而易举的通过,从而导致数据泄露
  • 预编译机制则可以很好的防止SQL注入

Lombok

Lombok项目是一个Java库,他是一个插件,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来消除Java类中的大量样板代码。仅五个字符@Data就可以替换数百行代码从而产生干净,简洁且易于维护的Java类

使用步骤

  • 在IDEA中安装Lombok插件
  • 在项目中导入Lombok的jar包
  • 在实体类上加入注解
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String password;
}
注解含义
@Data提供属性的set、get方法、无参构造
@AllArgsConstructor提供类的全参构造
@NoArgsConstructor提供类的无参构造

第二个Mybatis程序:基础,多对一处理

案例背景

描述

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

要求

  • 查询所有的学生,及其关联的老师信息

数据库创建

-- 新建老师表
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=utf8

-- 插入学生数据
INSERT INTO `student` (`id`, `name`, `tid`) VALUES 
('1', '小明', '1'),
('2', '小红', '1'),
('3', '小张', '1'),
('4', '小李', '1'),
('5', '小王', '1')

编写代码

1、Pojo

  • 老师类
import lombok.Data;

@Data
public class Teacher {
    private int id;
    private String name;
}
  • 学生类
import lombok.Data;

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

2、Utils

  • 写法参照《第一个Mybatis程序:数据的增删改查查》

3、Dao:按照查询嵌套处理

思路:子查询

  • 查询所有的学生信息
  • 根据查询出来的学生的tid,寻找对应的老师

Mapper.xml

<select id="getTeacher"  resultType="GSF.Example.Pojo.Teacher">
    select * from learn_mybatis.teacher where id=#{id}
</select>

<!--根据查询嵌套处理-->
<select id="getStudent" resultMap="getStudent">
    select id, name, tid from learn_mybatis.student;
</select>

<resultMap id="getStudent" type="GSF.Example.Pojo.Student">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <association property="teacher" column="tid" javaType="GSF.Example.Pojo.Teacher" select="getTeacher"/>
</resultMap>

test.java

@Test
public void GetStudent() {
    // 官方建议用try...catch包裹,可以不用

    SqlSession sqlsession = MybatisUtils.getSqlsession();
    UserDao mapper = sqlsession.getMapper(UserDao.class);
    List<Student> students = mapper.getStudent();
    for (Student student : students) {
        System.out.println(student);
    }
    sqlsession.close();
}

4、Dao:按照结果嵌套处理

思路:子查询

  • 查询所有的学生信息、老师信息
  • 将结果嵌套处理

Mapper.xml

<select id="getStudent2" resultMap="getStudent2">
    select  s.id s_id ,s.name s_name, t.id t_id, t.name t_name 
    from learn_mybatis.student s, learn_mybatis.teacher t
    where s.tid=t.id
</select>

<resultMap id="getStudent2" type="GSF.Example.Pojo.Student">
    <result property="id" column="s_id"/>
    <result property="name" column="s_name"/>
    <association property="teacher" javaType="GSF.Example.Pojo.Teacher">
        <result property="id" column="t_id"/>
        <result property="name" column="t_name"/>
    </association>
</resultMap>

test.java

@Test
public void GetStudent() {
    // 官方建议用try...catch包裹,可以不用

    SqlSession sqlsession = MybatisUtils.getSqlsession();
    UserDao mapper = sqlsession.getMapper(UserDao.class);
    List<Student> students = mapper.getStudent();
    for (Student student : students) {
        System.out.println(student);
    }
    sqlsession.close();
}

5、结果

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=秦老师))

总结:association

  • association标签,处理关联关系,即多对一
    标签属性含义
    javaType集合对象的类型

第二个Mybatis程序:进阶,一对多处理

案例背景

描述

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

要求

  • 查询对应id的老师信息,及其包含的所有学生

数据库创建

  • 同上

编写代码

1、Pojo

  • 老师类
package GSF.Example.Pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Teacher {
    private int id;
    private String name;
    private List<Student> students;
}
  • 学生类
package GSF.Example.Pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Student {
    private int id;
    private String name;
    private int tid;
}

2、Utils

  • 写法参照《第一个Mybatis程序:数据的增删改查查》

3、Dao:按照查询嵌套处理

思路:子查询

  • 查询对应id的老师信息
  • 根据查询出来的老师的tid,寻找对应的学生

Mapper.xml

<select id="getTeacher2" resultMap="TeacherStudent2">
    select * from learn_mybatis.teacher where id=#{id}
</select>
<resultMap id="TeacherStudent2" type="GSF.Example.Pojo.Teacher">
	<!--两个地方用到id,这个result就得写出来,不然不展示老师的id-->
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <collection property="students" column="id" javaType="ArrayList" ofType="GSF.Example.Pojo.Student" select="getStudentByTeacherId"/>
</resultMap>

<select id="getStudentByTeacherId" resultType="GSF.Example.Pojo.Student">
    select * from learn_mybatis.student where tid =#{id}
</select>

test.java

@Test
public void GetTeacherById2() {
    // 官方建议用try...catch包裹,可以不用

    SqlSession sqlsession = MybatisUtils.getSqlsession();
    UserDao mapper = sqlsession.getMapper(UserDao.class);
    Teacher teacher = mapper.getTeacher2(1);
    System.out.println(teacher);
    sqlsession.close();
}

4、Dao:按照结果嵌套处理

思路:子查询

  • 查询对应id的所有老师、学生信息
  • 将结果嵌套处理

Mapper.xml

<select id="getTeacher"  resultMap="getTeacher">
    select s.id s_id, s.name s_name, t.name t_name, t.id t_id
    from learn_mybatis.student s, learn_mybatis.teacher t
    where s.tid = t.id and t.id=#{id}
</select>

<resultMap id="getTeacher" type="GSF.Example.Pojo.Teacher">
    <result property="id" column="t_id"/>
    <result property="name" column="t_name"/>
    <collection property="students" javaType="ArrayList" ofType="GSF.Example.Pojo.Student">
        <result property="id" column="s_id"/>
        <result property="name" column="s_name"/>
        <result property="tid" column="t_id"/>
    </collection>
</resultMap>

test.java

@Test
public void GetTeacherById() {
    // 官方建议用try...catch包裹,可以不用

    SqlSession sqlsession = MybatisUtils.getSqlsession();
    UserDao mapper = sqlsession.getMapper(UserDao.class);
    Teacher teacher = mapper.getTeacher(1);
    System.out.println(teacher);
    sqlsession.close();
}

5、结果

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)])

总结:collection

  • collection标签,处理集合关系,即一对多

    标签属性含义
    javaType集合对象的类型
    ofType集合对象的类型

动态SQL

什么是动态SQL?

简单来说,就是根据不同的判断条件和需要,来动态拼接SQL语句。

在Mybatis之前,动态SQL的描述需要耗费很大精力。借助强大的OGNL表达式,Mybatis3替换了之前的大部分元素,使得动态SQL的描述变得简单很多。

环境搭建

  • 数据库环境
-- 
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

INSERT INTO `blog`(`id`,`title`,`author`,`create_time`,`views`) VALUES
(1, "如何学Python", "青", "2022-10-11", 300),
(2, "如何学Java", "刘", "2022-10-12", 400),
(3, "如何学Django", "郭", "2022-10-13", 700)
  • java项目环境及相关配置:参考前文

  • Dao:实体类

@Data
public class Blog {
    private int id;
    private String title;
    private String author;
    private Date   createTime;
    private int views;   
}

动态SQL-IF

public interface UserDao {
    List<Blog> getBlogIf(Map<String, Object> map);
}
<select id="getBlogIf"  parameterType="map" resultType="GSF.Example.Pojo.Blog">
    select * from learn_mybatis.blog where 1=1
    <if test="title != null">
        and title=#{title}
    </if>

    <if test="author != null">
        and author=#{author}
    </if>
</select>
-- 若传入空的map,真实的sql语句:
select * from learn_mybatis.blog where 1=1

-- 若传入的map带有title键,真实的sql语句:
select * from learn_mybatis.blog where 1=1 and title=?

-- 若传入的map带有title键和author键,真实的sql语句:
select * from learn_mybatis.blog where 1=1 and title=? and author=?
  • IF标签不具备自动添加and的功能,每个拼接的子SQL语句,需要自行添加and
@Test
public void GetBlogIf() {
    SqlSession sqlsession = MybatisUtils.getSqlsession();
    UserDao mapper = sqlsession.getMapper(UserDao.class);
    Map<String, Object> objectMap = new HashMap<>();

    //注释掉,就返回默认检索的所有情况
    objectMap.put("title", "如何学Django");
    objectMap.put("author", "刘");

    List<Blog> blogIf = mapper.getBlogIf(objectMap);
    for (Blog blog : blogIf) {
        System.out.println(blog);
    }
    sqlsession.close();
}

动态SQL-choose、when、otherwise

public interface UserDao {
    List<Blog> getBlogChoose_When_Otherwise(Map<String, Object> map);
}
<select id="getBlogChoose_When_Otherwise"  parameterType="map" resultType="GSF.Example.Pojo.Blog">
    select * from learn_mybatis.blog
    <!-- 这里用到了下一个查询才讲述的where标签 -->
    <where>
        <choose>
            <when test="title != null">
               and title = #{title}
            </when>

             <when test="author !=null">
                and author = #{author}
            </when>

            <otherwise>
                and id = 1
            </otherwise>
        </choose>
    </where>
</select>
-- 若传入空的map,真实的sql语句:
select * from learn_mybatis.blog where id=1

-- 若传入的map带有title键,真实的sql语句:
select * from learn_mybatis.blog where title=?

-- 若传入的map带有title键和author键,真实的sql语句:
select * from learn_mybatis.blog where title=?
  • choose、when、otherwisel类似java的switch用法
    • 从上到下的判断语句,遇到满足的就用,即使后续有条件也满足,也不会调用
  • where标签具备自动添加where字符和删除首个子SQL语句and字符的功能
@Test
public void GetBlogChoose_When_Otherwise() {
    SqlSession sqlsession = MybatisUtils.getSqlsession();
    UserDao mapper = sqlsession.getMapper(UserDao.class);
    Map<String, Object> objectMap = new HashMap<>();

    //注释掉,就返回默认检索的所有情况
    //objectMap.put("title", "如何学Django");
    //objectMap.put("author", "刘");

    List<Blog> blogIf = mapper.getBlogChoose_When_Otherwise(objectMap);
    for (Blog blog : blogIf) {
        System.out.println(blog);
    }

    sqlsession.close();
}

动态SQL-Set

public interface UserDao {
    int updateBlogSet(Map<String, Object> map);
}
<update id="updateBlogSet" parameterType="map">
    update learn_mybatis.blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author},
        </if>
    </set>
    where id = #{id}
</update>
-- 若传入空的map或仅仅有id的map,真实的sql语句:
报错

-- 若传入的map带有title键,真实的sql语句:
update learn_mybatis.blog SET title = ? where id = ?

-- 若传入的map带有title键和author键,真实的sql语句:
update learn_mybatis.blog SET title = ?, author = ? where id = ?
  • set标签具备补充set字符和删除sql语句末尾“,”字符的功能
  • if标签中sql子句末尾的“,”需要写入,不然sql语句报错
@Test
public void UpdateBlogSet() {
    SqlSession sqlsession = MybatisUtils.getSqlsession();
    UserDao mapper = sqlsession.getMapper(UserDao.class);
    Map<String, Object> objectMap = new HashMap<>();

    objectMap.put("id", 1);
    objectMap.put("title", "如何学Python-修改");
    objectMap.put("author", "刘");

    int blogIf = mapper.updateBlogSet(objectMap);
    System.out.println(blogIf);

    sqlsession.commit();
    sqlsession.close();
}

动态SQL-trim、sql片段

public interface UserDao {
    int updateBlogTrim(Map<String, Object> map);
}
<sql id="if-title-author">
    <if test="title != null">
        title = #{title},
    </if>
    <if test="author != null">
        author = #{author},
    </if>
</sql>

<update id="updateBlogTrim" parameterType="map">
    update learn_mybatis.blog
    <trim prefix="SET" suffixOverrides=",">
        <include refid="if-title-author">
        </include>
    </trim>
    where id = #{id}
</update>
-- 若传入空的map或仅仅有id的map,真实的sql语句:
报错

-- 若传入的map带有title键,真实的sql语句:
update learn_mybatis.blog SET title = ? where id = ?

-- 若传入的map带有title键和author键,真实的sql语句:
update learn_mybatis.blog SET title = ?, author = ? where id = ?
  • trim标签可以自定义待拼接sql语句的相关前缀、后缀的补充操作及去除操作
  • 上述用trim标签,实现set标签的相关功能
trim标签属性描述
prefix给sql语句拼接的前缀
suffix给sql语句拼接的后缀
prefixOverrides去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND"
suffixOverrides去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定,假设该属性指定为",“,当sql语句的结尾为”,“,trim标签将会去除该”,"
// 使用trim标签和SQL片段
@Test
public void UpdateBlogTrim_SQLFragment() {
    SqlSession sqlsession = MybatisUtils.getSqlsession();
    UserDao mapper = sqlsession.getMapper(UserDao.class);
    Map<String, Object> objectMap = new HashMap<>();

    objectMap.put("id", 1);
    objectMap.put("title", "如何学Python-修改");
    objectMap.put("author", "青-修改-修改");

    int blogIf = mapper.updateBlogTrim(objectMap);
    System.out.println(blogIf);

    sqlsession.commit();
    sqlsession.close();
}

动态SQL-foreach

public interface UserDao {
    List<Blog> getBlogForeach(Map<String, Object> map);
}
<select id="getBlogForeach"  parameterType="map" resultType="GSF.Example.Pojo.Blog">
    select * from learn_mybatis.blog
    <where>
        <foreach collection="ids" item="id" open="and (" close=")" separator="or">
            id=#{id}
        </foreach>
    </where>
</select>
-- 若传入空的map,真实的sql语句:
select * from learn_mybatis.blog

-- 若传入的map中的list带有值1,真实的sql语句:
select * from learn_mybatis.blog WHERE ( id=? )

-- 若传入的map中的list带有值1、2、3,真实的sql语句:
select * from learn_mybatis.blog WHERE ( id=? or id=? or id=? )
  • 提供遍历操作
  • 还是传入的map,只是map的键对应的值是一个list
@Test
public void GetBlogForeach() {
    SqlSession sqlsession = MybatisUtils.getSqlsession();
    UserDao mapper = sqlsession.getMapper(UserDao.class);

    ArrayList<Integer> ids = new ArrayList<>();
    ids.add(1);
    ids.add(2);
    ids.add(3);
    ids.add(4);

    HashMap<String, Object> map = new HashMap<>();
    map.put("ids", ids);

    List<Blog> blogIf = mapper.getBlogForeach(map);
    for (Blog blog : blogIf) {
        System.out.println(blog);
    }
    
    sqlsession.close();
}

缓存

简介

什么是缓存?

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

为什么使用缓存?

  • 减少和数据库的交互次数,较少系统开销,提高系统效率

什么样的数据能使用缓存?

  • 经常查询而且不经常改变的数据

Mybatis缓存

MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。

MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存

  • 默认情况下,只有一级缓存开启 (SqlSession级别的缓存,也称为本地缓存)
  • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
  • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

一级缓存

也称为本地缓存

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

测试验证一级缓存

@Test
public void  test(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.queryUserById(1);
    System.out.println(user);
    System.out.println("=================================================================");
    User user2 = mapper.queryUserById(1);
    System.out.println(user2);

    System.out.println(user==user2);

    sqlSession.close();
}
  • 返回结果为true,且只执行了一次sql语句

一级缓存失效条件

  • 查询不同的东西
  • 增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
  • 查询不同的Mapper.xml
  • 手动清理缓存!
@Test
public void  test(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.queryUserById(1);
    System.out.println(user);

    // 更新数据,导致缓存时效
    //mapper.updateUser(new User(2,"niahoooo","309487"));

    // 手动清理缓存,导致缓存时效
    //sqlSession.clearCache();

    System.out.println("=================================================================");
    User user2 = mapper.queryUserById(1);
    System.out.println(user2);

    System.out.println(user==user2);

    sqlSession.close();
}

一级缓存生命周期

生命周期为一个特定mapper.xml的一次sqlsession会话

二级缓存

二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存

基于namespace级别的缓存,一个名称空间,对应一个二级缓存

工作机制:

  • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中:
  • 如果当前会话关闭了,这个会话对应的一级缓存就没了,一级缓存中的数据被保存到二级缓存中;
  • 新的会话查询信息,就可以从二级缓存中获取内容
  • 不同的mapper.xml查出的数据会放在自己对应的缓存(map)中

测试验证二级缓存

  • 在全局开启二级缓存:mybatis-config.xml
<setting name="cacheEnable" value="true"/>
  • 在要开启缓存的mapper.xml中开启
<cache  eviction="FIFO"
        flushInterval="60000"
        size="512"
        readOnly="true"/>
  • 测试
@Test
public void  test(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    SqlSession sqlSession2 = MybatisUtils.getSqlSession();

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

    User user = mapper.queryUserById(1);
    System.out.println(user);
    
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
    User user2 = mapper.queryUserById(1);
    System.out.println(user2);

    System.out.println(user==user2);

    sqlSession.close();
    sqlSession2.close();
}

注意

  • 我们需要将实体类序列化(实现Serializable接口),否则就会报错
  • sqlsession关闭的时候一定要在最后关闭,不能先关闭sqlsession再关闭sqlsession2,这样会导致Cause: org.apache.ibatis.executor.ExecutorException: Executor was closed

二级缓存的生命周期

在同一个Mapper.xml下的多次Sqlsession

只有当sqlsession关闭的时候,数据才会从一级缓存扔到二级缓存

Mybatis调用顺序

  1. 先看二级缓存中有没有
  2. 再看一级缓存中有没有
  3. 查询数据库

image-20231129103125446

  • 一二级缓存都没有,查询数据库,查询后将数据放入一级缓存

自定义缓存-ehcache

介绍

  • EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider
  • Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存

具体使用,用到再说,开发中常用Redis数据库来做缓存

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1294514.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电商行业邮件营销全攻略:解决方案与实施技巧

在电商行业这样多元化且快节奏的行业中&#xff0c;有效、及时的沟通则是其业务发展的基础之一。您需要在客户生命周期的各个阶段与他们进行交互&#xff0c;需要在恰当的时机推送他们需要的信息&#xff0c;更重要的是&#xff0c;您需要根据客户的购买经历迅速制定营销方案&a…

STL(五)(queue篇)

我发现之前一版在电脑上看 常用函数部分 没有问题,由于是手打上去的,在手机上看会发生错位问题,现已将电脑原版 常用函数部分 截图改为图片形式,不会再发生错位问题,非常感谢大家的支持 ### priority_queue优先队列出现频率非常高,尤为重要(是一定要掌握的数据结构) 1.queue队…

vmware虚拟机17 安装macos14过程及问题处理亲测

前期准备 1、可引导可虚拟机安装的macOS Sonoma 14 ISO镜像安装文件 我找到得地址&#xff0c;下载自行解决啦 2、VMware虚拟机应用软件 官网下载就好&#xff0c;搜个码搞定 3、解锁工具macOS Unlocker 开始安装&#xff1a; 1、打开VMware软件&#xff0c;新建一个系统…

[MySQL] MySQL复合查询(多表查询、子查询)

前面我们学习了MySQL简单的单表查询。但是我们发现&#xff0c;在很多情况下单表查询并不能很好的满足我们的查询需求。本篇文章会重点讲解MySQL中的多表查询、子查询和一些复杂查询。希望本篇文章会对你有所帮助。 文章目录 一、基本查询回顾 二、多表查询 2、1 笛卡尔积 2、2…

【Vulnhub 靶场】【Funbox: GaoKao】【简单】【20210606】

1、环境介绍 靶场介绍&#xff1a;https://www.vulnhub.com/entry/funbox-gaokao,707/ 靶场下载&#xff1a;https://download.vulnhub.com/funbox/FunboxGaoKao.ova 靶场难度&#xff1a;简单 发布日期&#xff1a;2021年06月06日 文件大小&#xff1a;1.3 GB 靶场作者&#…

虚拟化之指令的Trap和仿真

有时,虚拟机监控程序需要在虚拟机(VM)中模拟操作。例如,VM内的软件可能尝试配置与功耗管理或缓存一致性相关的低级处理器控件。通常,您不希望将VM直接访问这些控件,因为它们可能被用于突破隔离,或影响系统中的其他VM。 trap在执行给定操作(例如读取寄存器)时引发异常…

Python零基础入门之详解sort排序使用

文章目录 1.前言2.环境准备3.程序实现4.sort拓展关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游戏源码五、面试资料六、Python兼职渠道 1.前言 昨天一…

IBL环境贴图原理及着色器实现【基于图像的照明】

IBL - Image Based Lighting - 也就是基于图像的照明&#xff0c;是一组照亮物体的技术&#xff0c;不是像上一章那样通过直接分析光&#xff0c;而是将周围环境视为一个大光源。 这通常是通过操作立方体贴图环境贴图&#xff08;取自现实世界或从 3D 场景生成&#xff09;来完…

vuepress-----13、分割config

13、分割config config.js const headConfig require(./config/headConfig); const pluginsConfig require(./config/pluginsConfig); const themeConfig require(./config/themeConfig)module.exports {title: "小邵子",description: 小邵子的个人笔记,head: he…

openEuler JDK21 部署 Zookeeper 集群

zookeeper-jdk21 操作系统&#xff1a;openEuler JDK&#xff1a;21 主机名IP地址spark01192.168.171.101spark02192.168.171.102spark03192.168.171.103 安装 1. 升级内核和软件 yum -y update2. 安装常用软件 yum -y install gcc gcc-c autoconf automake cmake make \zl…

tomcat篇---第一篇

系列文章目录 文章目录 系列文章目录前言一、Tomcat的缺省端口是多少,怎么修改?二、tomcat 有哪几种Connector 运行模式(优化)?三、Tomcat有几种部署方式?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文…

04数据平台Flume

Flume 功能 Flume主要作用&#xff0c;就是实时读取服务器本地磁盘数据&#xff0c;将数据写入到 HDFS。 Flume是 Cloudera提供的高可用&#xff0c;高可靠性&#xff0c;分布式的海量日志采集、聚合和传输的系统工具。 Flume 架构 Flume组成架构如下图所示&#xff1a; A…

重要功能:妙手ERP正式接入北俄海外仓,助力跨境卖家出海俄罗斯市场!

近日&#xff0c;妙手ERP正式接入北俄海外仓&#xff0c;并支持Ozon、速卖通AliExpress、Joom平台卖家同步推送商品、查看库存清单、高效处理订单&#xff0c;助力卖家掘金俄罗斯市场。 关于北俄海外仓 北俄海外仓为北俄国际旗下项目&#xff0c;北俄国际成立于2008年&#xf…

【Java用法】Lombok中@SneakyThrows注解的使用方法和作用

Lombok中SneakyThrows注解的使用方法和作用 一、SneakyThrows的作用二、SneakyThrows注解原理 一、SneakyThrows的作用 普通Exception类,也就是我们常说的受检异常或者Checked Exception会强制要求抛出它的方法声明throws&#xff0c;调用者必须显示的去处理这个异常。设计的目…

企业集团采购系统(供应商、询价、招投标)-源码

一、业务需求 企业招标询价供应商管理系统是一种专业的采购管理系统&#xff0c;旨在帮助企业实现供应商关系的管理和采购成本的控制。该系统涵盖了企业采购管理的各个方面&#xff0c;包括采购预算、供应商管理、产品管理、采购计划、询价、竞价、招标、采购订单、采购合同执…

Docker构建自定义镜像

创建一个docker-demo的文件夹,放入需要构建的文件 主要是配置Dockerfile文件 第一种配置方法 # 指定基础镜像 FROM ubuntu:16.04 # 配置环境变量&#xff0c;JDK的安装目录 ENV JAVA_DIR/usr/local# 拷贝jdk和java项目的包 COPY ./jdk8.tar.gz $JAVA_DIR/ COPY ./docker-demo…

前端vue3实现本地及在线文件预览(含pdf/txt/mp3/mp4/docx/xlsx/pptx)

一、仅需实现在线预览&#xff0c;且文件地址公网可访问 &#xff08;一&#xff09;微软office免费预览&#xff08;推荐&#xff09; 支持doc/docx/xls/xlsx/ppt/pptx等多种office文件格式的免费预览 //示例代码//​在https://view.officeapps.live.com/op/view.aspx?src…

使用autodl服务器,两个3090显卡上运行, Yi-34B-Chat-int4模型,并使用vllm优化加速,显存占用42G,速度23 words/s

1&#xff0c;演示视频地址 https://www.bilibili.com/video/BV1Hu4y1L7BH/ 使用autodl服务器&#xff0c;两个3090显卡上运行&#xff0c; Yi-34B-Chat-int4模型&#xff0c;用vllm优化&#xff0c;增加 --num-gpu 2&#xff0c;速度23 words/s 2&#xff0c;使用3090显卡 和…

JavaScript中的构造函数是什么,如何使用ES6中的类来进行构造函数的封装和继承?

目录 学习目标&#xff1a; 学习内容&#xff1a; 学习时间&#xff1a; 学习讲解&#xff1a; 深入对象 创建对象三种方式 构造函数 练习 实例化执行过程 说明&#xff1a;1. 创建新对象2. 构造函数this指向新对象3. 执行构造函数代码&#xff0c;修改this&#xff…

微信小程序调用相机拍摄或手机相册

wx.chooseMedia(Object object) 功能描述 拍摄或从手机相册中选择图片或视频。