MyBatis 知识宝典

目录

1、简介

1.1、什么是MyBatis?

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。

MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。

MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了[google code](https://baike.baidu.com/item/google code/2346604),并且改名为MyBatis 。2013年11月迁移到Github

iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)

当前,最新版本是MyBatis 3.5.7 ,其发布时间是2021年4月21日

2、如何使用?

中文文档:https://mybatis.org/mybatis-3/zh/index.html

GitHub:https://github.com/mybatis/mybatis-3/releases

MAVEN中添加如下依赖:

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>

详细简介及配置见官方文档

2、第一个MyBatis

2.1、创建数据库

用命令行创建数据库

CREATE DATABASE `mybatis`;

USE `mybatis`;

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

INSERT INTO `user`(`id`,`name`,`ps`)
VALUES
(1,'aaa','13451314'),
(2,'bbb','4314456456'),
(3,'中文','123451354')

2.2、配置pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>mybatis-study</artifactId>
        <groupId>com.ajun</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mybatis-01</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--测试包-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <!--mysql驱动包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
        </dependency>
        <!--mybatis包-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>provided</scope>
        </dependency>	
    </dependencies>

</project>

2.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>

    <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/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

</configuration>

2.4、建立mybatis工具类

因为获取SqlSession的操作是固定不变的,所以用工具类的形式实现,方便后面调用

//sqlSessionFactory --> sqlSession
public class MyBatisUtils {
    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            //使用mybatis第一步:获取sqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //既然有了 SqlSessionFactory,就可以从中获得 SqlSession 的实例。
    // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
    // 可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
    //openSession(boolean autoCommit)可以添加参数,是否自动提交事务。默认为false,不自动提交
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

2.5、实体类 User.java

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

2.6、Dao接口类 UserDao.java

public interface UserDao {
    public List<User> getAll();
}

2.7、接口配置 UserDao.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">
<mapper namespace="com.ajun.dao.UserDao">
    <select id="getAll" resultType="User">
        select * from mybatis.user;
    </select>
</mapper>

命名空间namespace使用全限定名

2.8、两种配置文件结构对比

mybatis-config.xml 与 mapper.xml结构对比

mapper.xml(即Dao映射配置文件)

结构相同,关键字不同

  • 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>

</configuration>
  • 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">
<mapper namespace="">

</mapper>

2.9、注册映射器UserDao.xml

在mybatis-config.xml中添加:

每一个dao.xml都需要注册

四种写法

<mappers>    
    <!-- 写法1
	<package name="com.ajun.dao"/>
	-->
    
    <!-- 写法2:不建议用
    <mapper url="file:///var/mappers/AuthorMapper.xml"/>
	-->
    
    <!-- 写法3
	<mapper class="com.ajun.dao.UserDao"/>
	-->
    
    <!-- 写法4 -->
    <mapper resource="com/ajun/dao/UserDao.xml"/>
</mappers>

2.10、配置Maven静态资源过滤

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <!--false:表示不启用过滤器-->
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

使用Maven构建项目的时候,会默认过滤掉静态资源,所以,需要手动来配置

静态资源 : 包含HTMl,图片,CSS,JS,xml等不需要与数据库交互的一类文件

动态资源 : 需要与数据库交互,可以根据需要显示不同的数据,不需要修改页面

2.11、测试

public class MyTest {
    @Test
    public void test(){

		SqlSession sqlSession = MyBatisUtils.getSqlSession();

        try{
            UserDao userDao = sqlSession.getMapper(UserDao.class);
            List<User> users = userDao.getAll();
            for (User user : users) {
                System.out.println(user);
            }
        }finally {
            //关闭sqlSession
            sqlSession.close();
        }
    }
}

把关闭操作放在finally中

3、增删改查CRUD

