MyBatis笔记

本文是作者学习MyBatis的详细笔记,涵盖了MyBatis的介绍、项目配置、CRUD操作、配置解析、生命周期、日志、分页、动态SQL、缓存、错误汇总等内容。MyBatis是一款优秀的持久层框架,简化了JDBC代码,支持自定义SQL、存储过程和高级映射。文章还讨论了MyBatis的优缺点,以及如何在项目中使用和配置,包括Maven依赖、主配置文件、日志配置、分页实现等,并对常见错误进行了总结。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MyBatis知识汇总

简介

本篇文章详细介绍了mybatis从入门到精通的全部内容。也是个人学习MyBatis的笔记。文章中有很多作者的个人理解。如有错误,欢迎评论

Mybatis

前言

1.1 什么是MyBatis ?

​ MyBatis 是一款优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。MyBatis免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作。MyBatis可以通过简单的XML 或注解来配置和映射原始类型、接口和Java POJO (Plain Old Java Objects,普通老式Java对象)为数据库中的记录。

1.1.1 Maven依赖
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>
1.1.2 中文文档

https://mybatis.org/mybatis-3/zh/index.html

1.2 持久化

数据持久化

  • 持久化就是把程序的数据在持久状态和瞬时状态转化的过程
  • 内存:断电即失
  • 持久化方式 :数据库(JDBC),IO文件持久化
  • 生活:罐头,冷藏

为什么需要持久化

  • 有一些对象不能让他丢掉。

  • 内存比硬盘贵

1.3 持久层

​ 完成持久化工作的代码块

​ 层界限非常明显

1.4 为什么需要Mybatis?

  • 帮助开发人员将数据存入到数据库中

    • 方便
    • 传统的JDBC代码太复杂了,Mybatis 可以简化,自动化。
  • 优点:

    • 简单易学
    • 灵活
    • sql和代码的分离,提高可维护性
    • 提供映射标签,支持对象与数据库的ORM字段关系映射
    • 提供对象关系映射标签,支持对象关系组建维护
    • 提供xml标签,支持编写动态sql。

最重要的一点:使用的人多!

2 Mybatis项目

2.1 导入jar 包

2.2 创建主配置文件

创建 mybatis-config.xml 文件

<!-- 导入mybatis dtd 文档类型声明文件 -->
<!DOCTYPE configuration
 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 核心配置元素-->
<configuration>
    <!--环境子元素 -->
    <environments default = "mysql">
    	<environment id = "mysql">
            <!-- 事务管理器-->
            <transactionManager type = "JDBC"/>
            <!-- 数据源 -->
            <dataSource type = "POOLED">
                <property name = "driver" value =""/>
                <property name = "url" value =""/>
                <property name = "username" value =""/>
                <property name = "password" value =""/>
            </dataSource>
        </environment>
    </environments>
</configuration>

2.3 创建Mybatis工具类

//创建 Sql 会话工厂
SqlSessionFactory sqlSessionFactory; 
//初始化
static{
    String resource = "mybatis-config.xml";
	//获取资源
    InputSteam is = Resources.getResourceAsStream(resource);
    //创建工厂
    sqlSessionFactory = SqlSessionFactoryBuilder.build(is);
}

public static SqlSession openSession(){
    //使用工厂 创建Sql会话(默认不提交)
    return sqlSessionFactory.openSession();
}

2.4 编写Dao&POJO

  • 创建 实体类
  • 创建 mapper接口(映射) (Dao接口)

2.5 创建Mapper 配置文件

<!--引入mapper DTD 文档类型声明文件 -->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--命名空间对应UserMapper接口的全限定类名 -->
<mapper namespace="com.mybatis.mapper.UserMapper">
<!--    id必须要对应方法名,resultType 对应POJO 全限定类名/别名-->
    <select id = "selectByColumn" resultType="com.mybatis.pojo.user">
    	<!-- Sql语句 其中 ${}表示拼接  #{}预编译-->
        <!-- 其中${arg0}会被直接替换,而#{arg1}会使用 ? 预处理. -->
        SELECT * FROM `user` WHERE ${arg0} = #{arg1};
    </select>
</mapper>

