MyBatis简介(四)

映射器

由接口+XML/注解组成;可配置参数、各类SQL语句、存储过程、缓存、级联等,并通过简易的映射规则映射到指定POJO或者其他对象上,能有效消除JDBC底层代码;由于注解对于复杂SQL处理麻烦,自身可读性差,丢失了XML上下文相互引用的实际情况,一般推荐使用XML文件实现;

在这里插入图片描述
(1)select
常用元素配置:
在这里插入图片描述
–MyBatis自带映射:自动映射(POJO的属性名与SQL列名一致或查询时使用别名as使其一致)和驼峰映射(SQL是xxx_yyy,对应POJO是xxxYyy),由setting元素中的配置autoMappingBehavior和mapUnderscoreToCamelCase控制;一般推荐使用自动映射,因为驼峰映射要求会比较严苛;autoMappingBehavior取值有:
NONE:不进行自动映射;
PARTIAL:默认值,只对没有嵌套结果集进行自动映射;
FULL:对所有结果集进行自动映射;

resultMap:对于复杂的映射,如主从表格的级联,字段的转换等;

<resultMap type="cn.infocore.pojo.MyStreamer" id="streamerResultMap">
	<id column="id" property="id"/>
	<result column="username" property="username"/>
	<result column="password" property="password"/>
</resultMap>
<select id="list" resultMap="streamerResultMap">
	select * from Streamer
</select>

传递参数
–单个:#{参数名}
–多个:
—map:业务可读性差,一般不用;
—@Param注解:参数小于等于5个时,推荐使用;
—Java Bean:参数大于5个时,推荐使用;
—混合使用:参数中多个对象,支持EL;

分页参数RowRounds:MyBatis内置了一个专门处理分页的类RowRounds,适用于小数据量的查询,大量数据查询建议使用分页插件,源码如下:

public class RowBounds {
  public static final int NO_ROW_OFFSET = 0; //偏移量,从第几行开始读
  public static final int NO_ROW_LIMIT = Integer.MAX_VALUE;  //限制条数
  public static final RowBounds DEFAULT = new RowBounds();

  private final int offset;
  private final int limit;

  public RowBounds() {
    this.offset = NO_ROW_OFFSET;
    this.limit = NO_ROW_LIMIT;
  }

  public RowBounds(int offset, int limit) {
    this.offset = offset;
    this.limit = limit;
  }

  /****getter setter*****/
}

使用:

public List<MyStreamer> list(RowBounds rowBounds);

SQL:没有RowBounds信息,因为它是MyBatis的一个附加参数,会被自动识别从而进行分页;

<select id="list" resultMap="streamer">
	select * from Streamer
</select>

(2)insert
在这里插入图片描述
MyBatis执行完一条insert语句后,会返回一个整数表示其影响的记录数;

<insert id="insertDiskGroup" parameterType="diskgroup" useGeneratedKeys="true" keyProperty="group.id">
   	insert into DiskGroup(name,size,clientId) values('${group.name}','${group.size}','${group.clientId}')
   </insert>

useGeneratedKeys="true"表示获取自动生成的主键,同时配置keyProperty或keyColumn,表示把主键赋值到POJO的哪个属性中,如存在多个主键,则用逗号“,”隔开;实现自动生成的主键回填的功能;

<insert id="insertDiskGroup" parameterType="diskgroup">
	<selectKey keyProperty="group.id" resultType="int" order="BEFORE">
		select if (max(group.id)==null,1,max(group.id)+3) from DiskGroup
	</selectKey>
   	insert into DiskGroup(name,size,clientId) values('${group.name}','${group.size}','${group.clientId}')
   </insert>

自定义主键增长规则,上例为表记录为空时,id设置为1,否则id设置为当前id+3;返回一个int类型的结果集,order="BEFORE"表示该语句将在定义的SQL之前执行,还可以使用AFTER;

(3)update、delete
update和delete与insert类似,执行完也会返回一个表示影响数据库记录数的整数;

<update id="renameClient">
   	update Client set name=#{name} where id=#{cId}
</update>

<delete id="removeClient" parameterType="int">
   	delete from Client where id=#{cId}
</delete>

(4)sql元素
在MyBatis的xml文件里,sql元素作为定义的SQL的一部分,可以实现编写一次,而在其他元素中多次引用的功能;