  • 增加(Create)
  • 检索(Retrieve)
  • 更新(Update)
  • 删除(Delete)

3.1、Mapper.xml中命名空间

Mapper.xml中命名空间和Dao的全限定名一致

3.2、添加、修改、删除

在SqlSessionFactory的openSession()中,默认不自动提交事务。所以增删改操作要提交事务,才能操作成功。sqlSession.commit()

SqlSessionFactory的openSession()方法可以添加参数:boolean autoCommit

是否自动提交事务,默认为false,不自动提交

实体类:User.java

//添加用户
int addUser(User user);

//修改用户
int updateUser(User user);

//删除用户
int deleteUser(int id);

Mapper.xml配置文件

<insert id="addUser" parameterType="User">
    insert into mybatis.user (id, name, ps) values (#{id},#{name},#{ps});
</insert>

<update id="updateUser" parameterType="User">
    update mybatis.user set name = #{name},ps = #{ps} where id = #{id};
</update>

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

测试类

@Test
public void addUser(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    try{
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        userMapper.addUser(new User(5,"测试","464655646"));
        //要提交事务
        sqlSession.commit();
    }finally {
        sqlSession.close();
    }
}

@Test
public void updateUser(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    try{
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        userMapper.updateUser(new User(5,"成功","4546545464444464"));
        //要提交事务
        sqlSession.commit();
    }finally {
        sqlSession.close();
    }
}

@Test
public void deleteUser(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    try{
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.deleteUser(5);
        //要提交事务
        sqlSession.commit();
    }finally {
        sqlSession.close();
    }
}

3.3、Dao中用Map参数

假如实体类属性过多,用map,传递map的key。万能方法

Dao接口类

User getUserByMap(Map<String,Object> map);

Mapper配置

<select id="getUserByMap" parameterType="map" resultType="User">
    select * from mybatis.user where id = #{key};
</select>

测试类

@Test
public void getUserByMap(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    try{
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("key",3);

        User user = userMapper.getUserByMap(map);
        System.out.println(user);
    }finally {
        //关闭sqlSession
        sqlSession.close();
    }
}

3.4、模糊查询

like %value%

关键字两端加通配符%

通配符可以加在sql语句中

select * from mybatis.user where name like "%"#{value}"%"

也可以加在调用方法中

List<User> users = mapper.getUserLike("%文%");

Dao接口类

//模糊查询
List<User> getUserLike(String value);

Mapper配置

<select id="getUserLike" parameterType="String" resultType="User">
    select * from mybatis.user where name like #{value};
</select>

测试类

@Test
public void getUserLike(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    try{
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.getUserLike("%文%");
        for (User user : users) {
            System.out.println(user);
        }
    }finally {
        sqlSession.close();
    }
}


4、配置解析

4.1、核心配置文件

  • mybatis-config.xml

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

configuration(配置)

以上配置按从上到下的顺序,不能乱

4.2、环境配置(environments)

MyBatis 可以配置成适应多种环境 (例如,开发、测试和生产环境需要有不同的配置)

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

  • 每个数据库对应一个 SqlSessionFactory 实例

    为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可。可以接受环境配置的两个方法签名是:

    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);
    

environments 元素定义了如何配置环境

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <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>

注意一些关键点:

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

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

事务管理器(transactionManager)

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

提示 如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

数据源(dataSource)

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

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

  • UNPOOLED

    不用连接池

  • POOLED

    连接池

  • JNDI

    这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用

4.3、属性(properties)

这些属性可以在外部进行配置,并可以进行动态替换。既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。

外部配置文件:db.properties

driver = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT
username = root
password = 123456

由于mysql的驱动发生了更新,之前的链接方式需要改变,加入时区

之前:jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8

现在:jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone = GMT

引用:

<!--引用外部配置-->
<properties resource="db.properties"/>

<dataSource type="POOLED">
    <property name="driver" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
</dataSource>
<!--properties中可以加入别的属性,同时有的话,优先走外面properties-->
<properties resource="db.properties">
    <property name="username" value="root"></property>
    <property name="password" value="123456"></property>
</properties>

4.4、类型别名(typeAliases)

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

