重温Mybatis,收获满满

Mybatis

在这里插入图片描述

1、简介

1.1、什么是Mybatis

  • MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型。
  • MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github
  • maven依赖:
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.5.7</version>
</dependency>
  • **Maven仓库地址:**https://mvnrepository.com/

  • **中文文档:**https://mybatis.net.cn/index.html

1.2、为什么要用Mybatis?

  • 帮助程序员将数据存到数据库里
  • 方便
  • 传统的JDBC太复杂了
  • 优点:
    • 简单易学
    • 灵活
    • sql和代码分离,提高了可维护性
    • 提供映射标签,支持对象与数据库的ORM字段关系映射
    • 提供对象关系映射标签,支持对象关系组建维护
    • 提供xml标签,支持编写动态sql

最重要的:使用的人多

2、第一个Mybatis程序

搭建环境–>导入Mybatis–>编写代码–>测试

2.1、搭建环境

创建数据库

create database `mybatis`

use `mybatis`

create table `user`(
`id` int(20) primary key,
`name` varchar(200) default null,
`pwd` varchar(200) default null
)engine=innodb default charset utf8;

insert into `user` values
(1,'mybatis1号','asdf'),
(2,'mybatis2号','asdfasdf'),
(3,'mybatis3号','123456')

创建项目

  1. Idea新建空的maven项目

  2. 删除src目录(将项目变成父工程)

  3. pom导入依赖

    <dependencies>
        <!--mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
        <!--mybatis依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <!--juint依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>
    </dependencies>
    

2.2、创建子模块

  • 父项目 New Module (新建子项目)

  • 子项目resource目录下新建文件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">
    <!--mybatis核心配置文件-->
    <configuration>
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://127.0.0.1:3306/smbms?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
    </configuration>
    

2.3、编写代码

  • 编写MybatisUtils工具类

    public class MybatisUtils {
        private static SqlSessionFactory sqlSessionFactory;
        static {
            try {
                //第一步:获取sqlSessionFactory对象
                String resource = "mybatis-config.xml";
                InputStream inputStream = Resources.getResourceAsStream(resource);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        //获取sqlSession,可以操作数据库的对象
        public static SqlSession getSqlSession(){
            return sqlSessionFactory.openSession();
        }
    }
    
  • 实体类

    public class User implements Serializable {
        private int id;
        private String name;
        private String pwd;
        //省略构造方法、get、set方法
    }
    
  • Mapper接口

    public interface UserMapper {
        List<User> getAllUsers();
    }
    
  • Mapper.xml实现接口

    <?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">
    <!--mybatis-3-mapper.dtd:约束文件的名称,限制和检查在当前文件中出现的标签和属性符合mybatis的要求-->
    <!--namespace:命名空间,要有唯一的值,要求使用dao接口的权限定名称(一个dao接口对应一个mapper,namespace指明对应哪个dao接口)-->
    <mapper namespace="com.lx.dao.UserMapper">
        <!-- 所有的数据库操作都要写在mapper标签中,可以使用特定的标签表示数据库中的特定操作 -->
        <!--
            id:对应的UserMapper中方法名
            resultType:对应的方法返回类型
        -->
        <select id="getAllUsers" resultType="com.lx.pojo.User">
            select * from `user`
        </select>
    </mapper>
    
  • pom文件需要加入配置(maven导出资源问题,放入pom.xml文件中)
    <!--静态资源导出,不管是java文件下还是resources文件下,都导出-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
    
  • mybatis-config.xml需要将写好的Mapper注册进去

    <!--mybatis核心配置文件-->
    <configuration>
        <!--注册mapper才可以被调用-->
        <mappers>
            <mapper resource="com/lx/daoimpl/UserMapper.xml"/>
        </mappers>
    </configuration>
    
  • 测试

    @Test
    public void testGetAllUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
    
        //方式1(推荐使用)
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> allUsers = mapper.getAllUsers();
    
        //方式2(需要强制换,不推荐)
        // List<User> allUsers = sqlSession.selectList("com.lx.dao.UserMapper.getAllUsers");
    
        for (User user : allUsers) {
            System.out.println(user);
        }
        sqlSession.close();
    }
    
  • 输出

    User{id=1, name='mybatis1号', pwd='asdf'}
    User{id=2, name='mybatis2号', pwd='asdfasdf'}
    User{id=3, name='mybatis3号', pwd='123456'}
    

3、认识Mapper.xml中属性

dtd头文件:
<?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:所有的数据库操作都要写在mapper标签中,可以使用特定的标签表示数据库中的特定操作
namespace:命名空间,要有唯一的值,值和Dao/Mapper的包名一致
id:对应的UserMapper中方法名
resultType:对应的方法返回类型
parameterType:参数值类型(int、String、实体类等等)
select:查询标签
insert:新增标签
update:更新标签
delete:删除标签

4、增删改查

注意点:增删改需要提交事务才可以操作成功

提交事务两种方法:

1、自动提交
//设置为true,则将自动提交事务打开
public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession(true);
        }
