MyBatis笔记整理

TMaize 于 2016-11-17 发布

基本使用

  1. 引入 jar 文件,jdbc 的 jar 和 mybatis 的 jar,如果需要使用 mybatis 的高级功能可以再添加相关的依赖

  2. 根据全局配置文件创建一个 SqlSessionFactory 对象

  3. sql 映射文件;配置了每一个 sql,以及 sql 的规则封装等

  4. 将 sql 映射文件注册中在全局配置中

  5. 代码编写/接口和非接口两种使用方式

<?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.jdbc.Driver" />
                <property name="url"
                value="jdbc:mysql://127.0.0.1:3306/mm?characterEncoding=utf-8" />
                <property name="username" value="tmaize" />
                <property name="password" value="123456" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="User.xml" />
    </mappers>
</configuration>

非接口式编写

<!-- 命名空间,为sql语句划定范围,同时减少输入,在接口式编写中用于指定mapper位置 -->
<mapper namespace="namespace1">
    <select id="findUserById" parameterType="int" resultMap="userResultMap">
        SELECT * FROM user WHERE id = #{id}
    </select>
</mapper>
Reader reader = Resources.getResourceAsReader("SqlMapConfig.xml");
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);

try{
    //sqlSession非线程安全,最好在方法体内定义,传入false可以设置为非自动提交
    SqlSession sqlSession = sqlMapper.openSession();
    // ...事务相关的代码
    // sqlSession.commit();
    User user = sqlSession.selectOne("namespace1.findUserById", 1);
}catch(){
    //回滚
}finally(){
    //每次使用结束最好关闭它
    sqlSession.close();
}

接口式编写

底层使用代理来实现,MyBatis 会为接口自动创建一个代理对象类执行接口里面的方法,好处在于能够规范传递的参数类型。同时 namespace 和 Mapper 的全类名对应,id 和方法名对应

UserMapper.xml,在主配置文件中加入 UserMapper.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">
<!-- 动态代理时namespace不能乱取名字了,要指定mapper -->
<mapper namespace="dao.UserDao">
    <select id="findUserById" parameterType="int" resultType="entity.User">
        SELECT * FROM user WHERE id = #{id}
    </select>
</mapper>

UserDao.java

import entity.User;

public interface UserDao {
    //方法名和UserMapper.xml中语句的id一样
    public User findUserById(int id);
}

测试

//动态代理关键在于不用写dao实现了
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = userDao.findUserById(1);
System.out.println(user.toString());

注意:sqlSessionFactory.openSession()默认是 false,即不自动提交更改,便于事务的回滚,在测试的时候可以 sqlSessionFactory.openSession(true),即自动提交,不需要手动 commit

主要类

配置显示 SQL 语句

Mybatis 内置的日志工厂提供日志功能,需要导入相关的 jar 包,具体的日志实现有以下几种方式:

具体选择哪个日志实现由 MyBatis 的内置日志工厂确定。它会使用最先找到的(按上文列举的顺序查找)。 如果一个都未找到,日志功能就会被禁用。

不少应用服务器的 classpath 中已经包含 Commons Logging,如 Tomcat 和 WebShpere, 所以 MyBatis 会把它作为具体的日志实现。

如果使用其他的最好手动指定需要在配置文件中指定使用那种日志工具

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

接着创建 log4j.properties

log4j.rootLogger=DEBUG, Console

# Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n

log4j.logger.java.sql.ResultSet=INFO
log4j.logger.org.apache=INFO
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

全局配置文件

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

注意:在进行配置时,标签的顺序必须和下面的顺序一致,否则会报错

properties 引用外部配置

在配置文件里面可以通过${key}的方式来得到外部 properties 对应的 value

<properties resource="org/mybatis/example/config.properties">
    <!-- 覆盖或者重新定义 -->
    <property name="username" value="dev_user"/>
</properties>

Settings

这些是极其重要的调整,它们会修改 MyBatis 在运行时的行为方式。

<settings>
    <setting name="cacheEnabled" value="true"/>
    <!-- ... -->
</settings>

p01

typeAliases 起别名

类型别名是为 Java 类型命名一个短的名字。它只和 XML 配置有关,只用来减少类完全限定名的多余部分对于普通的 Java 类型,有许多内建的类型别名。它们都是大小写不敏感的,由于重载的名字,要注意原生类型的特殊处理。

p02

为类自定义别名