  • 特定类起别名
<typeAliases>
  <typeAlias alias="User" type="com.ajun.pojo.User"/>
</typeAliases>

当这样配置时,User 可以用在任何使用 com.ajun.pojo.User 的地方

  • 包下所有类起别名
<typeAliases>
  <package name="com.ajun.pojo"/>
</typeAliases>

每一个在包 com.ajun.pojo 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名,(大写User也可以,但不建议)。 比如 com.ajun.pojo.User 的别名为 user;若有注解,则别名为其注解值。

下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。

别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

4.5、设置(settings)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。

设置名 描述 有效值 默认值
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true | false false
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置

详细设置查看:https://mybatis.org/mybatis-3/zh/configuration.html#settings

4.6、其它配置

  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins(插件)

5、映射器(mappers)

就是告诉 MyBatis 去哪里找映射文件(SQL语句)。

不支持通配符。推荐用package,可以指定包下的所有配置文件。

resource和class需要一个一个地配置

四种配置方法:

1、resource

<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
</mappers>

2、url(不建议用)

<!-- 使用完全限定资源定位符(URL) -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
</mappers>

3、class

<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>

4、package(推荐)

<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

注意:用class 和 package时,接口名和配置文件名一致,且在同一个包下

6、作用域(Scope)和生命周期

  • SqlSessionFactoryBuilder

    作用就是创建SqlSessionFactory。创建完之后就可以丢弃。

  • SqlSessionFactory

    相当于数据源连接池,会伴随程序运行的全过程。可以生成多个SqlSession。应当设为单例模式,全局唯一。

  • SqlSession

    相当于特定的一次请求,一个连接。请求完之后就关闭。不是线程安全的,不可以共享。

7、结果映射resultMap

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

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

7.1、实体属性名与数据库列名不一致

解决办法:

1、指定别名

把字段名 ps 指定为 属性名 psd,采用 as

    <select id="getAllUser" resultType="user">
        select id,name,ps as psd  from mybatis.user;
    </select>

2、结果集映射resultMap

<!--调用结果集-->
<select id="getAllUser" resultMap="userResultMap">
    select * from mybatis.user;
</select>

<!--结果集映射-->
<resultMap id="userResultMap" type="user">
    <!--
	名字一致时可以省略
    <result property="id" column="id"/>
    <result property="name" column="name"/>
	-->
    
    <result property="psd" column="ps"/>
</resultMap>


8、日志

8.1、日志工厂

设置名 描述 有效值 默认值
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置
  • SLF4J
  • LOG4J [掌握]
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING [掌握] 控制台输出
  • NO_LOGGING

具体使用哪一个,在设置中设定

STDOUT_LOGGING 标准日志输出 (控制台输出)

mybatis-config中

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

注意格式:大小写敏感,不能有空格

8.2、Log4j

8.2.1、介绍

  • Log4j是Apache的一个开源项目,可以控制日志信息输送的目的地是控制台、文件、GUI组件

  • 可以控制每一条日志的输出格式;

  • 通过定义每一条日志信息的级别,能够更加细致地控制日志的生成过程。

  • 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

8.2.2、导入jar包

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

8.2.3、配置log4j.properties

### set log levels ###
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 = [%c]-%m%n

### 输出到日志文件 ###
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/ajun.log
log4j.appender.file.MaxFileSize=10mb 
log4j.appender.file.Threshold=DEBUG 
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

# 日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

8.2.4、mybatis中配置log4j

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

8.2.5、使用

  • 在要使用log4j的类中,导入包 org.apache.log4j.Logger

    import org.apache.log4j.Logger;
    
  • 获取日志对象,参数为当前类的class

    Logger logger = Logger.getLogger(MyTest.class);
    
  • 日志级别

