MyBatis-映射器

映射器的主要元素

select元素

概述

select元素的属性说明

元素说明备注
id它和Mapper的命名空间组合起来是唯一的,刚好对应dao中方法的全类名+方法名不唯一会抛出异常
parameterType可以是全类名,也可以是别名
parameterMap即将废弃
resultType可以是全类名,也可以是别名,但不与resultMap同时出现
resultMap自定义的映射集可以配置映射规则,级联,typeHandler
flushCache是否清空缓存true/false
useCache是否启用二级缓存true/false
timeout设置超时参数,超时即抛出异常默认值为数据库厂商所设
fetchSize返回条数默认值为数据库厂商所设
statementType指定JDBC的statement方式,可选参数STATEMENT,PREPEARED,CALLABLESTATEMENT
resultSetType对JDBC的resultSet而言,可取值包括FORWARD_ONLY(游标允许向前访问)、SCROLL_SENSITIVE(双向滚动,但不及时更新,若数据库里的数据修改过,并不在resultSet中反映出来)、SCROLL_INSENSITIVE(双向滚动,及时更新,若数据库里的数据修改过,会在resultSet中反映出来)
databaseId参考??
resultOrdered这个设置仅适用于嵌套结果集select语句。如果为true,就是假设包含了嵌套结果集或者是分组了,当返回一个主结果行的时候,就不能对前面结果集的引用。避免了在获取嵌套的结果集的时候内存不够。
resultSets顾名思义,列出多个结果集,每个名称之间用逗号隔开很少使用

简易数据类型的例子

<mapper namespace="com.yan.dao.RoleMapper" >
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select 
    <include refid="Base_Column_List" />
    from role
    where id = #{id,jdbcType=INTEGER}
  </select>
</mapper>
package com.yan.dao;
import com.yan.po.Role;

public interface RoleMapper {
    Role selectByPrimaryKey(Integer id);
}

可以看到,xml的namespace与接口的全类名对应,id与方法名对应。

自动映射

由MyBatis-config.xml中的settings的子元素autoMappingBehavior决定,指定 MyBatis 如何自动映射列到字段/ 属性。NONE不使用自动映射,PARTIAL 只会自动映射简单, 没有嵌套的结果。FULL 会自动映射任意复杂的结果(嵌套的或其他情况) 。默认值为PARTIAL。

传递多个参数

使用Map类型传递

mapper.xml

<select id="selectByUnionKey" resultMap="BaseResultMap" parameterType="java.util.Map">
    select 
    <include refid="Base_Column_List" />
    from role
    where id = #{id} 
          and roleName = #{roleName}
  </select>

mapper.java

Role selectByUnionKey(Map<String,Object> map);

test.java

Role r =new Role();
Map<String, Object> map=new HashMap<String, Object>();
map.put("id", 1);
map.put("roleName", "超级管理员");
r=roleMapper.selectByUnionKey(map);

map传参的方式是硬编码,不推荐使用。

使用注解方式传递

直接在接口中方法参数前添加@Param(“自定义名称”),在mapper.xml中同样使用“自定义名称”接收即可,需要注意的是,同样遵循xml中id属性的限制,即它和Mapper的命名空间组合起来是唯一的,刚好对应dao中方法的全类名+方法名,不唯一会抛出异常。因为在测试中,接口中只要参数不同(map或组合参数),可以使用同样的方法名selectByUnionKey,而MyBatis不允许,因此以严格的一方为准。

//  Role selectByUnionKey(Map<String,Object> map);
    Role selectByUnionKey(@Param("roleName") String roleName,@Param("id") Integer id);
<!-- 注意,此时parameterType属性已删除 -->
<select id="selectByUnionKey" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from role
    where id = #{id} 
          and roleName = #{roleName}
  </select>

总结
- 使用map方式传参可读性和可维护性差,不推荐使用;
- 当参数数量<=5时,使用注解方式更有效率;
- 当参数>时,使用JavaBean更合理。

使用resultMap映射结果集

适合用于级联等复杂操作,以后讨论。基本的用法:

<resultMap id="BaseResultMap" type="com.yan.po.Role" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="rolename" property="roleName" jdbcType="VARCHAR" />
    <result column="note" property="note" typeHandler="com.yan.typeHandler.IStringTypeHandler" />
  </resultMap>
  <sql id="Base_Column_List" >
    id, rolename, note
  </sql> 
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select 
    <include refid="Base_Column_List" />
    from role
    where id = #{id,jdbcType=INTEGER}
  </select>