<typeAliases>
    <typeAlias alias="User" type="net.tmaize.mybatis.entity.User"/>
    <!-- 为某个包下面的所有类起别名,默认名是类名小写 -->
    <!-- 需要自定义名字可以在类上添加@Alias注解指定 -->
    <package name="xx.xx.xx"></package>
</typeAliases>

起了别名后下面两个是等价的

<select id="findUserById" parameterType="int" resultType="net.tmaize.mybatis.entity.User">
<select id="findUserById" parameterType="int" resultType="User">

typeHandlers

无论是 MyBatis 在预处理语句中设置一个参数,还是从结果集中取出一个值时,类型处理器被用来将获取的值以合适的方式转换成 Java 类型。下面这个表格描述了默认的类型处理器。

MyBatis3.4 以前的版本需要我们手动注册这些处理器,以后的版本都是自动注册的

03

最重要的是你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型

自定义累类型处理器

public class ExampleTypeHandler implements TypeHandler{}

添加到配置文件中

<typeHandlers>
    <typeHandler javaType="String" jdbcType="VARCHAR" handler="xx.ExampleTypeHandler"/>
</typeHandlers>

objectFactory

MyBatis 每次创建结果对象新的实例时,它使用一个 ObjectFactory 实例来完成。如果参数映射存在,默认的 ObjectFactory 不比使用默认构造方法或带参数的构造方法实例化目标类做的工作多。 如果你想重写默认的 ObjectFactory,你可以创建你自己的。

ObjectFactory 接口很简单。它包含两个创建用的方法,一个是处理默认构造方法的,另外一个是处理带参数构造方法的。最终, setProperties 方法可以被用来配置 ObjectFactory。在初始化你的 ObjectFactory 实例后, objectFactory 元素体中定义的属性会被传递给 setProperties 方法。

public class ExampleObjectFactory extends DefaultObjectFactory {
    public Object create(Class type) {
        return super.create(type);
    }
    public Object create(Class type,List<Class> constructorArgTypes,
        List<Object> constructorArgs) {
        return super.create(type, constructorArgTypes,constructorArgs);
    }
    public void setProperties(Properties properties) {
        super.setProperties(properties);
    }
}
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
    <property name="someProperty" value="100"/>
</objectFactory>

plugins

MyBatis 允许你在某一点拦截已映射语句执行的调用。默认情况下, MyBatis 允许使用插件来拦截方法调用

environments 多环境

MyBatis 可以配置多种环境。这会帮助你将 SQL 映射应用于多种数据库之中。例如,你也许为开发要设置不同的配置,测试和生产环境。或者你可能有多种生产级数据库却共享相同的模式,所以你会想对不同数据库使用相同的 SQL 映射。

一个很重要的问题要记得:你可以配置多种环境,但你只能为每个 SqlSessionFactory 实例选择一个

每个数据库对应一个 SqlSessionFactory

<environments default=" development">
    <environment id="development">
        <!-- ... -->
    </environment>
    <environment id="test">
        <!-- ... -->
    </environment>
</environments>

如果环境被忽略,那么默认环境将会被加载

// 指定配置中的环境id
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader,environment,properties);

// 使用默认环境
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader,properties);

environment

environment>transactionManager

在 MyBatis 中有两种事务管理器类型type="[JDBC|MANAGED]"

JDBC – 这个配置直接简单使用了 JDBC 的提交和回滚设置。它依赖于从数据源得到的连接来管理事务范围。

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

environment>dataSource

dataSource 元素使用基本的 JDBC 数据源接口来配置 JDBC 连接对象的资源

有三种内建的数据源类型:

一个常用的配置

<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/mm?characterEncoding=utf-8" />
        <property name="username" value="tmaize" />
        <property name="password" value="123456" />
    </dataSource>
</environment>

mappers

告诉 MyBatis 到哪里去找到我们定义的 SQL 映射语句配置文件

resource 引用类路径下的文件,url 引用网络路径或者磁盘路径下的文件,class 引用接口 (配置文件和接口同名且在同一个目录下 ,或者接口的每个方法上有 mybatis 的注解)

<mappers>
    <!-- 位置任意 -->
    <mapper resource="xxx.UserMapper.xml" />
    <!-- 批量添加,要求SQL映射文件名必须和接口名相同并且在同一目录下,不能分开还有啥意思。不过 在和spring整合时支持配置mapperLocations来分离-->
    <package name="xx.xx"><package>
</mappers>