    logger.info("info信息");
    logger.debug("debug信息");
    logger.error("error信息");
    

9、分页

9.1、limit分页【重点】

9.1.1、SQL

select * from 表名 limit startIndex , pageSize;

select * form 表名 limit pageSize; //startIndex默认为0

startIndex:起始位置

pageSize:一次查询的数量

startIndex省略时,默认从0开始

9.1.2、接口方法

//分页查询
List<User> getUserByPage(Map<String,Integer> map);

9.1.3、Mapper.xml配置

<resultMap id="userResultMap" type="user">
    <result property="psd" column="ps"/>
</resultMap>

<select id="getUserByPage" parameterType="map" resultMap="userResultMap">
    select * from mybatis.user limit #{startIndex},#{pageSize};
</select>

9.1.4、测试类

 @Test
public void getUserByPage(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    try{
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        
        Map<String,Integer> map = new HashMap<String,Integer>();
        map.put("startIndex",2);
        map.put("pageSize",2);
        
        List<User> users = mapper.getUserByPage(map);
        for (User user : users) {
            System.out.println(user);
        }
    }finally {
        sqlSession.close();
    }
}

9.2、RowBounds分页 (面向对象)

不建议使用

9.3、分页插件

Mybatis PageHelper

10、注解开发

不建议使用。没有xml配置sql形式功能强大。

10.1、接口类 UserMapper.java

public interface UserMapper {
    //查询全部
    @Select("select * from user")
    List<User> getAllUser();
}

10.2、Mybatis-config.xml配置

<mappers>
    <!--用class来配置-->
    <mapper class="com.ajun.mapper.UserMapper"/>
</mappers>

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

本质:反射机制

底层:动态代理!

10.3、自动提交事务

SqlSessionFactory的openSession()方法,默认不自动提交事务。可以接受参数:

//默认为false,不自动提交事务
openSession(boolean autoCommit);
    
//true时,就可以自动提交事务
openSession(true);    

实际开发中不建议自动提交。

10.4、@Param()注解

  • 基本类型的参数或String类型的参数,需要加上
  • 对象参数不需要加
  • 如果只有一个基本类型参数,可以省略,建议加上
  • SQL语句中的参数#{uid},和@Param()中的参数"uid"是对应的
int addUser(@Param("id") id,@Param("name") name,@Param("psd") ps);

int addUser(User user);//对象类型,不需要加

int deleteUser(int id);//只有一个基本类型参数,可以省略

//SQL语句中的参数#{uid},和@Param()中的参数"uid"是对应的
@Select("select * from user where id = #{uid}")
User getUserById(@Param("uid") id);

10.5、#{} 和 ${}

MyBatis中使用parameterType向SQL语句传参,parameterType后的类型可以是基本类型int,String,HashMap和java自定义类型。

在SQL中引用这些参数的时候,可以使用两种方式#{parameterName}或者${parameterName}。

  • #{}
        #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。

      例如:order by #{parameterName} //或取Map中的value#{Key}也是一样操作。

      假设传入参数是“Smith”

      会解析成:order by "Smith"

  • ${}
        $将传入的数据直接显示生成在sql中。

      例如:order by #{parameterName} //或取Map中的value#{Key}也是一样操作。

      假设传入参数是“Smith”

      会解析成:order by Smith

区别:

#方式能够很大程度防止sql注入,$方式无法防止Sql注入。
$方式一般用于传入数据库对象,例如传入表名。
从安全性上考虑,能使用#尽量使用#来传参,因为这样可以有效防止SQL注入的问题。

注意:

MyBatis排序时使用order by 动态参数时需要注意,用$而不是#
例如:ORDER BY ${columnName} //这里MyBatis不会修改或转义字符串,可实现动态传入排序。

建议:接受从用户输出的内容并提供给语句中不变的字符串,这样做是不安全的。这会导致潜在的SQL注入攻击,因此你不应该允许用户输入这些字段,或者通常自行转义并检查。
传入表名称用${}
时间比较,需要为#{},参数尽量用#{},${}容易导致需要有引号的参数失效

11、Mybatis详细执行流程