2、手动提交
@Test
public void addUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.addUser(new User(4,"Mybatis4号","asdfasfadsfasdfadsf"));
        //手动通过commit方法提交事务
        sqlSession.commit();
        sqlSession.close();
        }
  • //根据Id获取用户
    User getUserById(int id);
    
    <select id="getUserById" resultType="com.lx.pojo.User" parameterType="int">
        select * from `user` where id = #{id}
    </select>
        
    @Test
    public void getUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.getUserById(1);
        System.out.println(user);
        sqlSession.close();
    }
    
    输出:User{id=1, name='mybatis1号', pwd='asdf'}
    
  • 增**(提交事务)**

    //新增用户
    void addUser(User user);
    
    <insert id="addUser" parameterType="com.lx.pojo.User">
        insert into `user` values(#{id},#{name},#{pwd})
    </insert>
        
    @Test
    public void addUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.addUser(new User(4,"Mybatis4号","asdfasfadsfasdfadsf"));
        //需要提交事务
        sqlSession.commit();
        sqlSession.close();
    }
    
  • 改**(提交事务)**

    //根据Id修改用户
    void updateUser(User user);
    
    <update id="updateUser" parameterType="com.lx.pojo.User">
        update `user` set name = #{name},pwd = #{pwd} where id = #{id}
    </update>
        
    @Test
    public void updateUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.updateUser(new User(4,"mmmmmm","123456789"));
        //需要提交事务
        sqlSession.commit();
        sqlSession.close();
    }
    
  • 删**(提交事务)**

    //根据Id删除用户
    void deleteUserById(int id);
    
    <delete id="deleteUserById" parameterType="int">
        delete from `user` where id = #{id}
    </delete>
        
    @Test
    public void deleteUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.deleteUserById(4);
        //需要提交事务
        sqlSession.commit();
        sqlSession.close();
    }
    

5、万能的Map

当参数过多的时候,我们可以考虑使用Map介入

//将参数设为Map类型
User getUserByMap(Map<String,Object> map);
<!--参数类型用map,下方传入的值是map的key,可以自定义参数名-->
<select id="getUserByMap" resultType="com.lx.pojo.User" parameterType="map">
  select * from `user` where id = #{userId} and name = #{userName}
</select>
@Test
public void getUserByMap(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Map<String, Object> map = new HashMap<>();
        map.put("userId",2);
        map.put("userName","mybatis2号");

        User user = mapper.getUserByMap(map);
        System.out.println(user);
        sqlSession.close();
        }

6、模糊查询

正常sql:

select * from `user` where `name` like '%号%'

Mybati中使用:

1、调用时拼接 %value%
List<User> getUserByLikeName(String name);

<select id="getUserByLikeName" resultType="com.lx.pojo.User">
        select * from `user` where `name` like #{name}
</select>

@Test
public void getUserByLikeName(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> lists = mapper.getUserByLikeName("%号%");
        for (User user : lists) {
        System.out.println(user);
        }

        sqlSession.close();
        }
