Mybatis详解(一)

框架介绍

三层架构

软件开发常用的三层架构,之所以流行是因为有这
清晰的任务划分,一般包括以下三层:

  • 持久层:主要完成与数据库相关的操作,即对数据库的增删改查,因为数据库访问的對象一般称为是Data Access Object (简称dao),所以有人把持久层叫做dao层。
  • 业务层:主要根据业务需求完成业务逻辑的定义和实现。因为它主要是对上层提供服务的,所以有人把业务层叫做service层或者Business层
  • 表现层:主要完成与最终软件使用用户的交互,需要有交互界面。
  • 因此有人把表现层称为是web层或者是View层。

三层架构之间的关系是:

表现层调用业务层,业务层调用持久层
各层之间必然要进行数据交互,我们一般使用java对象来实现传递数据。
Mybatis详解(一)_第1张图片

框架

什么是框架

框架就是一套规范,既然是规范,你使用这个框架就要遵守这个框架所规定的约束。
框架可以理解为半成品软件,框架做好以后,接下来在它基础上进行开发。

为什么要使用框架?

框架为我们封装好了一些冗余,且重用率低的代码。并且使用反射与动态代理机制,将代码实现了通用性,让 开发人员把精力专注在核心的业务代码实现上。
比如在使用servlet进行开发时,需要在servlet获取表单的参数,每次都要获取很麻烦,而框架底层就使用反射机制和拦截器机制帮助我们获取表单的值,使用jdbc每次做专一些简单的crud的时候都必须写sql,但使用框架就不需要这么麻烦了,直接调用方法就可以。当然,既然是使用框架,那么还是要遵循其一些规范进行配置

常見的框架:

Java世界中的框架非常的多,每一个框架都是为了解决某一部分或某些问题而存在的。下面列出在目前企业中流行的几种框架(一定要注意他们是用来解决哪一层问题的):
持久层框架:专注于解决数据持久化的框架。常用的有mybatis、hibernate、spring jdbc等等。
表现层框架:专注于解决与用户交互的框架。常见的有struts2、spring mvc等等。
全栈框架: 能在各层都给出解决方案的框架。比较著名的就是spring。

原始JDBC的操作分析

① 数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能
② sql 语句在代码中硬编码,造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变java代码。
③ 查询操作时,需要手动将结果集中的数据手动封装到实体中。

Mybatis简介

Mybatis是一个优秀的基于orm的半自动轻量级持久层框架,他对jdbc的操作数据库的过程进行封装,使开发者只需要关注sql本身,而不需要花费精力去处理例如注册驱动,创建connection,创建statement,手动设置参数,结果索引 資源釋放的繁雜代碼問題。

ORM思想

ORM(Object Relational Mapping)对象关系映射
O(对象模型):
实体对象,即我们在程序中根据数据库表结构建立的一个个实体javaBean
R(关系型数据库的数据结构):
关系数据库领域的Relational(建立的数据库表)
M(映射):
从R(数据库)到O(对象模型)的映射,可通过XML文件映射
Mybatis详解(一)_第2张图片
mybatis采用ORM思想解决了实体和数据库映射的问题,对jdbc 进行了封装,屏蔽了jdbc api 底层访问细节,使我们不用与jdbc api 打交道,就可以完成对数据库的持久化操作

代码实现

创建User数据表