  1. Resource获取全局配置文件
  2. 实例化SqlsessionFactoryBuilder
  3. 解析配置文件流XMLConfigBuilder
  4. Configuration所有的配置信息
  5. SqlSessionFactory实例化
  6. transactional事务管理
  7. 创建executor执行器
  8. 创建SqlSession
  9. 实现CRUD
  10. 查看是否执行成功
  11. 提交事务
  12. 关闭

12、Lombok

12.1、在IDEA中安装lombok插件

12.2、导入jar包

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
    <scope>provided</scope><!--开发环境-->
</dependency>

12.3、注解命令

@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
@val
@var
experimental @var
@UtilityClass
Lombok config system

12.3、IDEA中查看类结构

在IDEA中 Alt+F7 可以实时查看类方法

13、多对一

  • 多个学生关联一个老师(多对一)
  • 一个老师教多个学生 集合(一对多)

13.1、建库

<!--老师表-->
CREATE TABLE `teacher` (
	`id` INT(10) NOT NULL PRIMARY KEY,
	`name` VARCHAR(30) DEFAULT NULL
)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, 'xiao1', 1);
INSERT INTO student (`id`, `name`, `tid`) VALUES (2, 'xiao2', 1);
INSERT INTO student (`id`, `name`, `tid`) VALUES (3, 'xiao3', 1);
INSERT INTO student (`id`, `name`, `tid`) VALUES (4, 'xiao4', 1);
INSERT INTO student (`id`, `name`, `tid`) VALUES (5, 'xiao5', 1);

13.2、环境搭建

1、实体类

  • Teacher.java
@Data
public class Teacher {
    private int id;
    private String name;
}
  • Student.java
@Data
public class Student {
    private int id;
    private String name;

    private Teacher teacher;
}

2、接口类

  • TeacherMapper.java
public interface TeacherMapper {
    @Select("select * from teacher where id = #{uid}")
    Teacher getTeacher(@Param("uid") int id);
}
  • StudentMapper.java
public interface StudentMapper {
}

3、接口配置xml

接口配置文件 TeacherMapper.xml 和 StudentMapper.xml 放入资源resources目录中。在资源目录中创建和接口类一样的目录结构。创建目录时用斜杠 / 分开,如com/ajun/mapper,或者一层一层地创建。如果用com.ajun.mapper创建是不会分层的。和src中的创建方式不一样。src中是包,resources中是文件目录。

把mapper.xml配置文件放入资源目录下后,maven中就不需要静态资源过滤了


  • TeacherMapper.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">
<mapper namespace="com.ajun.mapper.TeacherMapper">

</mapper>
  • StudentMapper.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">
<mapper namespace="com.ajun.mapper.StudentMapper">

</mapper>

注:mybatis-config.xml 与 mapper.xml结构模版相同,关键词不同。详见2.8

4、mybatis-config.xml 中添加mapper

就是告诉 MyBatis 去哪里找映射文件(SQL语句)。

不支持通配符。推荐用package,可以指定包下的所有配置文件。

resource和class需要一个一个地配置

四种配置方法。见5

5、工具类

和2.4中一样

6、测试

public class MyTest {
    @Test
    public void test(){
        SqlSession sqlSession = MyBatisUtils.getSession();
        try{
            TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
            Teacher teacher = mapper.getTeacher(1);
            System.out.println(teacher);
        }finally {
            sqlSession.close();
        }

    }
}

13.3、子查询:查询嵌套处理

  • 接口类
public interface StudentMapper {
    List<Student> getStudent();
	//方式二使用
    List<Student> getStudent2();
}
  • mapper.xml
<!--按照查询嵌套处理-->
<select id="getStudent" resultMap="studentTeacher">
    select * from mybatis.student;
</select>

<resultMap id="studentTeacher" 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 mybatis.teacher where id = #{tid};
</select>
  • 测试
@Test
public void test2(){
    SqlSession sqlSession = MyBatisUtils.getSession();
    try{
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> studentList = mapper.getStudent();
        for (Student student : studentList) {
            System.out.println(student);
        }
    }finally {
        sqlSession.close();
    }
}

13.4、联表查询:结果嵌套处理(推荐)

  • mapper.xml