2、使用mysql的concat函数拼接
<select id="getUserByLikeName" resultType="com.lx.pojo.User">
        select * from `user` where `name` like concat('%',#{name},'%')
</select>

@Test
public void getUserByLikeName(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> lists = mapper.getUserByLikeName("2");
        for (User user : lists) {
        System.out.println(user);
        }
        sqlSession.close();
        }
3、xml中手动拼接
<select id="getUserByLikeName" resultType="com.lx.pojo.User">
  select * from `user` where name like "%"#{name}"%"
</select>

7、配置文件中标签属性解析

7.1、各个标签含义

Mybatis核心配置文件:mybatis-config.xml

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:

  • configuration(配置)
    • properties(属性)
    • settings(设置)
    • typeAliases(类型别名)
    • typeHandlers(类型处理器)
    • objectFactory(对象工厂)
    • plugins(插件)
    • environments(环境配置)
      • environment(环境变量)
        • transactionManager(事务管理器)
        • dataSource(数据源)
    • databaseIdProvider(数据库厂商标识)
    • mappers(映射器)

7.2、环境配置(environments)

MyBatis 可以配置成适应多种环境

default配置默认使用的数据源

不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

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

  <environment id="test">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
      <property name="driver" value=""/>
      <property name="url" value=""/>
      <property name="username" value=""/>
      <property name="password" value=""/>
    </dataSource>
  </environment>
</environments>

注意一些关键点:

  • 默认使用的环境 ID(比如:default=“development”)。
  • 每个 environment 元素定义的环境 ID(比如:id=“development”)。
  • 事务管理器的配置(比如:type=“JDBC”)。
  • 数据源的配置(比如:type=“POOLED”)。

默认环境和环境 ID 顾名思义。 环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。


事务管理器(transactionManager)

在 MyBatis 中有两种类型的事务管理器(也就是 type=“[JDBC|MANAGED]”):默认是JDBC

  • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。

  • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。

数据源(dataSource)

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

  • 大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。

有三种内建的数据源类型(也就是 type=“[UNPOOLED|POOLED|JNDI]”):默认是POOLED

  • UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。

  • POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。

  • JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。

总结:事务管理器默认是JDBC,数据源默认是POOLED

7.3、属性(properties)

这些属性可以在外部进行配置,并可以进行动态替换。

  1. resource目录下新建db.properties文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=root
  1. mybatis-config.xml核心配置文件
<configuration>
  <!--将配置文件加载进来,供下边可${}调用获取配置文件的值-->
  <properties resource="db.properties"></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>
</configuration>

也可以这样写:db.properties文件只写两个属性,剩下的属性在核心配置文件中配置

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true
<configuration>
  <properties resource="db.properties">
    <property name="name" value="root"/>
    <property name="pwd" value="root"/>
  </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="${name}"/>
        <property name="password" value="${pwd}"/>
      </dataSource>
    </environment>
  </mappers>
</configuration>

总结:

  • 可以直接引入外部文件如:db.properties
  • 可以直接在核心配置文件中配置属性
  • 如果db.properties和mybatis-config.xml也配置了相同的属性值,会优先读取db.properties中的配置

7.4、类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。

三种方法定义别名:

1:核心文件中通过进行配置

<typeAliases>
  <typeAlias type="com.lx.pojo.User" alias="bieming1"></typeAlias>
</typeAliases>
<select id="getAllUsers" resultType="bieming1">
  select * from `user`
</select>

2:核心文件中通过进行配置

用package引用实体类包名,会使用实体类的首字母小写类名来作为它的别名

<typeAliases>
  <package name="com.lx.pojo"/>
</typeAliases>
<select id="getAllUsers" resultType="user">
  select * from `user`
</select>

3:实体类使用注解自定义别名

@Alias("hahaha")
public class User implements Serializable {}
<select id="getAllUsers" resultType="hahaha">
  select * from `user`
</select>

总结:

  • 实体类多可以使用第一种方法
  • 实体类较少可以使用第二种方法
  • 第一种和第三种可以自定义别名名称

7.5、设置(settings)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。详情看文档链接:https://mybatis.net.cn/configuration.html#settings

7.6、映射器(mappers)

既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。

Mapper写完需要做映射,否则会抛异常org.apache.ibatis.binding.BindingException

方式1:resource寻找xml文件路径

<mappers>
  <!--使用相对于类路径的资源引用-->
  <mapper resource="com/lx/daoimpl/UserMapper2.xml"/>
</mappers>

方式2:class引入

<mappers>
  <!--使用映射器接口实现类的完全限定类名-->
  <mapper class="com.lx.dao.UserMapper2"/>
</mappers>

class引入注意点:接口类和实现类xml文件必须命名相同,而且要在同一包下

方式3:引入包路径

<mappers>
  <package name="com.lx.dao"/>
</mappers>

包路径引入注意点和class引入一样:接口类和实现类xml文件必须命名相同,而且要在同一包下

总结:推荐使用resource寻找xml文件路径来做映射,约束小

8、作用域和生命周期

不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题

SqlSessionFactoryBuilder–创造–>SqlSessionFactory–获取–>SqlSession

SqlSessionFactoryBuilder:

  • 这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。
  • SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
  • 可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory:

  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
  • 使用 SqlSessionFactory 的最佳实践是在运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。
  • 因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession:

  • 每个线程都应该有它自己的 SqlSession 实例。
  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
  • 代码最后要执行sqlSession.close();关闭资源是很重要的。

9、结果集映射 (resultMap)

resultMap 元素是 MyBatis 中最重要最强大的元素。

ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

发现问题:之前的实体类POJO中字段都是与数据库一致,不用我们做映射,mybatis会自动映射,但是当两边字段不一致时,字段映射不上,无法赋值如何处理

数据库字段:
id
name
pwd
实体类字段:
private int userid;
private String username;
private String password;
  • 方式1:xml中用as别名

    <select id="getUserById" resultType="user">
        select id as userid,name as username,pwd as password from `user` where id = #{id}
    </select>
    
  • 方式2:使用resultMap结果集映射

    <resultMap id="ysUser" type="user">
        <!--column:列,是指数据库中字段名, property:属性,指的是实体类中的字段名-->
        <result column="id" property="userid"></result>
        <result column="name" property="username"></result>
        <result column="pwd" property="password"></result>
    </resultMap>
    
    <!--返回类型resultType改为结果集映射resultMap-->
    <select id="getUserById" resultMap="ysUser">
        select * from `user` where id = #{id}
    </select>
    
  • 如果这个世界总是这么简单就好了。

10、日志

mybatis-config.xml核心配置文件中使用setting标签设置

logImpl:指定 MyBatis 所用日志的具体实现,未指定时将自动查找。

  • SLF4J

  • LOG4J(掌握)

  • LOG4J2

  • JDK_LOGGING

  • COMMONS_LOGGING

  • STDOUT_LOGGING(掌握,标准日志输出)

  • NO_LOGGING

10.1、日志工厂(STDOUT_LOGGING)

使用STDOUT_LOGGING配置日志

<!--标准日志工厂输出:STDOUT_LOGGING     standard out put-->
<settings>
  <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

配置后,运行的sql会有日志输出,效果如下:

Created connection 111374580.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6a370f4]
==>  Preparing: select * from `user` where id = ?
==> Parameters: 1(Integer)
<==    Columns: id, name, pwd
<==        Row: 1, mybatis1号, asdf
<==      Total: 1
User{userid=1, username='mybatis1号', password='asdf'}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6a370f4]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@6a370f4]
Returned connection 111374580 to pool.
10.2、LOG4J

