TMaize Blog

MyBatis笔记整理

mybatis-3官方中文文档

基本使用

  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 引用外部配置

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

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

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

细节

select标签里的useCache仅对二级缓存生效,以及缓存是一致都使用的

增删改标签的flushCache默认为false

增删改标签的flushCache默认为true,一级缓存和二级缓存都会会被清空

sqlsession.clearCache()只是清空一级缓存

3.3版本之后的localCacheScope

与第三方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>