接口实现类由原来的UserDaoImpl 转换为一个Mapper配置文件

  • 注:安全起见 一般我们把需要传值的地方都使用#{}来进行占位。
  • ${}的应用场景一般是 传入字段名…等等

上面介绍的只是一种简单的mapper 文件映射 。后续我们将深入讲解Mapper的更多玩法。

3 CRUD

3.1 namespace

  • namespace 一般起名为mapper接口的全限定类名。

  • 使用动态代理时。namespace必须跟mapper接口全限定名一致。

<mapper namespace="com.mybatis.dao.UserMapper"></mapper>
3.2 select
<select id="selectAll" resultType="User">
    SELECT * FROM `user`
</select>
3.3 insert
 <insert id="insertUser">
     INSERT INTO `user`(`name`,`role_id`)
     values
     <foreach collection="map" separator="," item="user">
         (#{user.af},#{user.a})
     </foreach>
</insert>
3.4 update
 <update id="updateUser">
     update `user` set `name` = #{name} where `id`= #{id}
</update>
3.5 delete
<delete id="deleteUser">
    delete from `user` where `id`= #{id}
</delete>
3.6 map

传入的参数会统统被转换成map

  • 传入对象时 ,set方法 和字段 会成为map的kay。return的结果和字段的内容会成为map的value。
  • 传入单个值时,内容会被当作value。
  • 传入map时,与map 键值对相对。
3.7拓展篇
  • 模糊查询

    ​ java代码实现

    • List<User> userList = mapper.getUserLike("%李%")
      

      配置文件实现

    • select * form user where name like concat('%',#{value},'%')
      select * form user where name like "%"#{value}"%"
      

4 配置解析

4.1 核心配置文件(configuration)

  • mybatis-config.xml

  • MyBatis的配置文件包含了会深深影响MyBatis行为的设置和属性信息。

  • 其书写顺序必须跟下面对应

    configuration(配置)
    properties(属性)
    settings(设置)
    typeAliases(类型别名)
    typeHandlers(类型处理器)
    objectFactory(对象工厂)
    plugins(插件)
    environments(环境配置)
    environment(环境变量)
    transactionManager(事务管理器)
    dataSource(数据源)
    databaseIdProvider(数据库厂商标识)
    mappers(映射器)
    
4.1.1 properties
<properties resource="org/mybatis/example/config.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>

优先级 resource > property

读取配置文件,其他地方使用 ${name}来获取值

4.1.2 settings
<settings>
    <!--全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。-->
  <setting name="cacheEnabled" value="true"/>
    <!--延迟加载的全局开关-->
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
    <!--自动映射级别 NONE 不自动映射 PARTIAL不会自动映射复杂关系,FULL全部映射 -->
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
    <!--localCacheScope-->
    <!--
			MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。
	-->
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
        <!--指定 MyBatis 所用日志的具体实现-->
    <setting name = "logImpl" value = "LOG4j"/>
        <!--是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
    <etting name = "mapUnderscoreToCamelCase" value = "true"/>
</settings>
4.1.3 typeAliases
 <typeAliases>
     <package name="com.mybatis.pojo"/>
     <typeAliase alias="Blog" type="com.mybatis.pojo.Blog"/>
</typeAliases>
  • typeAliase 用于指定某个类的别名 type属性类的位置,name属性 类的别名
  • pakeage 用于指定某个包类的别名。 默认别名 为类的首字母小写的非限定类名作为别名。如果指定注解 @Alias(“blog”) 则按注解来设置别名
4.1.4 environments
 <environments default="mysql">
        <environment id="mysql">
            <!--菅江晖-->
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="jdbc:mysql://localhost:3306/userdb"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
  • environment (环境) 可以配置多个 ,写进 environments里面 但只能指定一个作为默认的。 environments的default属性 内容为 environment的id属性
  • transactionManager (事务管理器) ,属性type取值为 JDBC|MANAGED
    • JDBC 直接使用JDBC的提交或回滚
    • MANAGED 这个配置几乎没做什么。
  • dataSource (数据源)属性type取值为 UNPOOLED POOLED|JNDI 属性property 用于设置属性
    • UNPOOLED 不使用池 每个请求都打开和关闭连接
    • POOLED 使用mybatis自身的池。
    • JNDI 使用JNDI上下文中的引用。
4.1.5 mappers

映射器 mappres

MapperRegistry 映射注册处 可以理解为 存放mapper 的容器 。简称 mapper仓库

常用三种方式:

  • 方式1 resource

  •     <mappers>
            <mapper resource = "com/mybatis/mapper/UserMapper.xml"/>
        </mappers>
    
  • 方式2 class

  • <mappers>
    	<mapper class = "com.mybatis.mapper.UserMapper"/>
    </mappers>
    
  • 方式3 扫描包

  • <mappers>
    	<package name = "com.mybatis.mapper" />
    </mappers>
    

方式1很好理解 通过xml 文件的方式来映射至仓库。

方式2 … 首先说一点 mybatis的降维处理做的真的服 ,从crud中的 set/get取不到会降维到去寻找字段!

  • class中指定类,如果同包内存在statement相同的xml 还会去加载xml一样。

方式2一般用于注解开发中。

方式3 和方式2相似。就是缩减了写class 的次数。

4.1.6 其他配置

​ 自行百度

5 声明周期和作用域

生命周期作用域 是至关重要的。因为错误的使用会导致非常严重的并发问题

5.1SqlSessionFactoryBuilder

作用域局部 ,其存在的意义是为了创建SqlSessionFactory.

5.2 SqlSessionFactory

作用域全局,程序运行过程中需要不断通过它来创建SqlSession。

说白了就是创建Sql 连接的东西。连接池

5.3 SqlSession

作用域局部。SqlSession可以提交 回滚,关闭,类似于数据库中的一个连接。 用完以后一定要关闭。

  • SqlSession 实例不是线程安全的。

  • 事务4大特征 原子性 唯一性 隔离性 持久性,这也是不能定义到全局的理由之一

  • 如果将此对象放置全局。则多线程情况下会造成很严重的错误,两个同时进来的方法一个已经提交并关闭了。另一个操作时就会发生异常。

6 日志

​ logImpl 默认未设置

​ 常用值 LOG4J(log4j日志)、STDOUT_LOGGING (控制台日志).

6.1 log4j

log4j 是Apache 的一个开源项目,通过使用log4j,我们可以控制日志信息输出的目的地是控制台、文件、GUI组件…

  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志地生成过程。
  • 通过一个配置文件来灵活地进行配置,无需修改代码。

常用设置 appeder 追加器

  • logger 记录器

  • rootLogger 根记录器

7 分页

7.1 limit

sql实现分页

select * from user limit 0,5

历史

select * from user limit 0,-1

在mysql的历史版本中, limit 第二个参数为-1时,会查询到最后一个数据(这是一个bug)

7.2 RowBounds

已过时不推荐使用!!

RowBounds rowBounds = new RowBounds(1,2);
List<User> userList = sqlSession.selectList("com.baqn.dao.UserMapper.selectALL",null,rowBounds);

7.3 分页插件

pagehelper 分页插件

了解即可

7.9999 注解

8.1 面向接口编程

优点: 解耦可拓展提高复用分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好。

关于接口的理解

定义(规范,约束)与实现(实现定义)的分离

接口的本身反映了系统设计人员对系统抽象理解。

首先并不推荐使用注解来写sql “注解一时爽,维护火葬场” ,其次是使用注解实现动态sql会非常非常麻烦。

官方解释"使用注解来映射简单语句会使代码显得更加简洁,淡然对于稍微复杂一点的语句,java注解就力不从心了,并且会显得更加混乱。因此,如果你需要完成很复杂的事情,那么最好使用xml来映射语句。"

  • java代码
    /**
     * 查询所有用户
     * @return 用户集合
     */
    @Select("select * from user")
    List<User> selectAll();
  • 配置文件
 <mapper class="com.mybatis.dao.UserDao"/>

8 复杂映射

以下内容

多对一

多个宠物拥有同一个主人,此时的关系便是 多对一 ,类似于

  • java
class Post{
    ...
    String name;
}
//宠物
class pet{
    String name;
    //主人
    Post post;
}
//多个宠物 list中的宠物post为同一个主人,此时便是 多对1
List<Pet> pets;

但对于其中一个宠物而言,它和主人的关系是1对1。

  • DDL
create table post(
    post_id int,
	name varchar(20),
    ...
);
create table pet(
    pet_id int,
	name varchar(20),
    post_id int(20),
    ...
);
一对一

一个人只能拥有一位伴侣,此时不管相对于谁,他们的伴侣只能是另一个人

  • java
class People{
    String name;
}
class Woman extends PeoPle{
    Man man;
}
class Man extends PeoPle{
    WoMan woMan;
}

WoMan p1;
Man p2;
p1.man =p2 ;
p2.woMan=p1;

  • DDL
create table people{
	p_id int,
	...,
	ref_p_id int unique key
}

or

create table people{
p_id int,
...
}
create table lovers{
	l_id int,
	man_id int unique key,
	woman_id int unique key
}

一个人只有一个身份证

  • java
class User{
    string identityCode;
}
  • DDL
create table user(
	...,
    identityCode varchar(20)
);
多对多

一个老师可以给多个学生讲课,一个学生可以有多个老师。

当相对于其中一个老师而言。他可以拥有多个学生。

相对于其中一个学生而言。他可以拥有多个老师。

  • java
Class Student{
String name;
List<Teacher> teachers;
}

Class Teacher{
String name;
List<Student> students;
}
//'
Student stu1 = new Student();
//学生的老师
stu1.teachers = new ArrayList<Teacher>(16);
//创建第一个老师
Teacher tea1 = new Teacher();
//老师的学生
tea1.students = new ArrayList<Student>(32);
//老师的第一个学生为...stu1
tea1.students[0] = stu1;
....
//学生的第一个老师为...tea1
stu1.teachers[0] = tea1;
....
//此时有一堆学生
List<Student> students =new ArrayList<Student>();
students[0] = stu1;
.....
    

  • DDL
....
create table relation(
	id int ,
    student_id int,
    teacher_id int,
);

一对多

一个主人可以拥有多个宠物

  • java
class user{
    String name;
    List<pet> pets;
}

DDL跟 上面的 一对多一致。

注:

  • 以上示例中包含此用法但不仅局限于此用法

  • 一般处于性能考虑 对象 会基于 所需要的数据来设计。以上的对象如果特别大,特别多的情况下,是非常占用内存的。

  • 以上为个人理解,如有错误,欢迎矫正!

一对一

assocation

  • 嵌套查询
<resultMap type="Commodity" id="Commodity">
    <id property="id" column="id" />
    <result property="name" column="name" />
    <result property="price" column="price" />
    <association property="type"
                 select="com.mybatis.dao.CommTypeMapper.selectCommTypeById"
                 column="typeId">
    </association>
</resultMap>
  • 联合查询
<resultMap type="Commodity" id="Commodity">
    <id property="id" column="id" />
    <result property="name" column="name" />
    <result property="price" column="price" />
    <association javaType="CommType">
        <id property = "id" column = "t_id"/>
        <result property = "name" column = "t_name"/>
    </association>
</resultMap>

一对多

collection

  • 嵌套查询
<resultMap type="User" id="User">
    <id property="id" column="id" />
    <result property="name" column="name" />
    <collection property="pets"
                 select="com.mybatis.dao.PetMapper.selectPetByUser"
                 column="pet_id">
    </collection>
</resultMap>
  • 联合查询
<resultMap type="User" id="User">
    <id property="id" column="id" />
    <result property="name" column="name" />
    <collection property="pets" ofType="Pet">
        <id property = "id" column = "p_id"/>
        <result property = "name" column ="p_name"
    </collection>
</resultMap>

javaType 用于 assocation 。用于映射java对象类型

ofType 用于collection 。用于映射集合泛型类型。

9 动态SQL

“动态sql就是指不同的条件生成不同的sql语句”——秦疆

动态SQL元素和JSTL或基于类似XML的文本处理器相似。在MyBatis之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于OGNL 的表达式来淘汰其他大部分元素。——MyBatis官方

9.1 if

多条件判断 多出口 test 表达式

<insert id="insertDept" parameterType="SysDept">
    insert into sys_dept(
    <if test="deptId != null and deptId != 0">dept_id,</if>
    <if test="parentId != null and parentId != 0">parent_id,</if>
    <if test="deptName != null and deptName != ''">dept_name,</if>
    <if test="ancestors != null and ancestors != ''">ancestors,</if>
    <if test="orderNum != null and orderNum != ''">order_num,</if>
    <if test="leader != null and leader != ''">leader,</if>
    <if test="phone != null and phone != ''">phone,</if>
    <if test="email != null and email != ''">email,</if>
    <if test="status != null">status,</if>
    <if test="createBy != null and createBy != ''">create_by,</if>
    create_time
    )values(
    <if test="deptId != null and deptId != 0">#{deptId},</if>
    <if test="parentId != null and parentId != 0">#{parentId},</if>
    <if test="deptName != null and deptName != ''">#{deptName},</if>
    <if test="ancestors != null and ancestors != ''">#{ancestors},</if>
    <if test="orderNum != null and orderNum != ''">#{orderNum},</if>
    <if test="leader != null and leader != ''">#{leader},</if>
    <if test="phone != null and phone != ''">#{phone},</if>
    <if test="email != null and email != ''">#{email},</if>
    <if test="status != null">#{status},</if>
    <if test="createBy != null and createBy != ''">#{createBy},</if>
    sysdate()
    )
</insert>

9.2 choose

多条件判断 单出口。类似于java switch

<select id="selectCommodiByCommodity" resultMap="Commodity"  parameterType="Commodity">
    <include refid="sqlSelectAll"></include>
    <where>
        <choose>
             <!--条件-->
            <when test="name != null and name != '' ">AND `name` LIKE concat('%',#{name},'%')</when>
            <when test = "type != null and type.id != null and type.id != 0">AND `typeId` = #{type.id}</when>
            <when test = "price != null and price != ''">AND `price` = #{price}</when>
            <!--除此之外-->
            <otherwise></otherwise>
        </choose>
    </where>
</select>

9.3 where

对应sql where

<select id="selectDictTypeList" parameterType="SysDictType" resultMap="SysDictTypeResult">
    <include refid="selectDictTypeVo"/>
    <where>
        <if test="dictName != null and dictName != ''">
            AND dict_name like concat('%', #{dictName}, '%')
        </if>
        <if test="status != null and status != ''">
            AND status = #{status}
        </if>
        <if test="dictType != null and dictType != ''">
            AND dict_type like concat('%', #{dictType}, '%')
        </if>
        <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
            and date_format(create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
        </if>
        <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
            and date_format(create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
        </if>
    </where>
</select>

9.4 set

对应sql set

<update id="updateDictType" parameterType="SysDictType">
 		update sys_dict_type
    <set>
        <if test="dictName != null and dictName != ''">dict_name = #{dictName},</if>
        <if test="dictType != null and dictType != ''">dict_type = #{dictType},</if>
        <if test="status != null">status = #{status},</if>
        <if test="remark != null">remark = #{remark},</if>
        <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
        update_time = sysdate()
    </set>
    where dict_id = #{dictId}
</update>

9.5 trim

灵活的可以在语句前,语句后,和废除指定前缀,废除指定后缀。

prefix 前缀

suffix 后缀

prefixOverrides 覆写指定前缀

suffixOverrides 覆写指定后缀

可以代替 set、where

  • set
<update id="updateDictType" parameterType="SysDictType">
 		update sys_dict_type
    <trim prefix="SET" suffixOverrides=",">
        <if test="dictName != null and dictName != ''">dict_name = #{dictName},</if>
        <if test="dictType != null and dictType != ''">dict_type = #{dictType},</if>
        <if test="status != null">status = #{status},</if>
        <if test="remark != null">remark = #{remark},</if>
        <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
        update_time = sysdate()
    </trim>
    where dict_id = #{dictId}
</update>
  • where
<select id="selectDictTypeList" parameterType="SysDictType" resultMap="SysDictTypeResult">
    <include refid="selectDictTypeVo"/>
    <trim prefix="WHERE" prefixOverrides="AND|OR">
        <if test="dictName != null and dictName != ''">
            AND dict_name like concat('%', #{dictName}, '%')
        </if>
        <if test="status != null and status != ''">
            AND status = #{status}
        </if>
        <if test="dictType != null and dictType != ''">
            AND dict_type like concat('%', #{dictType}, '%')
        </if>
        <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
            and date_format(create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
        </if>
        <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
            and date_format(create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
        </if>
    </trim>
</select>

9.6 foreach

open 开头,separator 分隔,close结尾, item 集合元素别名,collection 指定集合

<delete id="deleteLogininforByIds" parameterType="String">
    delete from sys_logininfor where info_id in
    <foreach collection="array" item="infoId" open="(" separator="," close=")">
        #{infoId}
    </foreach> 
</delete>
<insert id="batchUserRole">
    insert into sys_user_role(user_id, role_id) values
    <foreach item="item" index="index" collection="list" separator=",">
        (#{item.userId},#{item.roleId})
    </foreach>
</insert>

10 缓存

10.1 简介

//查询 连接数据库,耗资源。
//一次查询的结果,给他暂存在一个可以直接取到的地方!-->内存:暂存。
//我们再次查询数据的实话,直接走缓存,就不用走数据库了

什么是缓存[Cache]?

  • 存在内存的临时数据。
  • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率。

什么样的数据能使用缓存?

  • 经常查询并且不经常改变的数据。

10.2 MyBatis缓存

  • MyBatis包含一个非常强大的查询缓存特征,它可以非常方便的定制和配置缓存,缓存可以极大的提高查询效率。
  • MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
    • 默认情况下,只有以及缓存开启。(SqlSession级别的缓存,也称为本地缓存)
    • 二级缓存需要手动开启和配置,他是基于namespace级别缓存。
    • 为了提高拓展性,MyBatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存。
10.2.1 一级缓存
  • 一级缓存也叫本地缓存
    • 与数据库同一次会话期间查询到的数据会放在本地缓存中。
    • 如果以后需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库。

作用于SqlSession

MyBatis 缓存 一级缓存命中条件:

MyBatis 缓存 一级缓存命中条件
	1.必须是同一个会话(因为一级缓存的是会话级缓存)
	2.必须是同一个statement(类名/namespace,方法名相同/mapper CRUD id) 
	3.行的返回范围必须一样
	4.没有手动清空缓存 clearCache commit rollback	
	5.没有执行update方法
	6.未配置flushCache = true
	7.缓存作用域不是 STATEMET

注:一级缓存是默认开启的,只有一次SqlSession中有效.

10.2.2 二级缓存

二级缓存也叫全局缓存

只有缓存执行器提交时才会存到二级缓存

作用于namespace

使用此功能 必须要设置缓存开启

<setting name = "cacheEnabled" value = "true"/>

然后在开启缓存的mapper 中添加

<cache/> 

也可以自定义一些参数

<cache eviction="FIFO"
      lushInterval="interval"
       size="512"
       readOnly="true"/>

小結

  • 只要开启了二级缓存,在同一个Mapper下就有效
  • 所有的数据都会先放在一级缓存里
  • 只有当会话提交,或者关闭的时候,才会提交到二级缓存中。

10.5 源码分析

着重讲解 SQL会话 SqlSession 和 执行器 Executor
SqlSession 门面模式 ,API
它提供一个 基本API(CRUD),辅助API(提交,关闭会话)
他仅仅只是一个接口 提供一个api,它并不提供实现。他的具体处理交给执行器,executor

Executor 
	基本功能 update select,维护缓存
	辅助API 提交,关闭执行器,批处理刷新


	执行器实现一
		简单执行器 
			SimpleExecutor
		预编译执行器
			ReuseExecutor

		批处理执行器
			BatchExecutor
				他们三个有个共同父类 BaseExecutor 它可以用来 获取链接 、维护一级缓存  
				独有query 方法 这个方法首先会先去缓存查找如果不存在,则才会使用子类中的doQuery方法
				独有insert 方法 这个方法修改前也会先去更新缓存 再去使用子类中的doInsert方法修改数据库
		二级缓存执行器
		cachingExecutor实现了 二级缓存的功能 他继承于 Executor 接口并没有继承 BaseExecutor 。它只关心二级缓存相关操作
			cachingExecutor,它里面还有个Executor 用来执行数据库和一级缓存相关逻辑

11 错误汇总

PersistenceException 持久化异常…

BuilderException 构建异常 …

ReflectionException 反射异常…

BindingException(数据捆绑) 注入异常…

SQLSyntaxErrorException SQL 语句错误异常

ExpressionSyntaxException 表达式语法错误

11.1 Could not find resoure

资源未找到所导致的异常…

  • getResource时资源路径写错了 或者 是主配置文件 mapper 中的resource写错了
11.2 Cannot find class

没有找到class…

  • 去看看你包导入了吗或者看看你类名是不是写错了
11.3 Mapped Statements collection does not contain value for …

Mapped Statements collection中含指定内容 (key、value) 使用getMepperStatement()方法来获取不存在的的statement id 时会报此类异常

  • 去看看statement id 是否存在于配置文件

例: Mapped Statements collection does not contain value for 123.electAll

  • 值 123.electAll 在 Mapped Statements collection(映射声明集) 中不存在
11.4 Type ${interface} is not known to the MapperRegistry.

所指定的接口类型在映射仓库中不存在 使用动态代理获取指定接口时会遇到这种异常

  • 你写的mapper配置文件是否对应接口,去看看 mapper 的namespace

例: Type interface com.mybatis.dao.UserMapper is not known to the MapperRegistry.

  • 接口类型 com.mybatis.dao.UserMapper 不存在于映射仓库
11.5 Type ${interface} is already known to the MapperRegistry.

所指定的接口类型在映射仓库中已存在

  • 添加映射仓库中不存在的接口类型
11.6 Invalid bound statement (not found): …

非法范围声明(未找到) 使用动态代理执行接口方法时会遇到这种异常

  • 检查 mapper 文件中CRUD id 是否与 接口方法名相同。

例: Invalid bound statement (not found): com.mybatis.dao.UserMapper.selectAll

  • 指定的 com.mybatis.dao.UserMapper.selectAll 在 声明范围中不存在
11.7 Mapping is missing column attribute for property …

使用resultMap 在映射属性中找不到 column 属性 … 使用resultMap映射类时,未指定cloumn 会抛出此异常

  • 写出 column 属性即可

例:

 <resultMap type="User" id="User">
     <id />
     <result property="id" />
 </resultMap>

上面两种 都会抛出此异常

11.8 Cannot determine value type from string ‘${str}’

无法从字符串${str}中确定值类型

cloumn 映射至 POJO字段时,属性类型对不上。或者自动映射出现问题

  • 手动指定类型/检查映射字段是否符合java类型要求
11.9 There is no setter for property named ‘{name}’ in class '{class}'

在 {class} 类 中找不到属性为{name} 的set方法 使用resultMap 指定映射property值时 对不上pojo的set方法和字段

  • 写出set方法 或者 将映射property的值设置为 pojo 拥有的set方法名

  • 你知道吗? mybatis 会先去寻找 名称为 n a m e 的 s e t 方 法 。 找 不 到 时 会 去 寻 找 名 称 为 {name} 的set方法。找不到时会去寻找 名称为 nameset{name}的字段 如果还是找不到才会抛出这个异常。11.10同理

11.11 There is no getter for property named ' n a m e ′ i n ′ {name}' in ' namein{class}'

c l a s s 类 中 找 不 到 属 性 为 {class} 类 中找不到属性为 class{name} 的get方法

  • 写出get 方法
11.11 java.lang.NoSuchMethodException: ${class01}.()

低版本的mybatis映射对象时 对象必须拥有无参构造方法

  • 创建无参构造即可
  • 课外知识:这个异常也存在于反射中 当getMethod(" m e t h o d N a m e " ) ; 没 有 权 限 访 问 或 者 {methodName}"); 没有权限访问或者 methodName");访{methodName}不存在时会抛出此异常
11.12 java.lang.ClassCastException: ${class0} cannot be cast to ${class1}

不能将${class0} 转化成 ${class1} 十分经典的运行时异常,例如不能将String 转换为Integer 一样。 一般出现在 CUD parameterType中

  • 请检查你传入的参数类型 是否与 parameterType所指定的类型一致
11.13 Parameter {name} not found. Available parameters are [{collection}]

没有在{collection}中找到可用的形参{name}

  • 请确定你的name存在于${collection} 中
11.14 Invalid argument value: java.io.NotSerializableException

不可序列化异常 没有实现Serializable 接口??? implements Serializable

  • collection 指定为 list |map。 避免直接使用 list|map…

例:

 <insert id="insertUser">
        INSERT INTO `user`(`name`,`role_id`)
        values
        <foreach collection="list" separator=",">
            (#{list},#{list})
        </foreach>
    </insert>
 <insert id="insertUser">
        INSERT INTO `user`(`name`,`role_id`)
        values
        <foreach collection="map" separator=",">
            (#{map},#{map})
        </foreach>
    </insert>
  • 避免发生上面的错误即可
11.15 java.lang.UnsupportedOperationException

不支持的操作异常 操作Arrays 内部类 ArrayList也会抛出此异常 。

mybatis 动态sql 中 没有给list|map起别名 使用list|map会抛出此异常(list|map是整个集合。)

  • 请指定别名并用别名来操作属性

例:

 <insert id="insertUser">
        INSERT INTO `user`(`name`,`role_id`)
        values
        <foreach collection="list" separator="," >
            (#{list.name},#{list.role_id})
        </foreach>
    </insert>
 <insert id="insertUser">
        INSERT INTO `user`(`name`,`role_id`)
        values
        <foreach collection="map" separator="," >
            (#{map.name},#{map.role_id})
        </foreach>
    </insert>
  • WoW , 能在mybatis中遇到这个异常,都不是一般人 。
  • foreach 指定 item 属性,并 使用item起的别名来 .方法

注: 如果你的collection 指定的是 array ,传入的实参也是array 那么则会抛出 11.10 There is no getter…异常,其实也很好理解,因为数组是对象,而数组对象并不存在 #{array.name}这种奇特的set方法或字段

11.16 The expression ‘${value}’ evaluated to a null value.

表达式 ${value} 计算结果为空值

  • foreach元素中的属性collection所指定的值不存在
11.17 Malformed OGNL expression: …

表达式语法错误…

  • 请检查你的表达式是否正确

例:

<if test="${name} != null and name != '' ">AND `name` LIKE concat('%',#{name},'%')</if>
<if test="name ! null and name != '' ">AND `name` LIKE concat('%',#{name},'%')</if>
11.18 Mapper method ‘…’ has an unsupported return type:…

映射方法不支持返回类型

  • 检查CRUD 与标签是否对应

例:

<insert id="selectAll">
    <include refid="sqlSelectAll"/>
</insert>
11.19 It’s likely that neither a Result Type nor a Result Map was specified

可能没有指定结果类型或结果映射

  • 在select 标签中加入属性 resultMap 或 resultType

了解&参考

mybatis 别名 不区分大小写

Mybatis高版本支持自动有参构造方法创建对象。参数类型顺序需要和数据库中数据一一对应)

  • 注意:Mybatis自动使用有参构造方法构建对象后也会执行pojo的set方法 。就像Mybatis正常执行无参构造方法创建对象后会去寻找set方法为字段赋值时一样。
  • Mybatis自动使用有参构造方法是用来更灵活的创建对象的,并非是新增的一种映射对象的方式。
  • 当然你不写set方法或者字段名跟数据库列对不上的时候,它默认也就按照有参构造形参的顺序来进行传参(这就可以实现你想象中的mybatis构造方法映射对象的方式)
  • 注:如果传入实参不能被赋值给字段就会抛出 11.8 SQLDataException:Cannot determine value type from … 异常

以上内容来自
* 【狂神说Java】Mybatis最新完整教程IDEA版通俗易懂
* 【鲁班大叔_007】MyBatis源码暴力解析
* MyBatis中文文档
* 菅江晖的个人见解

自信是成功的第一诀窍

菅江晖

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值