<!--按照结果嵌套处理-->
<select id="getStudent2" resultMap="studentTeacher2">
    select s.id sid,s.name sname,t.id tid,t.name tname
    from mybatis.student s,mybatis.teacher t
    where s.tid = t.id;
</select>
<resultMap id="studentTeacher2" type="student">
    <result property="id" column="sid"/>
    <result property="name" column="sname"/>
    <association property="teacher" javaType="teacher">
        <result property="name" column="tname"/>
        <!--不指定id时,查询出的老师id为0-->
        <!--<result property="id" column="tid"/>-->
    </association>
</resultMap>
  • 测试

14、一对多

14.1、联表查询

1、实体类

学生类:Student.java

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

老师类:Teacher.java

@Data
public class Teacher {
    private int id;
    private String name;
    private List<Student> students;
}

2、接口类

public interface TeacherMapper {

    //获取所有老师
    List<Teacher> getTeacher();

    //获取特定老师及学生
    Teacher getTeacherById(@Param("tid") int id);
}

3、接口配置TeacherMapper.xml

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ajun.mapper.TeacherMapper">
    <select id="getTeacher" resultType="teacher">
        select * from mybatis.teacher;
    </select>

    <select id="getTeacherById" resultMap="teacherStudent">
        select t.id tid,t.name tname,s.id sid,s.name sname
        from mybatis.teacher t,mybatis.student s
        where t.id = #{tid} and t.id = s.tid
    </select>
    <resultMap id="teacherStudent" 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>

4、mybatis-config.xml 中添加mapper

就是告诉 MyBatis 去哪里找映射文件(SQL语句)。

不支持通配符。推荐用package,可以指定包下的所有配置文件。

resource和class需要一个一个地配置

四种配置方法。见5

5、工具类

同上

6、测试

@Test
public void test2() {
    SqlSession sqlSession = MyBatisUtils.getSession();
    try {
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeacherById(1);

        System.out.println("===老师===");
        System.out.println("id: "+teacher.getId());
        System.out.println("姓名: "+teacher.getName());
        List<Student> students = teacher.getStudents();
        for (Student student : students) {
            System.out.println(student);
        }
    } finally {
        sqlSession.close();
    }

}

14.2、子查询

与14.1联表查询环境基本一致。主要改动为TeacherMapper.xml:

<!--子查询:按查询嵌套处理-->
<select id="getTeacherById2" resultMap="teacherStudent2">
    select * from mybatis.teacher where id = #{tid};
</select>
<resultMap id="teacherStudent2" type="teacher">
    <collection property="students" javaType="ArrayList"
                ofType="student" select="getStudentByTeacherId"
                column="id"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="student">
    select * from mybatis.student where tid = #{tid};
</select>

14.4、多对一 和 一对多 比较

  • 都推荐用联表查询

  • 多对一:结果集映射用 association 表示一个实体,指向 的一方。类型为:javaType

    <!--子查询-->
    <association property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
    
    <!--联表查询-->
    <association property="teacher" javaType="teacher">
        <result property="name" column="tname"/>
        <!--不指定id时,查询出的老师id为0-->
        <!--<result property="id" column="tid"/>-->
    </association>
    
  • 一对多:结果集映射用 collection 表示集合,指向 的一方。

    子查询中用javaType表示结果集(ArrayList),用ofType表示结果集中的对象student

    <!--子查询-->
    <collection property="students" javaType="ArrayList"
                    ofType="student" select="getStudentByTeacherId"
                    column="id"/>
    
    <!--联表查询-->
    <collection property="students" ofType="student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <!-- 名字相同时可以省略
        <result property="tid" column="tid"/>
        -->
    </collection>
    

15、动态SQL

动态 SQL 是 MyBatis 的强大特性之一。可以根据条件动态地生成SQL

