MyBatis 映射器

映射器的主要元素

 

元素名称 描述 备注
select 查询语句 可自定义参数,返回结果集
insert 插入语句 执行后返回一个整数,代表插入的条数
update 更新语句 执行后返回一个整数,代表更新的条数
delete 删除语句 执行后返回一个整数,代表删除的条数
parameterMap 定义参数映射关系 即将过时的元素,不建议使用
sql 允许定义一部分sql然后再其他地方引用它  
resultMap 用来描述从数据库结果集中来加载对象映射为java对象 它将提供映射规则
cache 给定命名空间的缓存配置  
cache-ref 其他命名空间的缓存配置的引用  

 

select 元素

元素   说明  备注
id  与命名空间组合起来是唯一的,提供给MyBatis调用  
parameterType 可以是类的全名,也是可以是别名   
parameterMap  即将被废弃的元素  
resultType 

可以是类的全名,也可以使用别名,不能和resultMap同时使用

在允许自动匹配的情况下结果集将通过JavaBean的规范映射

 
resultMap 是结果集的引用,有强大的映射功能 可以配置映射规则、级联、typeHandler等
flushCache 它的作用是在调用SQL后,是否要求MyBatis清空之前查询的本地缓存和二级缓存 true/false,默认 false
useCache 启动二级缓存的开关,是否要求MyBatis将此次结果缓存 true/false ,默认 true 
timeout 设置超时参数,等待超时的时候将抛出异常,单位为秒  默认是JDBC驱动设置的秒数 
fetchSize 获取记录的总条数设置定 默认是JDBC驱动所设置的条数
statementType

告诉MyBatis使用哪个JDBC的Statement工作,

SATEMENT、PREPARED、CallableStatement

默认为 PREPARED
resultSetType

这是对JDBC的resultSet接口而言,值包括

FORWARD_ONLY(游标允许向前)

SCROLL_SENSITIVE(双向滚动,不及时更新,数据库里修改,并不在resultSet中反应出来)

SCROLL_INSENSITIVE(双向滚动,及时跟踪数据库更新)

默认是JDBC驱动所设置的 
databaseId 提供多种数据库的支持,参考databseProvider  
resultOrdered

仅适用于嵌套结果集select语句,当为true时,假设包含了嵌套结果集,

当返回一个朱结果行的时候就不对前面的及结果集引用,确保在获取嵌套

结果集的时候,不至于导致内存不够用

true/false ,默认值为false
resultSets

适合与多个结果集的情况,它将列出执行SQL后每个结果集的名称,

每个名称之间用逗号分隔

很少使用

传递多个参数

使用 @Param 传递多个参数

大于5个参数建议封装为JavaBean进行传递

 

使用resultMap映射结果集

实体类

public class SystemUser {

    private Long id;

    private String uname;

    private String email;

    private String address;

    // getter、setter.....