insert元素

概述

比select元素的属性少,不同的有:

属性名称描述备注
keyProperty指定主键,不与keyColumn同时使用
keyColumn指定主键,不与keyProperty同时使用
useGeneratedKeys使用JDBC的getGeneratedKeys方法获取由数据库生成的主键默认为false

主键回填和自定义

在由数据库生成主键的情况中,我们往往需要在插入数据后获取主键,以便于以后的操作 ,而MyBatis提供了实现的方法:

<!--<insert id="insert" parameterType="com.yan.po.Role" keyColumn="id" useGeneratedKeys="true">-->
<insert id="insert" parameterType="com.yan.po.Role" keyProperty="id" useGeneratedKeys="true">
    insert into role (id, rolename, note
      )
    values (#{id,jdbcType=INTEGER}, #{roleName,jdbcType=VARCHAR}, #{note,jdbcType=VARCHAR}
      )
  </insert>

这样插入完成紧跟着就可以通过主键查询了。

另一个体现MyBatis灵活性的方法是通过子元素selectKey来自定义主键增长方式, 前提是去掉数据库的自增功能但保留主键:

<insert id="insert" parameterType="com.yan.po.Role">
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    select if(max(id) is null,1,max(id)+2) as id from role
  </selectKey>
    insert into role (id, rolename, note)
    values (
    #{id,jdbcType=INTEGER}, 
     #{roleName,jdbcType=VARCHAR}, 
      #{note,jdbcType=VARCHAR}
      )
  </insert>

总结

  • selectKey中的keyProperty属性值需要对应POJO中set方法名,否则会出现No setter found for the keyProperty异常;
  • selectKey中keyColumn属性值不起作用,不会用;
  • 一般order属性设置为order=”BEFORE”,否则出现异常,AFTER的适用范围目前不清楚;
  • 使用了selectKey后,insert中不需要再配置keyProperty和useGeneratedKeys等属性,配了也不起作用。

update元素和delete元素

sql元素

sql元素可以定义sql语句片段,使用include可以直接引用:

<sql id="Base_Column_List" >
    id, rolename, note
  </sql> 
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select 
    <include refid="Base_Column_List" />
    from role
    where id = #{id,jdbcType=INTEGER}
  </select>

参数

参数配置

存储过程支持