什么是LOG4J?

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件。
  • 我们也可以控制每一条日志的输出格式
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
  • 这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
  1. 导入依赖(此版本有漏洞,后面再用log4j2)

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  2. resource目录下新建log4j.properties文件

    # 设置日志级别和输出目的地
    log4j.rootLogger=DEBUG, stdout, file
    
    # 控制台输出的相关设置
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.Target=System.out
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
    
    # 文件输出的相关设置
    log4j.appender.file=org.apache.log4j.RollingFileAppender
    log4j.appender.file.File=logs/app.log
    log4j.appender.file.MaxFileSize=5MB
    log4j.appender.file.MaxBackupIndex=10
    log4j.appender.file.layout=org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
    
  3. mybatis-config.xml文件配置

    <settings>
      	<!--配置log4j日志输出-->
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    
  4. 执行test类中调用mapper方法输出

    2024-01-26 13:46:52 DEBUG PooledDataSource:434 - Created connection 428996455.
    2024-01-26 13:46:52 DEBUG JdbcTransaction:101 - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1991f767]
    2024-01-26 13:46:52 DEBUG getUserById:137 - ==>  Preparing: select * from `user` where id = ?
    2024-01-26 13:46:52 DEBUG getUserById:137 - ==> Parameters: 1(Integer)
    2024-01-26 13:46:52 DEBUG getUserById:137 - <==      Total: 1
    User{userid=1, username='mybatis1号', password='asdf'}
    2024-01-26 13:46:52 DEBUG JdbcTransaction:123 - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1991f767]
    2024-01-26 13:46:52 DEBUG JdbcTransaction:91 - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1991f767]
    2024-01-26 13:46:52 DEBUG PooledDataSource:391 - Returned connection 428996455 to pool.
    
  5. 运用在代码中

    public class UserMapperTest {
    
        private static Logger logger = Logger.getLogger(UserMapperTest.class);
    
        @Test
        public void testLog4j(){
            logger.info("info:--进入了testLog4j");
            logger.debug("debug:--进入了testLog4j");
            logger.error("error:--进入了testLog4j");
        }
    }
    
  6. 输出

    2024-01-26 13:50:08 INFO  UserMapperTest:27 - info:--进入了testLog4j
    2024-01-26 13:50:08 DEBUG UserMapperTest:28 - debug:--进入了testLog4j
    2024-01-26 13:50:08 ERROR UserMapperTest:29 - error:--进入了testLog4j
    

11、分页

为什么要使用分页?

处理数据量过多查询

原sql查询

select * from user limit startIndex,pageSize;  startIndex:起始索引  pageSize:每页条数
select * from user limit 3; 只传一个数字代表查询0,3
11.1、使用sql中limit分页
  • UserMapper接口

    public interface UserMapper4 {
        //分页获取用户
        List<User> getUsersByLimit(Map<String,Integer> map);
    }
    
  • UserMapper.xml

    <mapper namespace="com.lx.dao.UserMapper4">
        <resultMap id="yingsheUser" type="user">
            <result column="pwd" property="password"></result>
        </resultMap>
        <select id="getUsersByLimit" resultMap="yingsheUser" parameterType="map">
            select * from `user` limit #{startIndex},#{pageSize}
        </select>
    </mapper>
    
  • Test测试

    public class UserMapperTest {
        private static Logger logger = Logger.getLogger(UserMapperTest.class);
        @Test
        public void testLimit(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper4 mapper = sqlSession.getMapper(UserMapper4.class);
            HashMap<String, Integer> map = new HashMap<>();
            map.put("startIndex",1);
            map.put("pageSize",2);
            List<User> userList = mapper.getUsersByLimit(map);
            for (User user : userList) {
                logger.info(user);
            }
            sqlSession.close();
        }
    }
    
  • 输出

    2024-01-26 15:15:00 DEBUG getUsersByLimit:137 - ==>  Preparing: select * from `user` limit ?,?
    2024-01-26 15:15:00 DEBUG getUsersByLimit:137 - ==> Parameters: 1(Integer), 2(Integer)
    2024-01-26 15:15:00 DEBUG getUsersByLimit:137 - <==      Total: 2
    2024-01-26 15:15:00 INFO  UserMapperTest:22 - User{id=2, name='mybatis2号', password='asdfasdf'}
    2024-01-26 15:15:00 INFO  UserMapperTest:22 - User{id=3, name='mybatis3号', password='123456'}
    