databaseIdProvider

根据不同的数据库执行不同的 sql 语句

sql 语句匹配规则如下:

  1. 如果没有配置 databaseIdProvider 标签,那么 databaseId=null

  2. 如果配置了 databaseIdProvider 标签,使用标签配置的 name 去匹配数据库信息,匹配上设置 databaseId=配置指定的值,否则依旧为 null

  3. 如果 databaseId 不为 null,他只会找到配置 databaseId 的 sql 语句

  4. MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。如果同时找到带有 databaseId 和不带 databaseId 的相同语句, 则后者会被舍弃。

<!-- type="VENDOR"固定,根据连接返回的信息的数数据库厂商 -->
<databaseIdProvider type="VENDOR">
    <!-- 为不同的数据库厂商起别名,value为别名 -->
    <property name="SQL Server" value="sqlserver"/>
    <property name="DB2" value="db2"/>
    <property name="Oracle" value="oracle" />
</databaseIdProvider>

在 sql 映射文件中

<select id="findUserById" parameterType="int" resultType="entity.User" databaseId="mysql">

</select>

<select id="findUserById" parameterType="int" resultType="entity.User" databaseId="mysql">

</select>

SQL 映射文件配置

配置 SQL 语句…

SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):

<mapper namespace="namespace1">
    <select id="findUserById" parameterType="int" resultType="entity.User">
        SELECT * FROM user WHERE id = #{id}
    </select>
</mapper>

sql 操作

查询语句是使用 MyBatis 时最常用的元素之一

下面的语句被称作 selectPerson,使用一个 int(或 Integer)类型的参数,并返回一个 HashMap 类型的对象,其中的键是列名,值是列对应的值。

<select id="selectPerson" parameterType="int" resultType="hashmap">
    SELECT * FROM PERSON WHERE ID = #{id}
</select>

select 元素有很多属性允许你配置,来决定每条语句的作用细节

<select
  id="selectPerson" 在命名空间中唯一的标识符,可以被用来引用这条语句
  parameterType="int" 传入这条语句的参数类的完全限定名或别名
  parameterMap="deprecated" 废弃
  resultType="hashmap" 返回的期望类型的类的完全限定名或别名
  resultMap="personResultMap" 命名引用外部的 resultMap,解决了许多复杂映射的情形
  flushCache="false" 每次调用是否清空缓存
  useCache="true" 是否缓存这条语句的结果,默认值: true。
  timeout="10000" 超过等待时间抛出异常,一般不设置
  fetchSize="256" 暗示驱动程序每次批量返回的结果行数 一般不设置
  statementType="PREPARED" 让MyBatis使用 Statement,PreparedStatement 还是CallableStatement
  resultSetType="FORWARD_ONLY">

insert, update and delete 这几个可以归为一类,因为它们不需要返回特殊数据

所以它们的接口发方法可以直接定义下面记几种类型的返回值 Integer,Long ,Boolean(>0 为 true)

<insert
  id="insertAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  keyProperty=""
  keyColumn=""
  useGeneratedKeys=""
  timeout="20">

<update
  id="insertAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">

<delete
  id="insertAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">

参数传递

如果要传入的多个参数没有对应的业务模型(POJO),但是又要经常使用,为了方便推荐编写一个 TO(Transfer Object)数据传输对象

在使用接口开发时,标签上的 parameterType 可以不写,不过为了规范还是要写上 parameterType,同时建议使用接口开发,来规范化传入的参数类型

#{}和¥{}

默认情况下,使用#{}格式的语法会导致 MyBatis 创建预处理语句属性并以它为背景设置安全的值(比如?)。这样做很安全,很迅速也是首选做法,它是通过 PreparedStatement 添加参数。

有时你只是想直接在 SQL 语句中插入一个不改变的字符串,应该使用${},这是简单的字符串拼接,比如在 order by 时不支持?占位符,可以通过这个来解决

参数处理

参数位置支持的属性

javaType、 jdbcType、 mode、 numericScale、resultMap、 typeHandler、 jdbcTypeName、 expression

比如

#{id ,javaType=int,jdbcType=NUMERIC}

jdbcType 通常需要在某种特定的条件下设置,在数据为 null 的时候,有些数据库不能识别 mybatis 对 null 的默认处理,比如 Oracle

mybatis 对所有的 null 都映射的是 JdbcType 里面的 Other

public enum JdbcType {