15.1、环境搭建

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 '浏览量',
	PRIMARY KEY (`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

2、工具类

  • IDUtils.java

    生成随机id,并去掉 - (把 - 换成空)

public class IDUtils {
    //随机生成随机id,把-换成空
    public static String getId(){
        return UUID.randomUUID().toString().replaceAll("-","");
    }
}
  • MyBatisUtils.java

    和之前相同

3、实体类 Blog.java

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

4、Mapper接口类

//BlogMapper.java
public interface BlogMapper {
    //添加
    int addBlog(Blog blog);
}

5、配置文件

  • mybatis-config.xml
<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>

    <!--开启驼峰命名自动映射:
	即从经典数据库列名 CREATE_TIME 映射到经典 Java 属性名 createTime-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
  • BlogMapper.xml

    放在resources下的对应目录中,maven不用做静态资源过滤

<!--插入博客-->
<insert id="addBlog" parameterType="blog">
	insert into mybatis.blog(id, title, author, create_time, views)
	values (#{id},#{title},#{author},#{createTime},#{views});
</insert>

6、测试

@Test
public void test1(){
    SqlSession sqlSession = MyBatisUtils.getSession();
    try{
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        Blog blog = new Blog();
        blog.setId(IDUtils.getId());
        blog.setAuthor("ajun");
        blog.setCreateTime(new Date());
        blog.setViews(999);
        blog.setTitle("红楼梦");

        mapper.addBlog(blog);

        blog.setId(IDUtils.getId());
        blog.setTitle("水浒传");
        mapper.addBlog(blog);

        blog.setId(IDUtils.getId());
        blog.setTitle("西游记");
        mapper.addBlog(blog);

        blog.setId(IDUtils.getId());
        blog.setTitle("三国演义");
        mapper.addBlog(blog);

        sqlSession.commit();//提交事务

    }finally {
        sqlSession.close();
    }
}

15.2、if标签

  • 接口类增加方法:
//查询
List<Blog> getBlogByMap(Map map);
  • BlogMapper.xml
<!--查询-->
<select id="getBlogByMap" parameterType="map" resultType="blog">
    select * from mybatis.blog where views > 500
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null">
        AND author like #{author}
    </if>
</select>

根据title和author是否为空,可以动态生成SQL

默认SQL:
select * from mybatis.blog where views > 500

title不为空时:
select * from mybatis.blog where views > 500  and  title like  #{title}

author不为空时: 
select * from mybatis.blog where views > 500  and  author like #{author}
  • 测试
@Test
public void test2(){
    SqlSession sqlSession = MyBatisUtils.getSession();
    try{
        BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);

        Map<String,Object> map = new HashMap<String,Object>();
        map.put("title","西游记");
        //map.put("author","aaa");
        List<Blog> blogs = blogMapper.getBlogByMap(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
    }finally {
        sqlSession.close();
    }
}



15.3、where标签

<!--查询-->
<select id="getBlogByMap" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <if test="views != 0">
            views > 500
        </if>
        <if test="title != null">
            AND title like #{title}
        </if>
        <if test="author != null">
            AND author like #{author}
        </if>
    </where>
</select>

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

15.3、choose(when、otherwise)标签

按顺序只选择其中的一个条件

where和choose配合使用

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

15.4、set标签

<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元素中间的判断条件要有至少一个成立

15.5、trim标签

trim为自定义标签,可以替换where和set

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。

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

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

15.6、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" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <include refid="if-title-author"></include>
    </where>
</select>

注意:

  • 最好基于单表

  • sql里不要存在where标签

  • 了解即可

15.7、foreach

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

<!--
select * from blog where id in (1,2,3,4)
-->
<select id="selectPostIn" resultType="blog">
  select * from blog where id in
  <foreach item="item" index="index" collection="ids"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>

<!--
select * from blog where (id = 1 or id =2 or id =3)
-->
<select id="selectPostIn" resultType="blog">
  select * from blog
  <where>
      <foreach item="item" index="index" collection="ids"
      	open="(" separator="or" close=")">
        #{item}
      </foreach>  
  </where>
</select>

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

16、缓存

16.1、简介

查询 : 连接数据库,耗资源

一次查询的结果,给他暂存一个可以直接取到的地方 --> 内存:缓存

我们再次查询的相同数据的时候,直接走缓存,不走数据库了
12345
  • 什么是缓存Cache?
    • 存在内存中的临时数据
    • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题
  • 为什么使用缓存?
    • 减少和数据库的交互次数,减少系统开销,提高系统效率
  • 什么样的数据可以使用缓存?
    • 经常查询并且不经常改变的数据 【可以使用缓存】

16.2、MyBatis缓存

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

16.3、一级缓存

也叫本地缓存:SqlSession

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中
  • 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
  • 一级缓存的生命周期:最长不超过从SqlSession创建到关闭。如果执行了非查询操作,缓存会被刷新。

一级缓存不能关闭,可以配置。默认为session。

设置名 描述 有效值 默认值
localCacheScope MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 SESSION | STATEMENT SESSION

测试:

  • 开启日志
<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
  • 测试在一个Session中查询两次相同记录
@Test
public void test(){
    SqlSession sqlSession = MyBatisUtils.getSession();
    try{
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user1 = mapper.getUserById(4);
        System.out.println("第一次:"+user1);

        System.out.println("-----------------------");

        User user2 = mapper.getUserById(4);
        System.out.println("第二次:"+user2);

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

    }finally {
        sqlSession.close();
    }
}

缓存失效的情况:

  • 查询不同的东西

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

  • 查询不同的Mapper.xml

  • 手动清理缓存

    sqlSession.clearCache();
    

16.4、二级缓存

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

一级缓存开启(SqlSession级别的缓存,也称为本地缓存)

  • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
  • 为了提高可扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来定义二级缓存。

二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。

步骤:

1、开启全局缓存

默认是开启的:true。可以省略

<!--显式的开启全局缓存-->
<setting name="cacheEnabled" value="true"/>

2、在Mapper.xml中使用缓存

<!--在当前Mapper.xml中使用二级缓存-->
<cache/>

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

3、实体类序列化

@Data
public class User implements Serializable {
    private int id;
    private String name;
    private String ps;
}

4、测试

@Test
public void test1(){

    //生成session1
    SqlSession sqlSession1 = MyBatisUtils.getSession();

    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);

    //查询用户4,存入一级缓存
    User user1 = mapper1.getUserById(4);
    System.out.println("1:"+user1);

    //查询用户3,存入一级缓存
    User user2 = mapper1.getUserById(3);
    System.out.println("2:"+user2);

    //重复查询用户3
    User user3 = mapper1.getUserById(3);
    System.out.println("3:"+user3);

    //关闭session1,清空一级缓存
    sqlSession1.close();

    System.out.println("-----------------------");

    //生成session2
    SqlSession sqlSession2 = MyBatisUtils.getSession();

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

    User user4 = mapper2.getUserById(3);
    System.out.println("4:"+user4);

    sqlSession2.close();

}


16.5、缓存原理


16.6、自定义缓存

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

  • 导包
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.1</version>
</dependency>
  • 在mapper中指定ehcache缓存实现
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
  • ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <!--
       diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
       user.home – 用户主目录
       user.dir  – 用户当前工作目录
       java.io.tmpdir – 默认临时文件路径
     -->
    <diskStore path="java.io.tmpdir/Tmp_EhCache"/>
    <!--
       defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
     -->
    <!--
      name:缓存名称。
      maxElementsInMemory:缓存最大数目
      maxElementsOnDisk:硬盘最大缓存个数。
      eternal:对象是否永久有效,一但设置了,timeout将不起作用。
      overflowToDisk:是否保存到磁盘,当系统当机时
      timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
      timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
      diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
      diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
      diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
      memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
      clearOnFlush:内存数量最大时是否清除。
      memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
      FIFO,first in first out,这个是大家最熟的,先进先出。
      LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
      LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
   -->
    <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>
posted @ 2021-08-12 11:29  土味儿  阅读(32)  评论(0)    收藏  举报