1. 简介
特性
- 支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架
- 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
- 可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java
Objects,普通的Java对象)映射成数据库中的记录 - 是一个 半自动的ORM(Object Relation Mapping)框架
和其它持久化层技术对比
- JDBC
- SQL 夹杂在Java代码中耦合度高,导致硬编码内伤
- 维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见
- 代码冗长,开发效率低
- Hibernate 和 JPA
- 操作简便,开发效率高
- 程序中的长难复杂 SQL 需要绕过框架
- 内部自动生产的 SQL,不容易做特殊优化
- 基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难
- 反射操作太多,导致数据库性能下降
- MyBatis
- 轻量级,性能出色
- SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据
- 开发效率稍逊于HIbernate,但是完全能够接受
2. 搭建
1. 依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
2. 核心配置文件
<?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="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
3. 映射文件
<?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="org.mybatis.example.BlogMapper">
<insert id="insertUser">
insert into t_user values(null,'admin','123456',23,'男','12345@qq.com')
</select>
</mapper>
3. mapper接口
public interface UserMapper {
int insertUser();
}
4. 测试
//读取MyBatis的核心配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//通过核心配置文件所对应的字节输入流创建工厂类SqlSessionFactory,生产SqlSession对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
//创建SqlSession对象,此时通过SqlSession对象所操作的sql都必须手动提交或回滚事务
//SqlSession sqlSession = sqlSessionFactory.openSession();
//创建SqlSession对象,此时通过SqlSession对象所操作的sql都会自动提交
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//通过代理模式创建UserMapper接口的代理实现类对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//调用UserMapper接口中的方法,就可以根据UserMapper的全类名匹配元素文件,通过调用的方法名匹配 映射文件中的SQL标签,并执行标签中的SQL语句
int result = userMapper.insertUser();
//sqlSession.commit();
System.out.println("结果:"+result);
SqlSession
代表Java程序和数据库之间的会话。(HttpSession是Java程序和浏览器之间的会话)SqlSessionFactory
“生产” SqlSession的 “工厂”
3. 核心配置文件详解
<?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>
<!--MyBatis核心配置文件中,标签的顺序:
properties?,settings?,typeAliases?,typeHandlers?, objectFactory?,
objectWrapperFactory?,reflectorFactory?,
plugins?,environments?,databaseIdProvider?,mappers?
-->
<!--引入properties文件-->
<properties resource="jdbc.properties" />
<!--设置类型别名-->
<typeAliases>
<!--typeAlias:设置某个类型的别名
属性:
type:设置需要设置别名的类型
alias:设置某个类型的别名,若不设置该属性,那么该类型拥有默认的别名,即类名 且不区分大小写
-->
<!--<typeAlias type="com.atguigu.mybatis.pojo.User"></typeAlias>-->
<!--以包为单位,将包下所有的类型设置默认的类型别名,即类名且不区分大小写-->
<package name="com.atguigu.mybatis.pojo"/>
</typeAliases>
<!--environments:配置多个连接数据库的环境
属性:
default:设置默认使用的环境的id
-->
<environments default="development">
<!--environment:配置某个具体的环境
属性:
id:表示连接数据库的环境的唯一标识,不能重复
-->
<environment id="development">
<!--transactionManager:设置事务管理方式
属性:
type="JDBC|MANAGED"
JDBC:表示当前环境中,执行SQL时,使用的是JDBC中原生的事务管理方式,事务的提交或回滚需要手动处理
MANAGED:被管理,例如Spring
-->
<transactionManager type="JDBC"/>
<!--dataSource:配置数据源
属性:
type:设置数据源的类型 type="POOLED|UNPOOLED|JNDI"
POOLED:表示使用数据库连接池缓存数据库连接
UNPOOLED:表示不使用数据库连接池
JNDI:表示使用上下文中的数据源
-->
<dataSource type="POOLED">
<!--设置连接数据库的驱动-->
<property name="driver" value="${jdbc.driver}"/>
<!--设置连接数据库的连接地址-->
<property name="url" value="${jdbc.url}"/>
<!--设置连接数据库的用户名-->
<property name="username" value="${jdbc.username}"/>
<!--设置连接数据库的密码-->
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssmserverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--引入映射文件-->
<mappers>
<!--<mapper resource="mappers/UserMapper.xml"/>-->
<!--以包为单位引入映射文件
要求:
1、mapper接口所在的包要和映射文件所在的包一致
2、mapper接口要和映射文件的名字一致
-->
<package name="com.atguigu.mybatis.mapper"/>
</mappers>
4. 增删改查
增
<!--int insertUser();-->
<insert id="insertUser">
insert into t_user values(null,'admin','123456',23,'男')
</insert>
删
<!--int deleteUser();-->
<delete id="deleteUser">
delete from t_user where id = 7
</delete>
改
<!--int updateUser();-->
<update id="updateUser">
update t_user set username='ybc',password='123' where id = 6
</update>
查
<!--User getUserById();-->
<select id="getUserById" resultType="com.atguigu.mybatis.bean.User">
select * from t_user where id = 2
</select>
<!--List<User> getUserList();-->
<select id="getUserList" resultType="com.atguigu.mybatis.bean.User">
select * from t_user
</select>
- 查询的标签
select
必须设置属性resultType
或resultMap
,用于设置实体类和数据库表的映射关系 resultType
:自动映射,用于属性名和表中字段名一致的情况resultMap
:自定义映射,用于一对多或多对一或字段名和属性名不一致的情况
5. 获取参数值的两种方式
${} 和 #{}
${}
的本质就是字符串拼接
#{}
的本质就是占位符赋值${}
使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号
#{}
使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号
1. 单个字面量类型的参数
-
若mapper接口中的方法参数为单个的字面量类型
-
此时可以使用
${}
和#{}
以任意的名称获取参数的值,注意${}
需要手动加单引号User getUserByUserName(String username);
<select id="getUserByUserName" resultType="user"> select * from t_user where username = '${username}'; </select>
2. 多个字面量类型的参数
-
若mapper接口中的方法参数为多个
-
此时MyBatis会自动将这些参数放在一个map集合中,以**arg0,arg1…**为键,以参数为值;以param1,param2…为键,以参数为值
-
因此只需要通过
${}
和#{}
访问map集合的键就可以获取相
对应的值,注意${}
需要手动加单引号User getLogin(String username,String password);
<!--User getLogin(String username,String password);--> <select id="getLogin" resultType="User"> select * from t_user where username = #{arg0} and password = #{arg1}; </select>
3. map集合类型的参数
-
若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在map中
-
只需要通过
${}
和#{}
访问map集合的键就可以获取相对应的值,注意${}
需要手动加单引号User checkLogin(Map map);
<select id="checkLogin" resultType="user"> select * from t_user where username = #{username} and password = #{password}; </select>
4. 实体类类型的参数
-
若mapper接口中的方法参数为实体类对象时
-
此时可以使用
${}
和#{}
,通过访问实体类对象中的属性名获取属性值,注意${}
需要手动加单引号int insertUser(User user);
<insert id="insertUser"> insert into t_user values(null,#{username},#{password},#{age},#{sex},#{email}); </insert>
5. 使用@Param标识参数
-
可以通过
@Param
注解标识mapper接口中的方法参数 -
此时,会将这些参数放在map集合中,以@Param注解的value属性值为键,以参数为值;以param1,param2…为键,以参数为值
-
只需要通过
${}
和#{}
访问map集合的键就可以获取相对应的值,注意${}
需要手动加单引号User checkLoginByParam(@Param("username") String username,@Param("password") String password);
<select id="checkLoginByParam" resultType="user"> select * from t_user where username = #{username} and password = #{password}; </select>
6. MyBatis的各种查询功能
1. 查询一个实体类对象
User getUserById(@Param("id") int id);
<select id="getUserById" resultType="User">
select * from t_user where id = #{id}
</select>
2. 查询一个list集合
List<User> getUserList();
<select id="getUserList" resultType="User">
select * from t_user
</select>
- 当查询的数据为多条时,不能使用实体类作为返回值,否则会抛出异常TooManyResultsException
- 但是若查询的数据只有一条,可以使用实体类或集合作为返回值
3. 查询单个数据
int getCount();
<select id="getCount" resultType="_integer">
select count(id) from t_user
</select>
4. 查询一条数据为map集合
Map<String, Object> getUserToMap(@Param("id") int id);
<!--结果: {password=123456, sex=男 , id=1, age=23, username=admin}-->
<select id="getUserToMap" resultType="map">
select * from t_user where id = #{id}
</select>
5. 查询多条数据为map集合
List<Map<String, Object>> getAllUserToMap();
<select id="getAllUserToMap" resultType="map">
select * from t_user
</select>
@MapKey("id")
Map<String, Object> getAllUserToMap();
@MapKey
设置map集合的键,值是每条数据所对应的 map集合
<!-- {
1={password=123456, sex=男, id=1, age=23, username=admin},
2={password=123456, sex=男, id=2, age=23, username=张三},
3={password=123456, sex=男, id=3, age=23, username=张三} }
-->
<select id="getAllUserToMap" resultType="map">
select * from t_user
</select>
7. 特殊SQL的执行
1. 模糊查询
List<User> testMohu(@Param("mohu") String mohu);
<select id="testMohu" resultType="User">
<!--select * from t_user where username like '%${mohu}%'-->
<!--select * from t_user where username like concat('%',#{mohu},'%')-->
select * from t_user where username like "%"#{mohu}"%"
</select>
2. 批量删除
int deleteMore(@Param("ids") String ids);
<delete id="deleteMore">
delete from t_user where id in (${ids})
</delete>
3. 动态设置表名
List<User> getAllUser(@Param("tableName") String tableName);
<select id="getAllUser" resultType="User">
select * from ${tableName}
</select>
4. 添加功能获取自增的主键
需求- 需求:在插入一条数据后需要获取该条记录的主键
- 方案
- 在一个单系统中常见的方法M是设置表中主键为自动递增,每次插入后,mybatis会将自动生成的主键赋值给指定的实体类字段
-
若数据库支持自动生成主键的字段(比如 MySQL和 SQL Server),则可以设置
useGeneratedKeys=”true”
,然后再把keyProperty
设置到目标属性上
(局限性很大、不利于项目后期扩展,实际开发中不推荐使用)int insertUser(User user);
<!--int insertUser(User user);--> <insert id="insertUser" useGeneratedKeys="true" keyProperty="id"> insert into t_user values( null, #{username}, #{password}, #{age}, #{sex}) </insert>
@Service public class UserService { @Autowired private UserMapper userMapper; public int insertUser(User user) { return userMapper.insertUser(user); } }
useGeneratedKeys设置为true后,mybatis会使用JDBC的getGeneratedkeys方法获取由数据库内部自动生成的主键,并将该值赋值给由keyProperty指定的字段;
-
对于不支持自增型主键的数据库(例如Oracle),则要先通过序列来模拟自增,每次插入数据前先从序列中拿到自增ID
(无论是单例项目还是分布式项目都适用)
-
- 在分布式系统中,则需要生成全局唯一主键ID
- 在一个单系统中常见的方法M是设置表中主键为自动递增,每次插入后,mybatis会将自动生成的主键赋值给指定的实体类字段
8. 自定义映射resultMap
1. resultMap处理字段和属性的映射关系
<!--resultMap:设置自定义映射
属性:
id:表示自定义映射的唯一标识
type:查询的数据要映射的实体类的类型
子标签:
id:设置主键的映射关系
result:设置普通字段的映射关系
association:设置多对一的映射关系
collection:设置一对多的映射关系
属性:
property:设置映射关系中实体类中的属性名
column:设置映射关系中表中的字段名
-->
<resultMap id="userMap" type="User">
<id property="id" column="id"></id>
<result property="userName" column="user_name"></result>
<result property="password" column="password"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
</resultMap>
<!--List<User> testMohu(@Param("mohu") String mohu);-->
<select id="testMohu" resultMap="userMap">
<!--select * from t_user where username like '%${mohu}%'-->
select
id,
user_name,
password,
age,
sex
from
t_user
where
user_name like concat('%',#{mohu},'%')
</select>
场景
字段名(_连接)与实体类名(小驼峰)不一致的情况,有以下两种处理方式:
- 查询的时候,给字段 取别名(
as
) - 在MyBatis的核心配置文件中设置一个全局配置信息mapUnderscoreToCamelCase,可以在查询表中数据时,自动将_类型的字段名转换为驼峰
在spring中的配置:
在springboot中的配置:<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD SQL Map Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 配置mybatis自动转换为驼峰式命名 --> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> </configuration>
mybatis: # mapper.xml所在位置 mapper-locations: - classpath*:mapper/read/*.xml - classpath*:mapper/write/*.xml # mapper接口所在包 base-packages: - com.yinhai.hipay.**.mapper.read - com.yinhai.hipay.**.mapper.write # 指定POJO扫描包来让mybatis自动扫描到自定义的POJO type-aliases-package: com.example.entity configuration: # 自动将_类型的字段名转换为驼峰 map-underscore-to-camel-case: true cache-enabled: false
2. 多对一映射处理
场景模拟: 查询员工信息以及员工所对应的部门信息。
级联方式处理映射关系
<resultMap id="empDeptMap" type="Emp">
<id column="eid" property="eid"></id>
<result column="ename" property="ename"></result>
<result column="age" property="age"></result>
<result column="sex" property="sex"></result>
<result column="did" property="dept.did"></result>
<result column="dname" property="dept.dname"></result>
</resultMap>
<!--Emp getEmpAndDeptByEid(@Param("eid") int eid);-->
<select id="getEmpAndDeptByEid" resultMap="empDeptMap">
select
emp.*,
dept.*
from
t_emp emp
left join
t_dept dept
on
emp.did = dept.did
where
emp.eid = #{eid}
</select>
使用association处理映射关系
<resultMap id="empDeptMap" type="Emp">
<id column="eid" property="eid"></id>
<result column="ename" property="ename"></result>
<result column="age" property="age"></result>
<result column="sex" property="sex"></result>
<association property="dept" javaType="Dept">
<id column="did" property="did"></id>
<result column="dname" property="dname"></result>
</association>
</resultMap>
<!--Emp getEmpAndDeptByEid(@Param("eid") int eid);-->
<select id="getEmpAndDeptByEid" resultMap="empDeptMap">
select
emp.*,
dept.*
from
t_emp emp
left join
t_dept dept
on
emp.did = dept.did
where
emp.eid = #{eid}
</select>
分步查询
查询员工信息
Emp getEmpByStep(@Param("eid") int eid);
<resultMap id="empDeptStepMap" type="Emp">
<id column="eid" property="eid"></id>
<result column="ename" property="ename"></result>
<result column="age" property="age"></result>
<result column="sex" property="sex"></result>
<association
property="dept"
select="com.atguigu.MyBatis.mapper.DeptMapper.getEmpDeptByStep"
column="did">
</association>
</resultMap>
<select id="getEmpByStep" resultMap="empDeptStepMap">
select
*
from
t_emp
where
eid = #{eid}
</select>
根据员工所对应的部门id查询部门信息
Dept getEmpDeptByStep(@Param("did") int did);
<select id="getEmpDeptByStep" resultType="Dept">
select
*
from
t_dept
where
did = #{did}
</select>
3. 一对多映射处理
collection
Dept getDeptEmpByDid(@Param("did") int did);
<resultMap id="deptEmpMap" type="Dept">
<id property="did" column="did"></id>
<result property="dname" column="dname"></result>
<!--ofType:设置collection标签所处理的集合属性中存储数据的类型 -->
<collection property="emps" ofType="Emp">
<id property="eid" column="eid"></id>
<result property="ename" column="ename"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
</collection>
</resultMap>
<!--Dept getDeptEmpByDid(@Param("did") int did);-->
<select id="getDeptEmpByDid" resultMap="deptEmpMap">
select
dept.*,
emp.*
from
t_dept dept
left join
t_emp emp
on
dept.did = emp.did
where
dept.did = #{did}
</select>
分步查询
查询部门信息
Dept getDeptByStep(@Param("did") int did);
<resultMap id="deptEmpStep" type="Dept">
<id property="did" column="did"></id>
<result property="dname" column="dname"></result>
<collection
property="emps"
fetchType="eager"
select="com.atguigu.MyBatis.mapper.EmpMapper.getEmpListByDid"
column="did">
</collection>
</resultMap>
<!--Dept getDeptByStep(@Param("did") int did);-->
<select id="getDeptByStep" resultMap="deptEmpStep">
select
*
from
t_dept
where
did = #{did}
</select>
根据部门id查询部门中的所有员工
List<Emp> getEmpListByDid(@Param("did") int did);
<select id="getEmpListByDid" resultType="Emp">
select
*
from
t_emp
where
did = #{did}
</select>
分步查询的优点
可以实现延迟加载
-
lazyLoadingEnabled
延迟加载的全局开关。当开启时,所有关联对象都会延迟加载 -
aggressiveLazyLoading
当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载 -
spring中的配置
<configuration> <!-- 全局参数的配置 --> <settings> <!--打开延迟加载的开关 --> <setting name="lazyLoadingEnabled" value="true"/> <!--将积极加载改为消极加载及按需加载 --> <setting name="aggressiveLazyLoading" value="false"/> </settings> </configuration>
-
springboot中的配置
mybatis: configuration: lazyLoadingEnabled: true aggressiveLazyLoading: false
-
此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过
association
和collection
中的fetchType
属性设置当前的分步查询是否使用延迟加载,fetchType="lazy(延迟加载)|eager(立即加载)"
9. 动态SQL
Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了 解决 拼接SQL语句字符串时的痛点问题。
1. if
if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之 标签中的内容不会执行
<select id="getEmpListByMoreTJ" resultType="Emp">
select * from t_emp where 1=1
<if test="ename != '' and ename != null">
and ename = #{ename}
</if>
<if test="age != '' and age != null">
and age = #{age}
</if>
<if test="sex != '' and sex != null">
and sex = #{sex}
</if>
</select>
2. where
where和if一般结合使用:
- 若where标签中的if条件都不满足,则where标签没有任何功能,即不会添加where关键字
- 若where标签中的if条件满足,则where标签会自动添加where关键字,并将条件最前方多余的and去掉
<select id="getEmpListByMoreTJ2" resultType="Emp"> select * from t_emp
<where>
<if test="ename != '' and ename != null">
ename = #{ename}
</if>
<if test="age != '' and age != null">
and age = #{age}
</if>
<if test="sex != '' and sex != null">
and sex = #{sex}
</if>
</where>
</select>
3. trim
trim用于去掉或添加标签中的内容
prefix
:在trim标签中的内容的前面添加某些内prefixOverrides
:在trim标签中的内容的前面去掉某些内suffix
:在trim标签中的内容的后面添加某些内suffixOverrides
:在trim标签中的内容的后面去掉某些内容
<select id="getEmpListByMoreTJ" resultType="Emp">
select * from t_emp
<trim prefix="where" suffixOverrides="and">
<if test="ename != '' and ename != null">
ename = #{ename} and
</if>
<if test="age != '' and age != null">
age = #{age} and
</if>
<if test="sex != '' and sex != null">
sex = #{sex}
</if>
</trim>
</select>
4. choose、when、otherwise
choose、when、 otherwise相当于if...else if..else
<!--List<Emp> getEmpListByChoose(Emp emp);-->
<select id="getEmpListByChoose" resultType="Emp"> select
<include refid="empColumns"></include> from t_emp
<where>
<choose>
<when test="ename != '' and ename != null">
ename = #{ename}
</when>
<when test="age != '' and age != null">
age = #{age}
</when>
<when test="sex != '' and sex != null">
sex = #{sex}
</when>
<when test="email != '' and email != null">
email = #{email}
</when>
</choose>
</where>
</select>
5. foreach
<!--int insertMoreEmp(List<Emp> emps);-->
<insert id="insertMoreEmp"> insert into t_emp values
<foreach collection="emps" item="emp" separator=",">
(null,#{emp.ename},#{emp.age},#{emp.sex},#{emp.email},null)
</foreach>
</insert>
<!--int deleteMoreByArray(int[] eids);-->
<delete id="deleteMoreByArray">
delete from t_emp where
<foreach collection="eids" item="eid" separator="or">
eid = #{eid}
</foreach>
</delete>
<!--int deleteMoreByArray(int[] eids);-->
<delete id="deleteMoreByArray">
delete from t_emp where eid in
<foreach collection="eids" item="eid" separator="," open="(" close=")">
#{eid}
</foreach>
</delete>
6. SQL片段
sql片段,可以记录一段公共sql片段,在使用的地方通过include标签进行引入。
<sql id="empColumns">
eid,ename,age,sex,did
</sql>
select
<include refid="empColumns"></include>
from t_emp
10. MyBatis的缓存
1. MyBatis的一级缓存
一级缓存是SqlSession级别的(默认开启),通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问。
使一级缓存失效的四种情况:
- 不同的SqlSession对应不同的一级缓存
- 同一个SqlSession但是查询条件不同
- 同一个SqlSession两次查询期间执行了任何一次增删改操作
- 同一个SqlSession两次查询期间手动清空了缓存
2. MyBatis的二级缓存
二级缓存是SqlSessionFactory级别(默认开启),通过同一个SqlSessionFactory创建的SqlSession查询的结果会被 缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取。
二级缓存开启的条件:
- 在核心配置文件中,设置全局配置属性
cacheEnabled="true"
,默认为true,不需要设置 - 在映射文件中设置标签
<cache/>
- 二级缓存必须在SqlSession关闭或提交之后有效
- 查询的数据所转换的实体类类型必须实现序列化的接口
使二级缓存失效的情况:
- 两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
3. 二级缓存的相关配置
在mapper配置文件中添加的cache标签可以设置一些属性:
eviction
缓存回收策略,默认的是 LRU- LRU(Least Recently Used):最近最少使用的——移除最长时间不被使用的对象
- FIFO(First in First out):先进先出——按对象进入缓存的顺序来移除它们
- SOFT:软引用——移除基于垃圾回收器状态和软引用规则的对象
- WEAK:弱引用——更积极地移除基于垃圾收集器状态和弱引用规则的对象
flushInterval
刷新间隔,单位毫秒- 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
size
引用数目,正整数- 最多可以存储多少个对象,太大容易导致内存溢出
readOnly
只读, true/false- true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势
- false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false