    NULL(Types.NULL),
    OTHER(Types.OTHER),
    // ...

}

可以设置#{jdbcType=NULL}

或者在全局配置文件中设置

<settings>
    <setting name="jdbcTypeForNull" value="NULL"/>
</settings>

返回值

resultType,如果要返回集合,类型要填写集合里面元素的类型

当结果有一行的时候返回对象,当有多行的时候返回 List<对象>,但是 resultType 都是对象

例如返回 hashmap,key 为列名,value 为对应的行值

当为一行结果时候,实际得到的是 Map<String,Object>

当为多行结果时候,实际得到的是 List<Map<String,Object»

在执行 insert, update and delete 时可以直接定义下面记几种类型的返回值 Integer,Long ,Boolean(>0 为 true),如果使用接口开发,在接口中方法的返回类型指定即可,比如:

public boolean addUser(User user);

ResultMap 是一个很重要的特性当你不知道返回的数据类型的时候,你可以指定返回 hashmap 类型,所有列被自动映射到 HashMap 的键上

HashMap<String, String> map = sqlSession.selectOne("namespace1.hashmap_id",1);

映射到 JavaBean

<select id="findUserById" parameterType="int" resultType="entity.User">
    SELECT * FROM user WHERE id = #{id}
</select>

在 resultType 指定为一个实体类的时候,MyBatis 也是会在幕后自动创建一个 ResultMap,然后基于属性名/get 来映射列到 JavaBean 的属性上,如果类名和属性名不一致的的话可以在 sql 语句中为列名起别名

MyBatis 还提供了另外一种方式来解决列名和属性名不匹配的问题

<resultMap id="userResultMap" type="entity.User">
    <id property="id" column="id" />
    <result property="name" column="u_name" />
    <!-- 列名和属性名一致的时候可以不写这条result -->
    <result property="address" column="address" />
</resultMap>

<select id="findUserById" parameterType="int" resultMap="userResultMap">
    SELECT * FROM user WHERE id = #{id}
</select>

级联映射

同时 resultMap 还支持级联属性封装,比如某些属性封装成实体类的某个对象属性

<resultMap type="xx.user" id="user_map">
    <id property="id" column="id" />
    <result property="name" column="name" />
    <result property="address" column="address" />
    <association property="userType" javaType="xx.userType">
        <id column="userType_id" property="id" />
        <result column="userType_name" property="name" />
    </association>
</resultMap>

或者使用一对多的级联映射,和 association 的使用方法相似,比如查询某个部门信息时候,顺便查出部门下的所有员工

<resultMap type="entity.TypeVo" id="collTestMap">
    <id property="id" column="id" />
    <result property="name" column="name"/>
    <collection property="users" ofType="entity.User">
        <id property="id" column="u_id"/>
        <result property="name" column="u_name"/>
    </collection>
</resultMap>


<select id="typeAndUserByTypeId" parameterType="int" resultMap="collTestMap">
    SELECT type.* ,user.id u_id,user.name u_name
    FROM type LEFT JOIN user ON user.type_id=type.id
    WHERE type.id=#{id}
</select>
<!-- Very Complex Result Map -->
<resultMap id="detailedBlogResultMap" type="Blog">
  <constructor>
    <idArg column="blog_id" javaType="int"/>
  </constructor>
  <result property="title" column="blog_title"/>
  <association property="author" javaType="Author">
    <id property="id" column="author_id"/>
    <result property="username" column="author_username"/>
    <result property="password" column="author_password"/>
    <result property="email" column="author_email"/>
    <result property="bio" column="author_bio"/>
    <result property="favouriteSection" column="author_favourite_section"/>
  </association>
  <collection property="posts" ofType="Post">
    <id property="id" column="post_id"/>
    <result property="subject" column="post_subject"/>
    <association property="author" javaType="Author"/>
    <collection property="comments" ofType="Comment">
      <id property="id" column="comment_id"/>
    </collection>
    <collection property="tags" ofType="Tag" >
      <id property="id" column="tag_id"/>
    </collection>
    <discriminator javaType="int" column="draft">
      <case value="1" resultType="DraftPost"/>
    </discriminator>
  </collection>
</resultMap>

关联的嵌套查询,延迟加载

resultMap 可实现高级映射(使用 association、collection 实现一对一及一对多映射),association、collection 具备延迟加载功能。

在进行级联查询的时候,在某个实体类内的某个属性在进行使用的时候再去加载