CREATE DATABASE `mybatis_db`;
USE `mybatis_db`;
CREATE TABLE `user` (
`id` int(11) NOT NULL auto_increment,
`username` varchar(32) NOT NULL COMMENT '用户名称',
`birthday` datetime default NULL COMMENT '生日',
`sex` char(1) default NULL COMMENT '性别',
`address` varchar(256) default NULL COMMENT '地址',
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- insert....
insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (1,'子
慕','2020-11-11 00:00:00','','北京海淀'),(2,'应颠','2020-12-12 00:00:00','','北
京海淀');

导入Mybatis的坐标和其他相关坐标

<!--指定编码和版本-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<java.version>1.11</java.version>
<maven.compiler.source>1.11</maven.compiler.source>
<maven.compiler.target>1.11</maven.compiler.target>
</properties>
<!--mybatis坐标-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<!--mysql驱动坐标-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
<!--单元测试坐标-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

编写User实体

public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
// getter/setter 略
}

编写UserMapper映射文件

<?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="UserMapper">
<!--查询所有-->
<select id="findAll" resultType="com.lagou.domain.User">
select * from user
</select>
</mapper>

编写MyBatis核心文件

<?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="mysql">
<!--使用MySQL环境-->
<environment id="mysql">
<!--使用JDBC类型事务管理器-->
<transactionManager type="JDBC"></transactionManager>
<!--使用连接池-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver">
</property>
<property name="url" value="jdbc:mysql:///mybatis_db">
</property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</dataSource>
</environment>
</environments>
<!--加载映射配置-->
<mappers>
<mapper resource="com/lagou/mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>

编写测试类

@Test
public void testFindAll() throws Exception {
// 加载核心配置文件
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
// 获取SqlSessionFactory工厂对象
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(is);
// 获取SqlSession会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行sql
List<User> list = sqlSession.selectList("UserMapper.findAll");
for (User user : list) {
System.out.println(user);
}
// 释放资源
sqlSession.close();
}

Mybatis映射文件概述

Mybatis详解(一)_第3张图片

Mybatis增删改查

新增

编写配置文件UserMapper.xml

<!--新增-->
<insert id="save" parameterType="com.lagou.domain.User">
insert into user(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>

新增注意事项

  • 插入语句使用insert标签
  • 在映射文件中使用parameterType属性指定要插入的数据类型
  • Sql语句中使用#{实体属性名}方式引用实体中的属性值
  • 插入操作使用的API是sqlSession.insert(“命名空间.id”,实体对象);
  • 插入操作涉及数据库数据变化,所以要使用sqlSession对象显示的提交事务,即
    sqlSession.commit()

编写测试类

@Test
public void testSave() throws Exception{
//加载核心配置文件
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
// 获取SqlSessionFactory工厂对象
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(is);
// 获取SqlSession会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行sql
User user = new User();
user.setUsername("jack");
user.setBirthday(new Date());
user.setSex("男");
user.setAddress("北京海淀");
sqlSession.insert("UserMapper.save", user);
// DML语句,手动提交事务
sqlSession.commit();
// 释放资源
sqlSession.close();

}

修改

编写映射文件UserMapper.xml

<!--修改-->
<update id="update" parameterType="com.lagou.domain.User">
update user set username = #{username},birthday = #{birthday},
sex = #{sex},address = #{address} where id = #{id}
</update>

修改注意事项

  • 修改语句使用update标签
  • 修改操作使用的API是sqlSession.update(“命名空间.id”,实体对象);

删除

编写映射文件UserMapper.xml

<!--删除-->
<delete id="delete" parameterType="java.lang.Integer">
delete from user where id = #{id}
</delete>

编写测试类

@Test
public void testDelete() throws Exception {
// 加载核心配置文件
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
// 获取SqlSessionFactory工厂对象
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(is);
// 获取SqlSession会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行sql
sqlSession.delete("UserMapper.delete", 50);
// DML语句,手动提交事务
sqlSession.commit();
// 释放资源
sqlSession.close();
}

删除注意事项

- 删除语句使用delete标签
- Sql语句中使用#{任意字符串}方式引用传递的单个参数
- 删除操作使用的API是sqlSession.delete(“命名空间.id”,Object);

Mybatis核心文件概述

Mybatis核心配置文件层级关系

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
配置文档的顶层结构如下:
Mybatis详解(一)_第4张图片

常用配置解析

environments标签

Mybatis详解(一)_第5张图片

1.其中,事务管理器(transactionManager)类型有两种:
 -JDBC:
  这个配置就是直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。  
 -MANAGED:
  这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。
  例如:mybatis与spring整合后,事务交给spring容器管理。
  
2.其中,数据源(dataSource)常用类型有三种:
 -UNPOOLED:
 这个数据源的实现只是每次被请求时打开和关闭连接。
  
 -POOLED:
  这种数据源的实现利用“池”的概念将JDBC连接对象组织起来。  
 -JNDI:
 这个数据源实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的数据源引用

properties标签

jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql:///mybatis_db jdbc.username=root
jdbc.password=root

Mybatis详解(一)_第6张图片

typeAliases标签

类型别名是为 Java 类型设置一个短的名字。
为了简化映射文件 Java 类型设置,mybatis框架为我们设置好的一些常用的类型的别名:
Mybatis详解(一)_第7张图片
Mybatis详解(一)_第8张图片

mappers 标签

该标签的作用是加载映射的,加载方式有如下几种:

  1. 使用相对于类路径的资源引用,例如:
  2. 使用完全限定资源定位符(URL),例如:

    《下面两种mapper代理开发中使用:暂时了解》
  3. 使用映射器接口实现类的完全限定类名,例如:
  4. 将包内的映射器接口实现全部注册为映射器,例如:

    Mybatis详解(一)_第9张图片

Mybatis的API介绍

sqlSession工厂构建器sqlSessionFactoryBuilder

常用API:SqlSessionFactory build(InputStream inputStream)
通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象

String resource = "org/mybatis/builder/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);

其中, Resources 工具类,这个类在 org.apache.ibatis.io 包中。Resources 类帮助你从类路径下、文件系统或一个 web URL 中加载资源文件。
SqlSession工厂对象SqlSessionFactory
Mybatis详解(一)_第10张图片

Sqlsession会话对象

SqlSession 实例在 MyBatis 中是非常强大的一个类。在这里你会看到所有执行语句、提交或回滚事务
和获取映射器实例的方法。
Mybatis详解(一)_第11张图片
Mybatis基本原理介绍
Mybatis详解(一)_第12张图片

Mybatis的dao层的开发和使用

传统开发模式

编写UserMapper接口

public interface usermapper{

public List<User> findAll() throws Exception;

}

编写UserMapper实现

public class UserMapperImpl implements UserMapper {
@Override
public List<User> findAll() throws Exception {
// 加载配置文件
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
// 获取SqlSessionFactory工厂对象
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(is);
// 获取SqlSe会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行sql
List<User> list = sqlSession.selectList("UserMapper.findAll");
// 释放资源
sqlSession.close();
return list;

编写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">
<mapper namespace="UserMapper">
<!--查询所有-->
<select id="findAll" resultType="user">
select * from user
</select>
</mapper>

测试

@Test
public void testFindAll() throws Exception {
// 创建UserMapper 实现类
UserMapper userMapper = new UserMapperImpl();
// 执行查询
List<User> list = userMapper.findAll();
for (User user : list) {
System.out.println(user);
}
}

传统开发问题思考

1.实现类中,存在mybatis模板代码重复
2.实现类调用方法时,xml中的sql statement 硬编码到java代码中
思考:能否只写接口,不写实现类。只编写接口和Mapper.xml即可?
因为在dao(mapper)的实现类中对sqlsession的使用方式很类似。因此mybatis提供了接口的动态代
理。

代理开发方式

采用 Mybatis 的基于接口代理方式实现 持久层 的开发,这种方式是我们后面进入企业的主流。
基于接口代理方式的开发只需要程序员编写 Mapper 接口,Mybatis 框架会为我们动态生成实现类的对
象。
这种开发方式要求我们遵循一定的规范:
Mybatis详解(一)_第13张图片

@Test
public void testFindAll() throws Exception {
// 加载核心配置文件
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
// 获得SqlSessionFactory工厂对象
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(is);
// 获得SqlSession会话对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 获得Mapper代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 执行查询
List<User> list = userMapper.findAll();
for (User user : list) {
System.out.println(user);
}
// 释放资源
sqlSession.close();
}

Mybatis基于接口代理方式的内部执行原理

我们的持久层现在只有一层接口吗,而接口是不实际干活的,那么是谁在做实际的查询工作呢?

下面我们通过源码看一下:
1.通过追寻源码我们可以看到,我们是用的mapper实际上是一个代理对象,是由mapperProxy代理产生的。
Mybatis详解(一)_第14张图片
2.追寻MapperProxy的invoke方法会发现,其最终调用了mapperMethod.execute(sqlSession,args)
Mybatis详解(一)_第15张图片
3.进入execute方法会发现,最终工作的还是sqlSession
Mybatis详解(一)_第16张图片

Mybatis的高级查询

ResultMap属性

建立对象关系映射

  • resultType
    如果实体的属性名与表中字段名一致,将查询结果自动封装到实体类中
  • ResutlMap
    如果实体的属性名与表中字段名不一致,可以使用ResutlMap实现手动封装到实体类中

编写UserMapper接口

public interface UserMapper {
public List<User> findAllResultMap();
}

编写UserMapper.xml

<!--
实现手动映射封装
resultMap
id="userResultMap" 此标签唯一标识
type="user" 封装后的实体类型
<id column="uid" property="id"></id> 表中主键字段封装
column="uid" 表中的字段名
property="id" user实体的属性名
<result column="NAME" property="username"></result> 表中普通字段封装
column="NAME" 表中的字段名
property="username" user实体的属性名
补充:如果有查询结果有 字段与属性是对应的,可以省略手动封装 【了解】
-->
<resultMap id="userResultMap" type="user">
<id column="uid" property="id"></id>
<result column="NAME" property="username"></result>
<result column="PASSWORD" property="username"></result>
</resultMap>
<select id="findAllResultMap" resultMap="userResultMap">
SELECT id AS uid,username AS NAME,password AS PASSWORD FROM USER
</select>
<!--
实现手动映射封装
resultMap
id="userResultMap" 此标签唯一标识
type="user" 封装后的实体类型
<id column="uid" property="id"></id> 表中主键字段封装
column="uid" 表中的字段名
property="id" user实体的属性名
<result column="NAME" property="username"></result> 表中普通字段封装
column="NAME" 表中的字段名
property="username" user实体的属性名
补充:如果有查询结果有 字段与属性是对应的,可以省略手动封装 【了解】
-->
<resultMap id="userResultMap" type="user">
<id column="uid" property="id"></id>
<result column="NAME" property="username"></result>
<result column="PASSWORD" property="username"></result>
</resultMap>
<select id="findAllResultMap" resultMap="userResultMap">
SELECT id AS uid,username AS NAME,password AS PASSWORD FROM USER
</select>

多条件查询的三中方式

方式一:

使用 #{arg0}-#{argn} 或者 #{param1}-#{paramn} 获取参数
Mybatis详解(一)_第17张图片

方式二:

使用注解,引入 @Param() 注解获取参数
Mybatis详解(一)_第18张图片

方式三:

使用pojo对象传递参数
Mybatis详解(一)_第19张图片

模糊查询

需求是根据username模糊查询user表
方式一:
Usermapper接口:
public interface usermapper
{
public List finaAllByname(String username);
}
UserMapper.xml

<mapper namespace="com.lagou.mapper.UserMapper">
<select id="findByUsername1" parameterType="string" resultType="user">
select * from user where username like #{username}
</select>
</mapper>

测试

@Test
public void testFindByUsername() throws Exception {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> list = userMapper.findByUsername1("%玉%");
for (User user : list) {
System.out.println(user);
}
}

方式二:
Usermapper接口:
public interface Usermapper{
public List findAllByName2(String username);

}
Usermapper.xml

<mapper namespace="com.lagou.mapper.UserMapper">
<!--不推荐使用,因为会出现sql注入问题-->
<select id="findByUsername2" parameterType="string" resultType="user">
select * from user where username like '${value}'
</select>
</mapper>

测试:

@Test
public void testFindByUsername() throws Exception {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> list = userMapper.findByUsername2("%王%");
for (User user : list) {
System.out.println(user);
}
}
${} 与 #{} 区别【笔试题】

#{}表示一个占位符,通过#{}可以实现preparedStatement向占位符中设置值,自动新进行java的类型和jdbc的类型转换,#{}可以有效防止sql注入。
#{}可以结束简单类型的值或pojo的属性值
如果parameterType传输单个简单类型值,#{}括号内的内容随便写。
表示拼接字符串通过 {}表示拼接字符串 通过 表示拼接字符串通过{}可以接受简单的类型值或者pojo的属性值如果parameterType传输单个简单类型值,${}括号中只能是value。在上述代码中也可以看到。

Mybatis映射文件深入

返回主键

应用场景:我们很多时候有这种需求,像数据库插入一条数据后,希望能里见到这条数据在数据库中的主键值

useGenerateKeys

piblic interface Usemapper{
//返回主键
public void save(User user);
}
<!--
useGeneratedKeys="true" 声明返回主键
keyProperty="id" 把返回主键的值,封装到实体的id属性中
注意:只适用于主键自增的数据库,mysql和sqlserver支持,oracle不支持
-->
<insert id="save" parameterType="user" useGeneratedKeys="true" keyProperty="id">
INSERT INTO `user`(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>

selectKey

public interface UserMapper {
// 返回主键
public void save(User user);
}
<!--
selectKey 适用范围广,支持所有类型数据库
keyColumn="id" 指定主键列名
keyProperty="id" 指定主键封装到实体的id属性中
resultType="int" 指定主键类型
order="AFTER" 设置在sql语句执行前(后),执行此语句
-->
<insert id="save" parameterType="user">
<selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID();
</selectKey>
INSERT INTO `user`(username,birthday,sex,address)
values(#{username},#{birthday},#{sex},#{address})
</insert>

测试代码:
@Test
public void testSave() throws Exception{
Usermapper usermapper =sqlSession.getMapper(Usermapper.class);
User user=new User();
user.setUsername(“付玉”);
user.setAddress(“美国”);
user.setBirthday(new Date());
user.setSex(“nv”) ;
userMapper.save(user);
System.out.println(“返回主键:”+user.getid());
}

动态sql之If

应用场景
当我们要根据不同的条件,来执行不同的sql语句的时候,需要用到动态SQL。

需求:根据id和Username查询,但是不确定俩个都有值

userMapper接口

public List findByIdAndUsernameIf(User user);

UserMapper.xml映射文件

<!--
where标签相当于是where 1=1 ,但是如果没有条件,就不会拼接where关键字
-->
<select id="findByIdAndUsernameIf" parameterType="user" resultType="user"
SELECT * FROM 'user' <where>
<if test="id!= null">
And id = #{id}
</if>
<if test = "username != null">
AND username = #{username}
</if>
</where>
 /select>

动态sql之set

需求:动态更新user表数据,如果该属性有值就更新,没有就不做处理。
Usermapper接口:
public void updateIf(User user);

UserMapper.xml映射文件
<!--
set标签在更新的时候,自动加上set关键字,然后去掉最后一个条件的逗号
-->
<update id="updateIf" parameterType="user">
UPDATE `user`
<set>
<if test="username != null">
username = #{username},
</if>
<if test="birthday != null">
birthday = #{birthday},
</if>
<if test="sex !=null">
sex = #{sex},
</if>
<if test="address !=null">
address = #{address},
</if>
</set>
WHERE id = #{id}
</update>

动态sql之foreach

foreach主要是用来做数据的循环遍历

Mybatis详解(一)_第20张图片

UserMapper接口

public List findByList(List ids);

userMapper.xml映射文件

如果查询条件为普通类型 List集合,collection属性值为:collection 或者 list

<!--
如果查询条件为普通类型 List集合,collection属性值为:collection 或者 list
-->
<select id="findByList" parameterType="list" resultType="user" >
SELECT * FROM `user`
<where>
<foreach collection="collection" open="id in(" close=")" item="id"
separator=",">
#{id}
</foreach>
</where>
</select>

如果查询条件为普通类型 Array数组,collection属性值为:array

<select id="findByArray" parameterType="int" resultType="user">
SELECT * FROM `user`
<where>
<foreach collection="array" open="id in(" close=")" item="id"
separator=",">
#{id}
</foreach>
</where>
</select>

测试代码:

// foreach标签 array
@Test
public void testFindByArray() throws Exception {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Integer[] ids = {46, 48, 51};
List<User> list = userMapper.findByArray(ids);
System.out.println(list);
}

sql片段

应用场景:
映射文件中可以将重复的sql提取出来,使用时使用include引用即可,最终达到sql重用的目的

<!--抽取的sql片段-->
<sql id="selectUser">
SELECT * FROM `user`
</sql>
<select id="findByList" parameterType="list" resultType="user" >
<!--引入sql片段-->
<include refid="selectUser"></include>
<where>
<foreach collection="collection" open="id in(" close=")" item="id"
separator=",">
#{id}
</foreach>
</where>
</select>
<select id="findByArray" parameterType="integer[]" resultType="user">
<!--引入sql片段-->
<include refid="selectUser"></include>
<where>
<foreach collection="array" open="id in(" close=")" item="id"
separator=",">
#{id}
</foreach>
</where>
</select>

Mybatis核心配置文件深入:

plugins标签

Mybatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进封装,使用简单的方式即可获得分页的相关数据
开发步骤:

  • 导入通用的PageHelper的坐标
  • 在mybatis核心配置文件中配置PageHelper插件
  • 测试分页数据获取

①导入通用PageHelper坐标

<!-- 分页助手 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>

②在mybatis核心配置文件中配置PageHelper插件

<!-- 分页助手的插件 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 指定方言 -->
<property name="dialect" value="mysql"/>
</plugin>

③测试分页代码实现

@Test
public void testPageHelper(){
//设置分页参数
PageHelper.startPage(1,2);
List<User> select = userMapper2.select(null);
for(User user : select){
System.out.println(user);
}
}

Mybatis详解(一)_第21张图片

mybatis一级缓存

1、一级缓存简介
一级缓存作用域是sqlsession级别的,同一个sqlsession中执行相同的sql查询(相同的sql和参数),第一次会去查询数据库并写到缓存中,第二次从一级缓存中取。

一级缓存是基于 PerpetualCache 的 HashMap 本地缓存,默认打开一级缓存。

2、何时清空一级缓存
如果中间sqlSession去执行commit操作(执行插入、更新、删除),则会清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

一级缓存时执行commit,close,增删改等操作,就会清空当前的一级缓存;当对SqlSession执行更新操作(update、delete、insert)后并执行commit时,不仅清空其自身的一级缓存(执行更新操作的效果),也清空二级缓存(执行commit()的效果)。

3、一级缓存无过期时间,只有生命周期
MyBatis在开启一个数据库会话时,会创建一个新的SqlSession对象,SqlSession对象中会有一个Executor对象,Executor对象中持有一个PerpetualCache对象,见下面代码。当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。

mybatis二级缓存

1、二级缓存简介
它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。

二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。

2、二级缓存何时存入
在关闭sqlsession后(close),才会把该sqlsession一级缓存中的数据添加到namespace的二级缓存中。

开启了二级缓存后,还需要将要缓存的pojo实现Serializable接口,为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样,不一定只存在内存中,有可能存在硬盘中。

3、二级缓存有过期时间,但没有后台线程进行检测
需要注意的是,并不是key-value的过期时间,而是这个cache的过期时间,是flushInterval,意味着整个清空缓存cache,所以不需要后台线程去定时检测。

每当存取数据的时候,都有检测一下cache的生命时间,默认是1小时,如果这个cache存活了一个小时,那么将整个清空一下。

4、当 Mybatis 调用 Dao 层查询数据库时,先查询二级缓存,二级缓存中无对应数据,再去查询一级缓存,一级缓存中也没有,最后去数据库查找。

你可能感兴趣的:(数据库,mybatis,java,spring)