11.2、使用RowBounds的java代码分页

  • UserMapper接口

    List<User> getUsersByRowBounds();
    
  • UserMapper.xml

    <select id="getUsersByRowBounds" resultType="user" resultMap="yingsheUser">
        select * from `user`
    </select>
    
  • Test

    @Test
    public void testRowBounds(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        List<User> userList = sqlSession.selectList("com.lx.dao.UserMapper4.getUsersByRowBounds", null, new RowBounds(1, 2));
        for (User user : userList) {
            logger.info(user);
        }
        sqlSession.close();
    }
    
  • 输出

    2024-01-26 15:27:52 DEBUG JdbcTransaction:101 - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@44e3760b]
    2024-01-26 15:27:52 DEBUG getUsersByRowBounds:137 - ==>  Preparing: select * from `user`
    2024-01-26 15:27:52 DEBUG getUsersByRowBounds:137 - ==> Parameters: 
    2024-01-26 15:27:52 INFO  UserMapperTest:33 - User{id=2, name='mybatis2号', password='asdfasdf'}
    2024-01-26 15:27:52 INFO  UserMapperTest:33 - User{id=3, name='mybatis3号', password='123456'}
    

12、使用注解开发

增删改查

public interface CommentUserMapper {

  @Select("select * from `user`")
  List<User> getAllUsers();

  //sql语句中的参数要和@Param中的值一致
  @Select("select * from `user` where id = #{userId}")
  User getUserById(@Param("userId") int id);

  //当参数为实体类时,sql中赋值参数应当与实体类中的字段一致
  @Insert("insert into `user` 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 = #{id}")
  int deleteUser(int id);

}

【这里注意要设置自动提交,删改查数据才会成功执行】

public class MybatisUtils {
  private static SqlSessionFactory sqlSessionFactory;
  static {
    try {
      //第一步:获取sqlSessionFactory对象
      String resource = "mybatis-config.xml";
      InputStream inputStream = Resources.getResourceAsStream(resource);
      sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }
  //获取sqlSession,可以操作数据库的对象
  public static SqlSession getSqlSession(){
    //设置为true,自动提交事务
    return sqlSessionFactory.openSession(true);
  }
}

注意点:

  • 简单sql可以通过注解进行开发
  • 当多个参数为基本数据类型或者String类型时,需要使用@Param注解声明参数
  • 参数只有一个可以省略不写,但是遵循规范,最好都加上
  • 引用类型不用带参数(实体类)
  • 注解中的sql语句中的参数名#{val}引用的是@Param("val")中的值
  • 当参数为实体类时,sql中赋值参数名#{val}应当与实体类中的字段一致
  • 使用#{},不使用${},预防sql注入

13、Lombok

什么是Lombok?

Lombok项目是一个java库,它可以自动插入到编辑器和构建工具中,增强java的性能。不需要再写getter、setter或equals方法,只要有一个注解,就有一个功能齐全的构建器、自动记录变量等等。

如何使用Lombok?
  1. idea导入lombok插件

  2. 项目导入lombok依赖

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.26</version>
    </dependency>
    
  3. 常用注解

  • Data:整合了Getter、Setter、ToString、EqualsAndHashCode、RequiredArgsConstructor注解。
  • Getter:快速构建Getter方法。
  • Setter:快速构建Setter方法。
  • ToString:快速将当前对象转换成字符串类型,便于log。
  • EqualsAndHashCode:快速进行相等判断。
  • NonNull:判断变量(对象)是否为空

14、复杂查询(多对一)

多个学生对应一个老师

  • 数据库创建

    -- 四个学生对应一个老师
    -- 也可以是一个老师有多个学生
    
    create table student(
    	id int(10) primary key,
    	name varchar(20),
    	tid int(10)
    );
    
    insert into student values
    (1,"学生1",1),
    (2,"学生2",1),
    (3,"学生3",1),
    (4,"学生4",1);
    
    create table teacher(
    	id int(10) primary key,
    	name varchar(20)
    );
    
    insert into teacher values
    (1,"刘老师");
    
  • 实体类创建

    @Data
    public class Teacher {
        private int id;
        private String name;
    }
    
    @Data
    public class Student {
        private int id;
        private String name;
        private Teacher teacher;
    }
    
  • Mapper接口

    public interface StudentMapper {
        List<Student> getAllStudents();
    }
    
  • Mapper.xml文件