在全局配置文件中添加如下配置或者在标签中设置 fectht

<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 某个属性使用时其余属性全部加载,新版本默认为false -->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>
<resultMap type="entity.User" id="lazyloadingTest">
    <id property="id" column="id" />
    <result property="name" column="name"/>
    <association property="type" javaType="entity.Type" select="findTypeById" column="type_id">
    </association>
</resultMap>

<select id="findUserById" parameterType="int" resultMap="lazyloadingTest">
    SELECT * FROM user WHERE id = #{id}
</select>

<select id="findTypeById" parameterType="int" resultType="entity.Type">
    SELECT * FROM type WHERE id = #{id}
</select>
User user = userDao.findUserById(5);
System.out.println(user.getId());
System.out.println(user.getName());
// 开启懒加载时,再发送一条语句
System.out.println(user.getType());

自增主键的获取

mysql 支持自增主键,以及值的获取,mybatis 也是利用 statement.getGenreatedkeys()

useGeneratedKeys 是否使用自增主键获取主键值策略

keyProperty 把添加后生成的主键映射到实体类的哪个属性上

keyProperty

<insert id="addUser" parameterType="entity.User" useGeneratedKeys="true" keyProperty="id">

Oracle 不支持自增,Oracle 使用序列来模拟自增,每次输入的数据的主键都是从序列中拿到的值