    @Override
    public String toString() {
        return "SystemUser{" +
                "id=" + id +
                ", uname='" + uname + '\'' +
                ", email='" + email + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

映射

<resultMap id="baseMap" type="SystemUser">
      <id property="id" column="id" />
      <result property="uname" column="uname" />
      <result property="email" column="email" />
      <result property="address" column="address" />
</resultMap>

 

insert 元素

MyBatis在执行完插入之后返回一个整数,表示插入到数据库的记录数

属性 描述 备注
id 与命名空间组合起来是唯一的,提供给MyBatis调用  
parameterType 可以是类的全名,也是可以是别名  JavaBean、Map等参数
parameterMap 即将被废弃的元素  
flushCache 它的作用是在调用SQL后,是否要求MyBatis清空之前查询的本地缓存和二级缓存 true/false 默认为false
timeout 设置超时参数,等待超时的时候将抛出异常,单位为秒  默认是JDBC驱动设置的秒数 
statementType

告诉MyBatis使用哪个JDBC的Statement工作,

SATEMENT、PREPARED、CallableStatement

默认为 PREPARED
keyProperty 表示那个列作为属性的主键,不能喝keyColumn同时使用

设置指定列为主键,

联合主键使用逗号隔开

useGeneratedKeys

MyBatis使用JDBC的getGeneratedKeys方法

来取出由数据库内部生成的主键

true/false,默认false
keyColumn 指明第几列是主键,不能和keyProperty同时使用,整型参数

和keyProperty一样联合主键

,可以用逗号隔开

databseId 参考 databaseIdProvider  
lang 自定义语言,第三方语言,极少使用  

主键回填

场景:主键自增字段,在插入后往往需要获得这个主键值,以便于未来的操作

  1. 使用keyProperty指定哪个字段是主键
  2. 使用useGeneratedKeys属性告诉MyBatis这个主键是否使用数据库内置生成策略

 

用户表:

CREATE TABLE `t_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `uname` varchar(255) DEFAULT NULL,
  `phone` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `address` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

实体类:

public class SystemUser {

    private Long id;

    private String uname;

    private String email;

    private String address;

    // getter、setter略

    @Override
    public String toString() {
        return "SystemUser{" +
                "id=" + id +
                ", uname='" + uname + '\'' +
                ", email='" + email + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

Mapper

<insert id="insert" parameterType="SystemUser"
    useGeneratedKeys="true" keyProperty="id">
    insert t_user(uname,email,address) values (#{uname},#{email},#{address})
</insert>

测试java代码:

SystemUser systemUser = new SystemUser();
systemUser.setUname("unknown");
systemUser.setEmail("unknown@mail.com");
systemUser.setAddress("未知");
this.systemUserMapper.insert(systemUser);
Assert.notNull(systemUser.getId(),"主键值回传失败");

 

update 和 delete 元素

与insert元素相同,执行完update和delete元素后会返回一个整数,表示执行后受影响的记录数

修改

<update id="update" parameterType="SystemUser">
    update t_user set uname=#{uname},email=#{email},address=#{address} where id=#{id}
</update>

删除

<delete id="delete" parameterType="long">
    delete from t_user where id =#{id}
</delete>

参数

注意:定义参数属性时,MyBatis不允许换行

参数配置

#{aga,javaType=int,jdbcType=NUMERIC}
#{age,javaType
=string,jdbcType=VARCHAR,typeHandler=MyHandler}
#{age,javaType
=double,jdbcType=NUMERIC,numericScale=2}
  • age 为参数名
  • javaType和jdbcType用于查找类型处理器
  • typeHandler用于指定类型处理器
  • numericScale设置参数精度

 

存储过程支持

存储过程存在三种参数:输入参数(IN)、输出参数(OUT)、输入输出参数(INOUT)

#{name,mode=OUT,jdbcType=CURSOR,javaType=ResultSet,resultMap=nameResultMap}

mode来确定参数类型

jdbcType设置为CURSOR时候需要配置resultMap,以便MyBatis将存储过程的参数映射到对应的类型,这时MyBatis会通过设置的resultMap自动映射结果

 

特殊的字符串替换和处理 

${...} 用于处理字符串拼接,有SQL注入的风险,常常说的不安全

#{...} 在大部分情况下MyBatis会创建预编译的语句,然后MyBatis会为它设置值,可以理解为占位符,不会发生SQL注入

 

 

sql元素

可以定义一串SQL语句的组成部分,其他语句可以通过引用来使用它

支持一处定义多处使用,可以减少工作量

<sql id="user_columns">
    uname,email,address
</sql>
    
<select id="list" resultMap="baseMap">
    select 
    <!-- 引用user_columns片段 -->
    <include refid="user_columns" />
    from t_user
</select>

 

resultMap结果映射集

它的作用是定义映射规则、级联更新、定制类型转换器等

 

resultMap 元素构成

<resultMap id="" type="">
    <constructor>
        <idArg />
        <arg />
    </constructor>
    <id />
    <result/>
    <association property=""/>
    <collection property="" />
    <discriminator javaType="">
        <case value=""></case>
    </discriminator>
</resultMap>

 

constructor元素用于配置构造方法中的参数,idArg元素和arg元素的顺序要和构造参数的顺序一致

实体类:

public class SystemUser {
    private Long id;
    private String uname;
    private String email;
    private String address;

    public SystemUser(Long id, String uname, String email, String address) {
        this.id = id;
        this.uname = uname;
        this.email = email;
        this.address = address;
    }

    // getter、setter 略  
}

result配置:

<resultMap id="baseMap" type="SystemUser">
    <constructor>
        <idArg column="id" javaType="long" />
        <arg column="uname" javaType="string" />
        <arg column="email" javaType="string" />
        <arg column="address" javaType="string" />
    </constructor>
    <id property="id" column="id" />
    <result property="uname" column="uname" />
    <result property="email" column="email" />
    <result property="address" column="address" />
</resultMap>

 

result 元素和id元素的属性

id元素表示哪个列是主键,允许多个主键(联合主键)

result配置POJO到SQL列明的映射关系

元素名称 说明 备注
property POJO类的字段或属性  
column 与POJO类的字段或属性对应的SQL的列  
javaType 配置Java的类型 可以是类全限定名或别名
jdbcType 配置数据库类型 JDBC类型,MyBatis已经做好了限定
typeHandler 类型处理器 支持覆盖默认的类型处理器

 

使用map存储结果集

<select id="listMap" resultType="map">
    select * from t_user
</select>

级联

association 代表一对一关系

<resultMap id="baseMap" type="SystemUser">
    <id property="id" column="id" />
    <result property="uname" column="uname" />
    <result property="email" column="email" />
    <result property="address" column="address" />
    <association property="picture" column="id" select="com.banywl.study.studymybatis.mapper.PictureMapper.listById" />
</resultMap>

collection 代表一对多关系

<resultMap id="baseMap" type="SystemUser">
    <id property="id" column="id" />
    <result property="uname" column="uname" />
    <result property="email" column="email" />
    <result property="address" column="address" />
    <association property="roles" column="id" select="com.banywl.study.studymybatis.mapper.PictureMapper.listById" />
    <collection property="roles" column="id" select="com.banywl.study.studymybatis.mapper.SystemRoleMapper.listByUserId" />
</resultMap>

discriminator 是鉴别器,它可以根据实际选择采用哪个类作为实例,允许根据特定的条件去关联不同的结果集

<resultMap id="baseMap" type="SystemUser">
    <id property="id" column="id" />
    <result property="uname" column="uname" />
    <result property="email" column="email" />
    <result property="address" column="address" />
    <association property="roles" column="id" select="com.banywl.study.studymybatis.mapper.PictureMapper.listById" />
    <collection property="roles" column="id" select="com.banywl.study.studymybatis.mapper.SystemRoleMapper.listByUserId" />
    <discriminator javaType="int" column="name">
        <case value="zhangsan" resultMap="maleMap" />
        <case value="lisi" resultMap="femaleMap" />
    </discriminator>
</resultMap>
    
<resultMap id="maleMap" type="MaleSystemUser" extends="SystemUser">
    <collection property="maleList" column="id" select="findMaleList" />
</resultMap>

<resultMap id="femaleMap" type="FemaleSystemUser" extends="SystemUser">
    <collection property="femaleList" column="id" select="findFemaleList" />
</resultMap>

 

性能分析和N+1问题

级联的方式能够方便快捷获取数据,建议超过三层关联尽量少用级联,会造成复杂度增加,不利于他人的理解和维护

有时候并不需要获取所有的数据,过多的执行SQL导致性能下降,这种可以使用代码去取代

混乱的关联关系在resultMap中还会造成N+1的问题(A在不知道B关联了C的情况下进行了A关联B的操作),为了解决这个问题考虑使用延迟加载

 

直接使用SQL查询语句完成关联关系的查询不存在N+1的问题

<resultMap id="baseMap" type="SystemUser">
    <id property="id" column="id" />
    <result property="uname" column="uname" />
    <result property="email" column="email" />
    <result property="address" column="address" />
    
    <association property="picture" column="id" javaType="Picture">
        <result property="id" column="pid" />
        <result property="url" column="url" />
    </association>
    <collection property="roles" ofType="SystemRole">
        <result property="rid" column="rid" />
        <result property="rname" column="rname" />
    </collection>
    <collection property="roles" column="id" select="com.banywl.study.studymybatis.mapper.SystemRoleMapper.listByUserId" />
    
    <discriminator javaType="int" column="name">
        <case value="zhangsan" resultMap="maleMap" />
        <case value="list" resultMap="femaleMap" />
    </discriminator>
</resultMap>

 

缓存cache

缓存是在计算机内存上保存的数据, 在读取时无需再从粗盘读入,因此具备快速读取和使用的特点,如果缓存命中率高

name可以极大的提高系统的性能 

 

系统缓存(一级缓存和二级缓存)

系统缓存是MyBatis应用机器上的本地缓存

MyBatis默认情况下只开启了一级缓存(SqlSession), 在参数和SQL完全一样的情况下,我们使用同一个SqlSession第一次查询后

MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没超时的情况下,SqlSession都只会取出当前

缓存的数据,而不会再次发送到SQL数据库

注意: SqlSession 是相互隔离的

 

SqlSessionFactory层面上的二级缓存的默认是不开启的,当二级缓存开启的时候,MyBatis要求返回的POJO必须是可序列化的(实

现Serializeable接口),然后在映射XML文件中配置<cache />即可

  • 映射语句文件中的所有select将会被缓存
  • 映射语句文件中的所有insert、update、delete语句会刷新缓存
  • 缓存会使用默认的Least Recently Used(LRU, 最近最少使用的)算法来回收
  • 根据时间表, 比如No Flush Interval,(CNFI,没有刷新间隔),缓存不会以任何时间顺序来刷新
  • 缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用
  • 缓存会被视为read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全的被调用者修改,不干扰其他调用者或线程所做的潜修改
<cache eviction="LRU" flushInterval="100000" size="1024" readOnly="true" />
  • eviction: 是缓存回收策略,目前支持以下策略
  1. LRU,最近最少使用,移除最长时间不用的对象
  2. FIFO,先进先出,按对象进入缓存的顺序来移除他们
  3. SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象
  4. WEAK,弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象
  • flushInterval: 刷新间隔时间,单位毫秒,如果不配置,那么当SQL被执行的时候才会去刷新缓存
  • size: 引用数目,正整数,代表缓存可以存储多少个对象
  • readOnly:只读,意味着缓存数据只能读取而不能修改,默认值为false

自定义缓存

实现MyBatis中提供的 org.apache.ibatis.cache.Cache缓存接口

package com.test;

import org.apache.ibatis.cache.Cache;
import java.util.concurrent.locks.ReadWriteLock;

// 自定义缓存
public class MyCache implements Cache {
    
    // 缓存所在的服务器地址
    private String host;

    public void setHost(String host) {
        this.host = host;
    }

    // 获取缓存编号
    @Override
    public String getId() {
        return null;
    }

    // 保存key值缓存对象
    @Override
    public void putObject(Object key, Object value) {

    }

    // 通过key获取缓存对象
    @Override
    public Object getObject(Object key) {
        return null;
    }

    // 通过key删除缓存对象
    @Override
    public Object removeObject(Object key) {
        return null;
    }

    // 清空缓存
    @Override
    public void clear() {

    }

    // 缓存对象大小
    @Override
    public int getSize() {
        return 0;
    }

    // 获取缓存的读写锁
    @Override
    public ReadWriteLock getReadWriteLock() {
        return null;
    }
}

如何使用自定义缓存?

<cache type="com.test.MyCache">
    <property name="host" value="localhost" />
</cache>

定制缓存策略

<select ... flusCache="false" useCache="true" />
<insert ... flushCache="true" />
<update ... flushCache="true" />
<delete ... flushCache="true" />

useCache表示是否需要缓存

flushCache表示执行后是否刷新缓存

以上是MyBatis提供的默认配置

 

posted @ 2020-11-12 22:04  半雨微凉  阅读(166)  评论(0)    收藏  举报