    1. 通过查询嵌套映射,先查询结果,后对结果再嵌套查询,再映射(子查询)
    <mapper namespace="com.lx.dao.StudentMapper">
        <select id="getAllStudents" resultMap="StudentTeacher">
            select * from student
        </select>
        <resultMap id="StudentTeacher" type="com.lx.pojo.Student">
            <result property="id" column="id"></result>
            <result property="name" column="name"></result>
            <!--复杂的属性,需要单独处理,对象时用:association,集合时用:collection-->
            <association property="teacher" column="tid" javaType="com.lx.pojo.Teacher" select="getAllTeacher"></association>
        </resultMap>
        <select id="getAllTeacher" resultType="com.lx.pojo.Teacher">
            select * from teacher where id = #{id}
        </select>
    </mapper>
    
    1. 按照结果嵌套映射,先查询结果,后对字段进行映射(联表查询)
    <select id="getAllStudents2" resultMap="StudentTeacher2">
        select s.id sid,s.name sname,t.id tid,t.name tname from student s,teacher t where s.tid = t.id
    </select>
    <resultMap id="StudentTeacher2" type="com.lx.pojo.Student">
        <result property="id" column="sid"></result>
        <result property="name" column="sname"></result>
        <association property="teacher" javaType="com.lx.pojo.Teacher">
            <result property="id" column="tid"></result>
            <result property="name" column="tname"></result>
        </association>
    </resultMap>
    

15、复杂查询(一对多)

一个老师对应多个学生

  • 实体类

    @Data
    public class Student {
        private int id;
        private String name;
        private int tid;
    }
    
    @Data
    public class Teacher {
        private int id;
        private String name;
        private List<Student> students;
    }
    
  • Mapper接口

    public interface TeacherMapper {
        List<Teacher> getAllTeachers();
    }
    
  • Mapper.xml文件

    1. 子查询嵌套查询
    <mapper namespace="com.lx.otm.dao.TeacherMapper">
    
        <select id="getAllTeachers" resultMap="TeacherStudent">
            select * from teacher
        </select>
        <resultMap id="TeacherStudent" type="com.lx.otm.pojo.Teacher">
            <result property="id" column="id"></result>
            <result property="name" column="name"></result>
            <!--
                javaType:List对应的类型是ArrayList
                ofType:对应的实体类
            -->
            <collection property="students" column="id" javaType="ArrayList" ofType="com.lx.otm.pojo.Student" select="getAllStudent"></collection>
        </resultMap>
        <select id="getAllStudent" resultType="com.lx.otm.pojo.Student">
            select * from student where tid = #{id}
        </select>
    </mapper>
    
    1. 联表查询字段映射
    Teacher getTeacherById(@Param("tid") int tid);
    
    <select id="getTeacherById" resultMap="TeacherStudent2">
        select t.id tid,t.name tname,s.id sid,s.name sname, s.tid stid from student s,teacher t where s.tid = t.id and t.id = #{tid}
    </select>
    <resultMap id="TeacherStudent2" type="com.lx.otm.pojo.Teacher">
        <result property="id" column="tid"></result>
        <result property="name" column="tname"></result>
        <collection property="students" javaType="ArrayList" ofType="com.lx.otm.pojo.Student">
            <result property="id" column="sid"></result>
            <result property="name" column="sname"></result>
            <result property="tid" column="stid"></result>
        </collection>
    </resultMap>
    

16、复杂查询小结

  • association 关联对象 【多对一使用】
  • collection 关联集合 【一对多使用】
  • javaType & ofType
    • javaType : 用来指定pojo实体类中属性的类型
    • ofType :pojo实体类中属性是List或者集合类型,ofType用来指定泛型中约束的类型
  • 注意点:
    • 保证sql的可读性,尽量通俗易懂,易于维护
    • 注意一对多和多对一中,属性和字段名的问题
    • 如果不好排查错误,可以使用日志,打印出sql排查,建议使用Log4j

17、动态sql

什么是动态sql?

  • 动态SQL是一种在运行时构建和执行SQL语句的技术。

  • 通常情况下,静态SQL是预先定义好的SQL语句,而动态SQL允许根据程序运行时的条件和需求来动态地生成SQL语句。

  • 动态SQL的主要优点是灵活性和适应性。它可以根据不同的情境和需求生成不同的SQL语句,从而提供更高的灵活性和可重用性。

17.1、环境搭建

  • 创建表