<insert id="addUser" parameterType="entity.User">
    <selectKey keyProperty="id" order="BEFORE" resultType="Integer">
    SELECT EMPLOYEE_SQL.nextval from dual
    </selectKey>
    INSERT INTO user(id,name,address) VALUES(#{id},#{name},#{address})
</insert>

动态 SQL

MyBatis 的一个强大的特性之一通常是它的动态 SQL 能力。 如果你有使用 JDBC 或其他 相似框架的经验,你就明白条件地串联 SQL 字符串在一起是多么的痛苦,确保不能忘了空 格或在列表的最后省略逗号。动态 SQL 可以彻底处理这种痛苦。

通常使用动态 SQL 不可能是独立的一部分,MyBatis 当然使用一种强大的动态 SQL 语 言来改进这种情形,这种语言可以被用在任意映射的 SQL 语句中。 动态 SQL 元素和使用 JSTL 或其他相似的基于 XML 的文本处理器相似。在 MyBatis 之 前的版本中,有很多的元素需要来了解。MyBatis 3 大大提升了它们,现在用不到原先一半 的元素就能工作了。MyBatis 采用功能强大的基于 OGNL 的表达式来消除其他元素。

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

常用标签

比如下面的语句,和 where 多条件一样容易多一个 and 这里可能会多一个逗号,所以出现了 set 标签

<update id="updateUser">
    update user set
    <if test="name!=null">
        name=#{name},
    </if>
    <if test="birthday!=null">
        birthday=#{birthday},
    </if>
    <if test="address!=null">
        ddress=#{address}
    </if>
    where id = #{id}
</update>

有可能会多出一个逗号,应使用 set 标签

<update id="updateOne" parameterType="user">
    update user
    <set>
        <if test="name!=null">
            name=#{name},
        </if>
        <if test="birthday!=null">
            birthday=#{birthday},
        </if>
        <if test="address!=null">
            address=#{address}
        </if>
    </set>
    where id = #{id}
</update>

内置参数

_parameter,代表整个参数,如果传递过来的是单个简单参数,那么_parameter就是这个参数,多个参数时候,参数会被封装成一个 map,_parameter就代表这个 map

_databaseId,如果配置了 databaseIdProvider 标签,那么_databaseId就是代表当前数据库的别名

<databaseIdProvider type="VENDOR">
    <property name="SQL Server" value="sqlserver"/>
    <property name="DB2" value="db2"/>
    <property name="Oracle" value="oracle" />
</databaseIdProvider>

<select id="findUserById" parameterType="int" resultType="entity.User" databaseId="mysql">
    ...
</select>

<select id="findUserById" parameterType="int" resultType="entity.User" databaseId="mysql">
    ...
</select>

可以变成这样

<select id="findUserById" parameterType="int" resultType="entity.User">
    <if test="_databaseId=='db2'">
        ....
    </test>
    <if test="_databaseId=='oracle'">

    </test>
</select>

缓存

缓存可以极大的提升查询效率

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

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

  1. 默认情况下,只有一级缓存( SqlSession 级别的缓存,也称为本地缓存)开启

  2. 二级缓存需要手动开启和配置,他是基于 namespace 级别的缓存

  3. 为了提高扩展性。 MyBatis 定义了缓存接口 Cache。我们可以通过实现 Cache 接口来自定义二级缓存

一级缓存

一级缓存(local cache), 即本地缓存, 作用域默认为 sqlSession。当 Session flush 或 close 后, 该 Session 中的所有 Cache 将被清空

同样的查询不会发送两次 sql 语句,返回的也是同一个对象

本地缓存不能被关闭 , 但可以调用 clearCache()来清空本地缓存, 或者改变缓存的作用域

在 mybatis3.1 之后, 可以配置本地缓存的作用域,在主配置文件中配置

失效条件

SqlSession 不同,对于不同的 SqlSession,执行同一条 sql 语句会有两次请求

SqlSession 相同,执行不同的查询条件

SqlSession 相同,两次相同 sql 查询之间有增删改操作

SqlSession 相同,手动清除以及缓存,sqlsession.clearCache()

二级缓存

全局的缓存,基于 namespace 级别的缓存,一个 namespace 对应一个二级缓存

工作机制:

  1. 一个会话,查询一条语句,这个数据就会被放在当前会话的以及缓存中

  2. 如果会话关闭,一级缓存的数据将会保存到二级缓存中,新的回话查询信息就可以参考二级缓存

  3. 不同的 namespace 查出来的数据会放在自己对应的缓存中(map)

使用步骤

  1. 配置开启

    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
    
  2. 在 mapper 中添加一个 cache 标签

    一般使用<cache></cache>即可

    <cache eviction="缓存回收策略" blocking="" flushInterval="缓存刷新时间间隔" readOnly="是否只读" size="缓存多少个元素" type="指定自定义缓存的全类名,实现Cache接口">
    </cache>
    
  3. POJO 实现序列化 Serializable 接口

细节

与第三方 cache 插件整合

https://github.com/mybatis查看官方的缓存整合项目和下载 jar 文件

比如使用 ehcache,需要整合包 mybatis-ehcache

jar 包准备好之后,创建 ehcache.xml 文件

<mapper namespace="">
    <cache type="org.mybatis.caches.ehcache.EhcacheCache">
        <property name="" value=""/>
        <!-- ... -->
    </cache>
    <!-- ... -->
</mapper>

逆向工程

运行命令 mybatis-generator:generate

maven 的方式

<plugin>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-maven-plugin</artifactId>
    <version>1.3.5</version>
    <configuration>
    <verbose>true</verbose>
    <overwrite>true</overwrite>
    </configuration>
</plugin>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >

<generatorConfiguration>
    <classPathEntry location="E:\apache-maven-3.3.9\local-repo\mysql\mysql-connector-java\5.1.39\mysql-connector-java-5.1.39.jar" />
    <context id="mysql_cms" targetRuntime="MyBatis3">

        <!-- 可选的,注释生成器 -->
        <commentGenerator>
            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>

        <!-- 必须的,数据库连接的信息:驱动类、连接地址、用户名、密码 -->
        <jdbcConnection
            driverClass="com.mysql.jdbc.Driver"
            connectionURL="jdbc:mysql://127.0.0.1:3306/cms?characterEncoding=utf-8"
            userId="tmaize"
            password="123456">
        </jdbcConnection>

        <!-- 可选的(0 or 1) -->
        <!-- 类型转换器或者加类型解析器 -->
        <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer true,把JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- 必须的,java模型生成器 -->
        <javaModelGenerator targetPackage="net.tmaize.cms.entity" targetProject="src/main/java" />

        <!-- 必须的,map xml 生成器 -->
        <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources" />

        <!-- 可选的,mapper或者就是dao接口生成器 -->
        <javaClientGenerator targetPackage="net.tmaize.cms.dao" targetProject="src/main/java" type="XMLMAPPER" />

        <!-- 必须的(1...N) -->
        <!-- pojo 实体生成器 -->
        <!-- tableName:用于自动生成代码的数据库表;domainObjectName:对应于数据库表的javaBean类名 -->
        <!-- schema即为数据库名 可不写 -->
        <table tableName="user" domainObjectName="User" enableInsert="true">
            <!-- 忽略字段 可选的(0 or 1) -->
            <!-- <ignoreColumn column="is_use" /> -->
        </table>
    </context>
</generatorConfiguration>

参考

mybatis-3 官方中文文档