<sql id="diskGroupCols">name,size,clientId</sql>
<insert id="insertDiskGroup" parameterType="diskgroup" useGeneratedKeys="true" keyProperty="group.id">
	insert into DiskGroup(<include refid="diskGroupCols"/>) values('${group.name}','${group.size}','${group.clientId}')
</insert>

sql元素还支持变量传递:在include元素下命名了一个alias的变量,其值是表DiskGroup的别名g,这样sql元素中就可以使用这个变量名了;

<sql id="diskGroupCols">${alias}.name,${alias}.size,${alias}.clientId</sql>
<insert id="insertDiskGroup" parameterType="diskgroup" useGeneratedKeys="true" keyProperty="group.id">
	insert into DiskGroup g(<include refid="diskGroupCols"><property name="alias" value="g"/></include>) values('${group.name}','${group.size}','${group.clientId}')
</insert>

(5)参数
-当一些数据库字段返回为null,而MyBatis系统又检测不到使用何种jdbcType进行处理时,会发生异常,这个时候就需要指定typeHandler:

update User set mode=#{mode} where username=#{username,typeHandler=org.apache.ibatis.type.StringTypeHandler}

-如果username是一个没有注册的类型,那么需要自定义typeHandler,然后再指定即可;
-指定jdbcType、javaType、typeHandler:可以支持精度;

#{username,javaType=int,jdbcType=NUMERIC,numericScale=2,typeHandler=MyTypeHandler}

存储过程参数支持:
#{id,mode=IN}:输入参数;
#{id,mode=OUT}:输出参数;
#{id,mode=INOUT}:输入输出参数;

(6)resultMap
定义映射规则、级联的更新、定义类型转换器等;结果集的映射关系;现有MyBatis只支持resultMap的查询,不支持更新或者保存;
resultMap元素构成如下:

<resultMap>
	<constructor> //配置构造方法,MyBatis会使用此构造方法构造POJO
		<idArg property="" column="" javaType="" jdbcType="" typeHandler=""/>
		<arg/>
	</constructor>
	<id/> //主键,允许多个主键,称为联合主键
	//配置POJO到SQL列名的映射关系
	<result property="" column="" javaType="" jdbcType="" typeHandler=""/> 
	<association/>
	<collection/>
	<discriminator>
		<case/>
	</discriminator>
</resultMap>

–存储结果集
可以使用map和POJO存储查询结果集,但由于map的可读性差,因此推荐POJO;使用方法,前文可见,就不再重复了;

(7)级联
级联可以十分快速的获取关联数据,但过多也会增加系统的复杂度和耦合度,从而使系统性能降低;因此当级联超过3层时,不建议使用,适用于对象关系比较简单,级联不多的场景;
MyBatis级联分3种:
–鉴别器(discriminator):根据某些条件采用具体实现类级联的方案;
–一对一(association):两个对象的关系是一对一;
–一对多(collection):两个对象的关系是一对多;
不支持多对多级联,因为过于复杂,可以通过两个一对多级联实现;

<resultMap type="cloudaccount" id="CloudAccountMap">
<association property="cloudserver" column="acId" select="cn.infocore.pojo.MyCloudServerDao.getServer" />
</resultMap>

一对一,property属性代表映射到指定POJO属性上;select配置命名空间+SQL id;column代表SQL的列,会作为参数传递给select指定的SQL,如果多个参数,用逗号隔开;

<resultMap type="cloudaccount" id="CloudAccountMap">
<collection property="servers" column="acId" select="cn.infocore.pojo.MyCloudServerDao.listByAcId" />
</resultMap>

一对多,select配置命名空间+SQL id;column代表SQL的列,会作为参数传递给select指定的SQL,如果多个参数,用逗号隔开;结果返回给property指定的POJO属性servers;

<discriminator javaType="long" cloumn="sex">
	<case value="1" resultMap="maleMapper"/>
	<case value="2" resultMap="femaleMapper"/>
</discriminator>

鉴别器,column代表使用哪个字段进行鉴别,子元素case用于区分,resultMap属性表示采用哪个ResultMap去映射;

通过级联设置,默认情况下会执行所有关联SQL,包括获取当前信息不需要的SQL,导致数据库资源的损耗和系统性能的下降;这个就出现了N+1问题,即当前N个关联关系完成级联,再加入一个级联关系,就变成N+1个级联,所有的级联SQL都会执行;针对这个问题,就提出了延迟加载的功能;

