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 | 自定义语言,第三方语言,极少使用 |
主键回填
场景:主键自增字段,在插入后往往需要获得这个主键值,以便于未来的操作
- 使用keyProperty指定哪个字段是主键
- 使用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: 是缓存回收策略,目前支持以下策略
- LRU,最近最少使用,移除最长时间不用的对象
- FIFO,先进先出,按对象进入缓存的顺序来移除他们
- SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象
- 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提供的默认配置

浙公网安备 33010602011771号