特殊字符串替换和处理(#和$)

代表传递普通参数,而$可代表数据列等,比如:

columns=”col1,col2,col3…”;
select ${columns} from t_tableName;

resultMap结果映射集

resultMap元素构成

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

restructor元素用于配置构造方法。一个POJO可能不存在没有参数的构造方法,这个时候我们就可以使用constructor进行配置。假设角色类RoleBean不存在没有参数的构造方法,它的构造方法声明为public RoleBean(Integer id,String roleName),那么我们需要配置这个结果集,代码如下:

<resultMap type="" id="">
    <constructor>
        <idArg column="id" javaType="int"/>
        <arg column="role_name" javaType="string"/>
    </constructor>
    ...
  </resultMap>

使用map存储结果集

一般而言,任何的select语句都可以使用map存储:

    <select id="" parameterType="" resultType="">
        select id,name from role where note like concat('%',#{note},'%')
    </select>

使用map原则上石可以匹配所有结果集的,但是使用map接口就意味着可读性的降低,所以不推荐。

使用POJO存储结果集

<resultMap id="BaseResultMap" type="com.yan.po.Role" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="rolename" property="roleName" jdbcType="VARCHAR" />
    <result column="note" property="note" typeHandler="com.yan.typeHandler.IStringTypeHandler" />
</resultMap>
<sql id="Base_Column_List" >
    id, rolename, note
</sql> 
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select <include refid="Base_Column_List" /> from role where id = #{id,jdbcType=INTEGER}
</select>

级联

collection一对多级联

discriminator鉴别器级联

性能分析和N+1问题

级联的优势是能够方便快捷地获取数据,但是当多层级联时,会造成复杂度增加,不利于他人理解和维护的问题,而且有时候我们并不需要获取所有的数据。全取出来会造成SQL执行过多导致性能下降,因此我们要考虑延迟加载的问题。

延迟加载

主要在MyBatis的配置文件中的settings元素中设置,这意味着全局延迟加载的配置:

<settings>
    <setting name="logImpl" value="LOG4J"></setting>
    <setting name="lazyLoadingEnabled" value="true"></setting>
    <setting name="aggressiveLazyLoading" value="false"></setting>
</settings>

其中lazyLoadingEnabled标志延迟加载,比如有一个父类表和一个子类表关联,当不需要子类表数据时先不加载它,aggressiveLazyLoading表示同级延迟加载,表示两个不存在父子类关系的表关联时,是否加载用不到的数据,比如A、B两表关联,但我只需要A表的数据,若aggressiveLazyLoading设置为false,则不会加载B表数据。

局部延迟加载

MyBatis也提供了局部延迟加载的功能,可以在association和collection元素上加入属性值fetchType就可以了,它有两个取值范围,即eager和lazy。它的默认值取决于在settings的配置,如果没有配置,就是eager,一旦配置了,也会被局部配置所覆盖。

其他级联

在sql语句中尽量使用左连接,但问题是resultMap得跟着变,而且复杂度呈正相关关系,不一定适用于性能要求较高的项目。

缓存

目前流行的缓存服务有MongoDB,Redis和Ehcache等,缓存是内存上保存的数据,无需再从硬盘上读取,因此具备快速读取和使用的特点,如果缓存命中率高,可以极大提高系统性能,否则没有什么意义,因此使用缓存的关键在于存储内容访问的命中率。

系统缓存

MyBatis支持缓存,在没有配置的情况下,只会开启一级缓存–就是只相对于同一个SqlSession而言。new一个就得从新读取了。
二级缓存是在SqlSessionFactory层面共享的,new一个SqlSession也可以被调用。
需要在***mapper.xml中配置,添加<cache/>即可,但是这样太笼统,这意味着:
所有的select语句会被缓存,增删改以后还会刷新缓存,使用默认的Least Resently Used(LRU,最近最少使用的)算法来回收;根据时间表,比如No Flush Interval(CNFI,没有刷新间隔),缓存不会以任何时间顺序来刷新。
缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用;
缓存会被视为read/write的缓存,意味着对象检索不是共享的,而且可以安全的被调用者修改,不干扰其他调用者或线程所做的潜在修改。
最重要的是POJO要实现serializable接口,否则会报异常。
细化cache元素

<cache eviction="LRU" flushInterval="100000" size="1024" readOnly="true"/>

属性说明:
eviction:代表缓存回收策略,目前MyBatis支持以下策略:
1.LRU:最近最少使用,移除最长时间不用的对象。
2.FIFO:先进先出,按对象进入缓存的顺序来移除。
3.SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象。
4.WEAK:弱引用,更积极地移除基于垃圾回收器状态和弱引用规则的对象,这里采用的是LRU,移除最长时间不用的对象。
flushInterval:刷新间隔时间,单位为毫秒,这里配置的是100秒刷新,如果你不配置它,那么当SQL被执行的时候才会去刷新缓存。
1.size:引用数目,一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。这里配置的是1024个对象
2.readOnly:只读,意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有办法修改缓存,它的默认值为false,不允许我们修改。

自定义缓存

系统缓存是应用本地机器上的本地缓存,但是在大型服务器上,会使用各类不同的缓存服务器,这个时候我们可以定制缓存,比如现在十分流行的Redis缓存。我们需要实现MyBatis提供的接口org.apache.ibatis.cache.Cache,缓存接口如下:

//清空缓存
public void clear() {
}
//获取缓存编号
public String getId() {
    return null;
}
//通过key获取缓存对象
public Object getObject(Object key) {
    return null;
}
//获取缓存的读写锁
@Override
public ReadWriteLock getReadWriteLock() {
    return null;
}
//获取缓存对象大小
@Override
public int getSize() {
    return 0;
}
//保存key值缓存对象
@Override
public void putObject(Object key, Object value) {
}
//通过key删除缓存对象
@Override
public Object removeObject(Object arg0) {
    return null;
}

我们需要实现这个接口并且在cache标签上添加type属性,比如我的实现类路径是com.yan.mybatis.cache.MyCache,那么这就是type的值。
另外我们可以在这个实现类中添加额外的属性,比如host,设置setHost方法,并且在cache标签中设置property子元素,这样它初始化就会被调用。

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

我们在映射器上可以配置insert、delete、select、update元素,对应增删改查。也可以配置SQL层面上的缓存规则,来决定他们是否需要使用或刷新缓存,我们往往是根据两个属性:useCache和flushCache来完成,其中useCache表示是否使用缓存,flushCache表示变更后是否需要刷新缓存。

    <select ... flushCache="false" useCache="true"/>
    <insert ... flushCache="true"/>
    <update ... flushCache="true"/>
    <delete ... flushCache="true"/>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值