延迟加载配置:settings
-lazyLoadingEnabled :默认false,延迟加载的全局开关,true表示所有关联对象都会延迟加载,在特定关联关系中,可通过fetchType覆盖;实体的关系对象属于另一个层级;
-aggressiveLazyLoading:在3.4.1版本后,默认false;按需加载,true表示开启了层级开关的延迟加载;鉴别器与实体在同一个层级;
-fetchType:适用于association和collection,eager值表示获得当前POJO后立即加载对应的数据;lazy值表示获得当前POJO后延迟加载对应的数据;fetchType会忽略上述两个全局配置项;

SQL表连接基础上的级联:
-每一个级联元素中的主键与POJO实体配置的主键一一对应,形成级联;
-association通过javaType的定义声明实体映射,而collection是使用ofType声明;
-discriminator元素定义使用何种具体的resultMap进行级联;

<collection property="employeeTaskList" ofType="cn.infocore.pojo.EmployeeTask" column="id">
	<id column="et_id" property="id" />
	<result column="id" property="empId" />
	<result column="task_name" property="taskName" />
	<result column="note" property="note" />
	<association property="task" javaType="cn.infocore.pojo.Task" column="et_task_id">
		<id column="t_id" property="id" /> //实体id与et_task_id一一对应
		<result column="t_title" property="title" />
		<result column="t_context" property="context" />
		<result column="t_note" property="note" />
	</association>
</collection>
<discriminator javaType="int" column="sex"> //通过sex列判断
	<case value="1" resultMap="maleHealthFormMapper" />
	<case value="2" resultMap="femaleHealthFormMapper" />
</discriminator>

(8)缓存
MyBatis默认开启一级缓存,这个缓存不需要POJO对象可序列化(实现Serializable接口);
1)一级缓存:SqlSession上的缓存,通过同一个SqlSession对象进行数据获取,第一次查询后会缓存数据,第二次只要SQL与参数没变,且缓存未超时和未声明需要刷新时,就会从缓存读取数据;不同SqlSession对象中的缓存是不能共享的;

2)二级缓存:SqlSessionFactory上的缓存,即对<mapper>上定义的namespace命名空间内的所有select元素SQL查询的结果进行缓存,而其中的insert、delete、update语句在操作时会刷新缓存;可以共享不同SqlSession对象上的缓存,二级缓存需要手动开启;在映射文件里加上<cache/>即可;POJO对象需要可序列化;
Cache元素配置项:
在这里插入图片描述

public interface Cache {
  //获取缓存ID
  String getId();
  //保存对象,key为键,value为值
  void putObject(Object key, Object value);
  //获取缓存数据,key为键
  Object getObject(Object key);
  //删除缓存,key为键
  Object removeObject(Object key);
  //清除缓存
  void clear();
  //获取缓存大小
  int getSize();
 //获取读写锁,需要考虑多线程的场景
  ReadWriteLock getReadWriteLock();
}

默认缓存配置:flushCache表示是否刷新缓存,useCache是否使用缓存;

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

引用其他映射器中cache元素的配置:

<cache-ref namespace="cn.infocore.mappser.RoleMapper"/>

重点再讲一下sqlSession.close()和sqlSession.commit():
1)执行select操作,会保存一级缓存,再调用close,会将其一级缓存的数据放进二级缓存中,此时一级缓存随着SqlSession的关闭也就不存在了;
2)执行select缓存,会保存一级缓存,再调用commit,会将其中的一级缓存的数据放进二级缓存中,并清空一级缓存
3)执行更新操作(update、delete、insert)时,同时不调用commit或close,这时只会清空其自身的一级缓存,对二级缓存没有影响;
4)执行更新操作(update、delete、insert)时,调用commit,不仅清空其自身的一级缓存(执行更新操作的效果),也清空二级缓存(执行commit()的效果);
5)执行更新操作(update、delete、insert)时,调用close,只会清空其自身的一级缓存(执行更新操作的效果),对二级缓存没影响了;当autoCommit为true时,会清空二级缓存;

(9)存储过程
数据库预先编译好,放在数据库内存中的一个程序片段,性能高,可重复使用;
参数类型:输入参数、输出参数、输入输出参数;
返回结果:整形、字符型OUT、INOUT参数等简易类型、游标类型;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值