    CREATE TABLE `blog` (
      `id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '主键',
      `title` varchar(255) DEFAULT NULL COMMENT '标题',
      `author` varchar(255) DEFAULT NULL COMMENT '作者',
      `create_time` datetime DEFAULT NULL COMMENT '创建时间',
      `views` int DEFAULT NULL COMMENT '浏览量',
      PRIMARY KEY (`id`)
    )
    
  • 创建实体类

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Blog {
        private String id;
        private String title;
        private String author;
        private Date createTime;
        private int views;
    }
    
  • 编写生成ID的工具类

    public class GenerateIdUtils {
        public static String getId() {
            return UUID.randomUUID().toString().replaceAll("-", "");
        }
    }
    
  • mybatis-config.xml核心配置文件中加入mapUnderscoreToCamelCase为true,开启下划线转驼峰命名

    <settings>
        <!--开启下划线转驼峰-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    
  • 插入数据以及测试是否成功

    public interface BlogMapper {
        int addBlog(Blog blog);
    }
    
    <?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.lx.dao.BlogMapper">
    
        <insert id="addBlog">
            insert into blog values
            (#{id},#{title},#{author},#{createTime},#{views})
        </insert>
    </mapper>
    
    @Test
    public void testAddBlog(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        mapper.addBlog(new Blog(GenerateIdUtils.getId(),"学习Mybatis很快乐","lx",new Date(),1000));
        mapper.addBlog(new Blog(GenerateIdUtils.getId(),"学习Spring很快乐","lx",new Date(),9999));
        mapper.addBlog(new Blog(GenerateIdUtils.getId(),"学习SpringBoot很快乐","lx",new Date(),520));
        mapper.addBlog(new Blog(GenerateIdUtils.getId(),"学习Mybatis-plus很快乐","lx",new Date(),10));
        sqlSession.close();
    }
    

    注意开启自动提交才会提交数据

17.2、动态sql之if标签

List<Blog> queryBlogByIf(Map map);
<select id="queryBlogByIf" resultType="com.lx.pojo.Blog" parameterType="map">
  select * from blog where 1=1
  <if test="title != '' and title != null">
    and title = #{title}
  </if>
  <if test="author != '' and author != null">
    and author = #{author}
  </if>
</select>

17.3、动态sql之where标签

上面的where 1=1写法也可以实现,但是这种写法不够好,可以使用where标签对其优化

<select id="queryBlogByIf" resultType="com.lx.pojo.Blog" parameterType="map">
  select * from blog
  <where>
    <if test="title != '' and title != null">
      and title = #{title}
    </if>
    <if test="author != '' and author != null">
      and author = #{author}
    </if>
  </where>
</select>

where元素只会在子元素有条件成立的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where元素也会很智能的将它们去除,如果子元素没有返回,那么mybatis也会自动将where元素去除。

17.4、动态sql之choose标签

<select id="queryBlogByChoose" resultType="com.lx.pojo.Blog" parameterType="map">
  select * from blog
  <where>
    <choose>
      <when test="title != '' and title != null">
        and title = #{title}
      </when>
      <when test="author != '' and author != null">
        and author = #{author}
      </when>
      <otherwise>
        and views = #{views}
      </otherwise>
    </choose>
  </where>
</select>

choose标签类似于java中的switch。当choose下第一个条件满足,则只会拼接第一个条件查询。第一个不满足,就往下接着走,直到有一个条件满足,则直接拼接查询条件,别的自动过滤掉。如果所有条件都不满足,则会拼接otherwise中的条件

17.5、动态sql之set标签

<update id="updateBlog">
  update blog
  <set>
    <if test="title != '' and title != null">
      title = #{title},
    </if>
    <if test="author != '' and author != null">
      author = #{author},
    </if>
  </set>
  where id = #{id}
</update>

set 元素可以用于动态包含需要更新的列,条件不满足则忽略其它不更新的列。set元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号

17.6、动态sql之sql标签

<!--有时候一些代码可以公用,可以通过sql标签提取出来-->
<sql id="if-title-author">
    <if test="title != '' and title != null">
        and title = #{title}
    </if>
    <if test="author != '' and author != null">
        and author = #{author}
    </if>
</sql>
<!--使用的时候可以使用include标签,通过refid引用对应的sql标签-->
<select id="queryBlogByIf" resultType="com.lx.pojo.Blog" parameterType="map">
    select * from blog
    <where>
        <include refid="if-title-author"></include>
    </where>
</select>

sql标签可以将共用的部分条件等提取出来,别的地方通过include标签中的refid指定对应的sqlid即可

17.7、动态sql之foreach标签

  • foreach中or条件查询
<!--
    collection:集合的名称
    item:集合中每一个元素
    open:以什么什么开始
    separator:以什么分隔
    close:以什么什么结束
-->
<select id="queryBlogByForeachOr" resultType="com.lx.pojo.Blog" parameterType="java.util.Map">
    select * from blog
    <where>
        <foreach collection="ids" item="i" open="and (" separator="or" close=")">
            id = #{i}
        </foreach>
    </where>
</select>
  • foreach中in条件查询
<select id="queryBlogByForeachIn" resultType="com.lx.pojo.Blog" parameterType="java.util.Map">
    select * from blog
    <where>
        id in
        <foreach collection="ids" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </where>
</select>
@Test
public void testQueryBlogByForeach(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    List<String> ids = new ArrayList<>();
    ids.add("1");
    ids.add("2");
    ids.add("3");
    Map<Object, Object> map = new HashMap<>();
    map.put("ids",ids);

    List<Blog> blogs = mapper.queryBlogByForeach(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
}

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

动态sql,就是根据不同的条件生成不同的sql语句

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

18、缓存

18.1、简介

  1. 什么是缓存【Cache】?
    • 存在内存中的临时数据
    • 将用户经常查询的数据放在缓存(内存)中,用户查询数据就不用从磁盘上查询,将从缓存中查数据,从而提高查询效率,解决了高并发系统的性能问题
  2. 为什么使用缓存?
    • 减少和数据库的交互次数,减少系统开销,提高系统的效率
  3. 什么样的数据能使用缓存?
    • 经常查询并且不经常改变的数据

18.2、Mybatis缓存

  • MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 缓存可以极大的提升查询效率。
  • Mybatis定义了两级缓存:一级缓存二级缓存
    • 默认开启的一级缓存。(SqlSession级别缓存,也称为本地缓存)
    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
    • 为了提高可扩展性,Mybatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存

18.3、一级缓存

一级缓存也叫本地缓存:

  • 与数据库同一次会话期间查询到的数据会放到本地缓存中
  • 期间如果需要查询相同的数据,直接从缓存中拿

测试:开启日志,查看查找相同的用户,sql执行几次

@Test
public void testQueryUsers(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserCacheMapper mapper = sqlSession.getMapper(UserCacheMapper.class);
    User user1 = mapper.queryUser(1);
    System.out.println(user1);
    System.out.println("==================================");

    User user2 = mapper.queryUser(1);
    System.out.println(user1);
    System.out.println("==================================");
    //只会执行一个sql查询,因为查询的相同用户,第二个用户直接从缓存中取,输出true
    System.out.println(user1==user2);

    sqlSession.close();

}
@Test
public void testQueryUsers(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserCacheMapper mapper = sqlSession.getMapper(UserCacheMapper.class);
    User user1 = mapper.queryUser(1);
    System.out.println(user1);
    System.out.println("==================================");
    //清空缓存,此时再查相同的用户,会走两次查询
    sqlSession.clearCache();
    //中间这里进行任何增删改,也都会触发清除缓存
    
    User user2 = mapper.queryUser(1);
    System.out.println(user1);
    System.out.println("==================================");
    //会执行两次sql,因为中间清除了缓存,输出false
    System.out.println(user1==user2);

    sqlSession.close();

}

小结:

  • 一级缓存是默认开启的,只在一次SqlSession中有效,也就是拿到数据库连接到关闭连接中间的区间段

  • 所有 select 语句的结果将会被缓存。

  • 所有 insert、update 和 delete 语句会刷新缓存。

  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。

  • 可以通过sqlSession.clearCache();清除缓存

18.4、二级缓存

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

  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存
  • 工作机制
    1. 一个会话查询一条数据,这个数据就会被放到当前会话的一级缓存
    2. 如果当前会话关闭了,那么一级缓存也就会消失,但我们想要的是,即使会话关闭了,一级缓存的数据会被保存到二级缓存
    3. 新的会话查询信息,就可以从二级缓存中读取获取内容
    4. 不同的mapper查出来的数据会被放到不同的缓存(map)中

开启二级缓存方法:

  1. mybatis-config.xml核心配置文件中settings标签加入此配置
<!--虽然缓存是默认开启的,但是也可以显示的声明一下,让他人知道缓存是开启的-->
<setting name="cacheEnabled" value="true"/>
  1. mapper.xml文件中加入cache标签
<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>
  1. 测试
@Test
public void testQueryUsers(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserCacheMapper mapper = sqlSession.getMapper(UserCacheMapper.class);
    //此时查到的数据会放到以及缓存中
    User user1 = mapper.queryUser(1);
    System.out.println(user1);
    //关闭连接,此时一级缓存消失,会将数据保存到二级缓存
    //此时有增删改操作时,一级二级缓存都会消失
    sqlSession.close();


    SqlSession sqlSession2 = MybatisUtils.getSqlSession();
    UserCacheMapper mapper2 = sqlSession2.getMapper(UserCacheMapper.class);
    User user2 = mapper2.queryUser(1);
    System.out.println(user1);
    //会执行一次sql,因为是从同一个mapper.xml文件中读取的,数据将从二级缓存中读取,结果为true
    System.out.println(user1==user2);

    sqlSession2.close();
}

小结:

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

18.5、缓存原理

  1. 先会访问二级缓存
  2. 再访问一级缓存
  3. 访问数据库
  4. 将数据临时放到一级缓存
    • 如果下面有增删改操作时,当前对应的namespace的mapper将会重置缓存
    • 如果没有增删改操作,SqlSession会话结束,数据将会存到二级缓存
  5. 再访问接着从二级缓存中取,依次循环

18.6、调优

缓存会将select的数据存起来,但是一个接口如果频繁被调用,放缓存会有问题,不想缓存,可以加上useCache=false

<select id="queryUser" resultType="com.lx.pojo.User" useCache="false">
    select * from `user` where id = #{id}
</select>

增删改操作会刷新缓存,如果有业务场景需要,不刷新缓存,也可以使用这个属性flushCache=“false”

<insert id="addUser" flushCache="false">
    insert into `user` values (#{id},#{name},#{pwd})
</insert>

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值