MyBatis核心映射器学习

版权声明:如果转载或使用本博客,请标明出处 https://blog.csdn.net/Black1499/article/details/83373825

一、概述

MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 就是针对 SQL 构建的,并且比普通的方法做的更好。

映射器的配置元素:

元素名称 描述 备注
insert 插入语句 执行后返回一条整数,代表插入的条数
delete 删除语句 执行后返回一条整数,代表删除的条数
update 更新语句 执行后返回一条整数,代表更新的条数
select 查询语句 可自定义参数,返回结果集
sql 先定义一部分SQL,可以被引用 如:一个表的列名可单独定义,多次在不同的语句中使用
resultMap 描述数据库结果集中加载的对象,复杂又强大 停供自定义映射规则
cache 给定命名空间的缓存配置。
cache-ref 其他命名空间缓存配置的引用。

二、insert、update、delete元素

元素及属性

<insert
  id="insertAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  keyProperty=""
  keyColumn=""
  useGeneratedKeys=""
  timeout="20">

<update
  id="updateAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">

<delete
  id="deleteAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">
Insert,Update,Delete'sAttributes
属性 描述
id 命名空间中的唯一标识符,可被用来代表这条语句。
parameterType 将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为MyBatis可以通过TypeHandler推断出具体传入语句的参数,默认值为unset。
parameterMap 这是引用外部parameterMap的已经被废弃的方法。使用内联参数映射和parameterType属性。
flushCache 将其设置为true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句)。
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为unset(依赖驱动)。
statementType STATEMENT,PREPARED或CALLABLE的一个。这会让MyBatis分别使用Statement,PreparedStatement或CallableStatement,默认值:PREPARED。
useGeneratedKeys (仅对insert和update有用)这会令MyBatis使用JDBC的getGeneratedKeys方法来取出由数据库内部生成的主键(比如:像MySQL和SQLServer这样的关系数据库管理系统的自动递增字段),默认值:false。
keyProperty (仅对insert和update有用)唯一标记一个属性,MyBatis会通过getGeneratedKeys的返回值或者通过insert语句的selectKey子元素设置它的键值,默认:unset。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
keyColumn (仅对insert和update有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
databaseId 如果配置了databaseIdProvider,MyBatis会加载所有的不带databaseId或匹配当前databaseId的语句;如果带或者不带的语句都有,则不带的会被忽略。

insert语句中的主键回填

如果在数据库中我们把主键设为自动增长,在插入数据的时候我们就没有必要再为主键插入值。但有时候我们需要继续使用这个主键,取到它的值为了后面更多的操作。为此MyBatis提供这样的支持去获取主键。

<insert id="insert" parameterType="com.lzx.entity.PhoneList" 
			useGeneratedKeys="true" keyColumn="id">
    insert into phone_list (phone, name)
    values ( #{phone}, #{name})
  </insert>

批量插入数据

<insert id="insertAuthor" useGeneratedKeys="true"
    keyProperty="id">
  insert into Author (username, password, email, bio) values
  <foreach item="item" collection="list" separator=",">
    (#{item.username}, #{item.password}, #{item.email}, #{item.bio})
  </foreach>
</insert>

自定义主键

对于不支持自动生成类型的数据库或可能不支持自动生成主键的 JDBC 驱动,MyBatis 有另外一种方法来生成主键。

<insert id="insertAuthor">
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
  </selectKey>
  insert into Author
    (id, username, password, email,bio, favourite_section)
  values
    (#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR})
</insert>

selectKey 元素将会首先运行,Author 的 id 会被设置,然后插入语句会被调用。这给你了一个和数据库中来处理自动生成的主键类似的行为,避免了使 Java 代码变得复杂。

update和delete

<update id="updateByPrimaryKey" parameterType="com.lzx.entity.PhoneList">
    update phone_list set phone = #{phone},name = #{name} where id = #{id}
  </update>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from phone_list where id = #{id}
  </delete>

三、select元素

select 元素有很多属性允许你配置,来决定每条语句的作用细节。

<select
  id="selectPerson"
  parameterType="int"
  parameterMap="deprecated"
  resultType="hashmap"
  resultMap="personResultMap"
  flushCache="false"
  useCache="true"
  timeout="10000"
  fetchSize="256"
  statementType="PREPARED"
  resultSetType="FORWARD_ONLY">
SelectAttributes
属性 描述
id 在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType 将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为MyBatis可以通过TypeHandler推断出具体传入语句的参数,默认值为unset。
parameterMap 这是引用外部parameterMap的已经被废弃的方法。使用内联参数映射和parameterType属性。
resultType 从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用resultType或resultMap,但不能同时使用。
resultMap 外部resultMap的命名引用。结果集的映射是MyBatis最强大的特性,对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解。使用resultMap或resultType,但不能同时使用。
flushCache 将其设置为true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。
useCache 将其设置为true,将会导致本条语句的结果被二级缓存,默认值:对select元素为true。
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为unset(依赖驱动)。
fetchSize 这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为unset(依赖驱动)。
statementType STATEMENT,PREPARED或CALLABLE的一个。这会让MyBatis分别使用Statement,PreparedStatement或CallableStatement,默认值:PREPARED。
resultSetType FORWARD_ONLY,SCROLL_SENSITIVE或SCROLL_INSENSITIVE中的一个,默认值为unset(依赖驱动)。
databaseId 如果配置了databaseIdProvider,MyBatis会加载所有的不带databaseId或匹配当前databaseId的语句;如果带或者不带的语句都有,则不带的会被忽略。
resultOrdered 这个设置仅针对嵌套结果select语句适用:如果为true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false
resultSets 这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的。

简单应用

 <select id="selectAll" resultType="com.lzx.entity.PhoneList">
    select id, phone, name from phone_list
  </select>

传递多个参数

//定义接口里面的方法

//方式一:使用注解
 Author queryByNameAndCity(@Param("name") String name,@Param("city") String city);
//方式二:使用实体类
 Author queryByNameAndCity(Author author);

<!--xml实现-->
<select id="queryByNameAndCity" resultType="com.lzx.entity.Author">
      select * from author where name = #{name} and city = #{city}
</select>

分页参数RowBounds

MyBatis中的分页是物理分页的方式。即先取出所有的对象放到一个集合里面,当你需要分页时,从集合中查找你所需要的结果集。这种方式当数据量较小时可以使用,如果数据量过大,它会占用过多的内存,给电脑造成很大的压力,所以这种方式不推荐使用。因为市面上有许多的数据库,MyBatis提供了这种通用的方式。为了提高分页效率,我们可以使用插件来实现。

//使用起来十分简单,只需给接口添加一个RowBounds参数即可
public List<Author> findByPage(@Param("name") String name,
@Param("city") String city,RowBounds rowBounds);

<!--xml实现时不需要rowBounds参数-->
<select id="queryByNameAndCity" resultType="com.lzx.entity.Author">
      select * from author where name = #{name} and city = #{city}
</select>

使用ResultMap作为结果集映射

resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来, 并在一些情形下允许你做一些 JDBC 不支持的事情。 实际上,在对复杂语句进行联合映射的时候,它很可能可以代替数千行的同等功能的代码。 ResultMap 的设计思想是,简单的语句不需要明确的结果映射,而复杂一点的语句只需要描述它们的关系就行了。实际开发中ResultMap多用于为多表连接查询定制结果集。

//定义一个结果集
<resultMap id="userResultMap" type="User">
  <id property="id" column="user_id" />
  <result property="username" column="user_name"/>
  <result property="password" column="hashed_password"/>
</resultMap>

<!--使用结果集-->
<select id="selectUsers" resultMap="userResultMap">
  select user_id, user_name, hashed_password
  from some_table
  where id = #{id}
</select>

这段代码里面,type代表使用哪个类作为映射类,可以使用别名或完全限定名。子元素id代表标识列(主键),property代表POJO(普通java对象)中的属性,column代表数据库里面的字段。

sql元素

这个元素可以被用来定义可重用的 SQL 代码段,可以包含在其他语句中。它可以被静态地(在加载参数) 参数化. 不同的属性值通过包含的实例变化.

简单使用

扫描二维码关注公众号,回复: 4354919 查看本文章
<!--定义-->
<sql id="authorColumns">
	name,address,phone
</sql>

<!--使用-->
<select id="getAuthor" parameterType="long" resultType="Author">
	select <include refid="authorColumns" /> from author where id = #{id}
</select>

传递变量给sql元素

<!--定义-->
<sql id="authorColumns">
	${alias}.name,${alias}.address,${alias}.phone
</sql>

<!--使用-->
<select id="getAuthor" parameterType="long" resultType="Author">
	select 
		<include refid="authorColumns">
			<property name = "alias" value = "a"/>			
		</include> 
	from author a where id = #{id}
</select>

了解#和$的区别

使用 # 时,代表这是一个参数,类似jdbc中使用prepareStatement通过 来设置参数; 使用 $ 代表这是一个字符串,会与sql语句连接起来。

N+1问题

在数据库中,如果已经完成了N个关联关系完成了级联,如果再加入一个关联东西,就变成了N+1个级联,所有的SQL都会被执行,有很多的数据都不是我们所需要的数据,造成了资源浪费,这就是N+1问题。
为了应对此问题,MyBatis提供了延迟加载的功能。

延迟加载

延迟加载就是我们一次性把常用的级联数据通过SQL直接查询出来,对于不常用的级联数据只有等到用时采取出,这些不常用的级联数据就可以采用延迟加载的功能。延迟加载的配置也十分简单,我们可以在mybatis的配置文件中进行配置。

<settings>
	<setting name="lazyLoadingEnable" value="true"/>
	<setting name="aggressiveLazyLoading" value="true"/>
</settings>

lazyLoadingEnableaggressiveLazyLoading这两个属性默认是关闭的。lazyLoadingEnable决定是否开启延迟加载,aggressiveLazyLoading控制是否采用层级加载,它们都是全局性的配置,有时不能解决我们的需求。除此之外,MyBatis中为我们提供了一个名为fetchType的属性给我们使用(只能在association、collection中使用),它有两个属性:

  • eager:获得当前的POJO后立即加载对应的数据。
  • lazy:获得当前的POJO后延迟加载对应的数据。

使用的前提是开启lazyLoadingEnabled即可。

四、缓存

基本介绍

MyBatis中允许使用缓存,分为一级缓存和二级缓存,你可以根据自己的需求进行配置。默认情况下是没有开启缓存的,除了局部的 session 缓存,可以增强变现而且处理循环 依赖也是必须的。要开启二级缓存,你需要在你的 SQL 映射文件中添加一行:

<cache/>

字面上看就是这样。这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句将会被缓存。
  • 映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。
  • 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
  • 根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。
  • 缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。
  • 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

所有的这些属性都可以通过缓存元素的属性来修改。比如:

<cache
  eviction="FIFO"<!--缓存策略 -->
  flushInterval="60000"<!--刷新时间,默认为null,正整数-->
  size="512"<!--缓存对象数,正整数默认1024-->
  readOnly="true"/><!--是否只读-->

建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会 导致冲突。

可用的收回策略有:

  • LRU – 最近最少使用的:移除最长时间不被使用的对象。(默认)
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
  • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

使用自定义缓存

cache接口

public interface Cache {
  String getId();
  int getSize();
  void putObject(Object key, Object value);
  Object getObject(Object key);
  boolean hasKey(Object key);
  Object removeObject(Object key);
  void clear();
}

自定义缓存必须要实现 org.mybatis.cache.Cache 接口。虽然复杂,我们使用时简单的给定它做什么即可。

配置自定义缓存

<cache type="com.ssm.lzx.cache.RedisCache">
	<property name="host" value="localhost"/>
</cache>

默认情况下,语句可以这样来配置:

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

flushCache代表是否刷新缓存,对于select、insert、update、delete都有效。useCache是select特有的,代表是否使用缓存。

五、存储过程

MyBatis对存储过程也提供了支持,对于存储过程的3种参数:**输入(IN)、输出(OUT)、输入输出(INOUT)**提供了支持。对于这三种参数,MyBatis已近为我们定义好了:

  • #{id,mode=IN}
  • #{name,mode=OUT}
  • #{sex,mode=INOUT}

简单调用

<!--statementType的参数告诉MyBatis,我们调用了一个存储过程-->
<select id="getCustomer" parameterType="com.ssm.lzx.Customer" statementType="CALLABLE">
	{call get_customer(
		#{id, mode=IN, jdbcType=INTEGER},
		#{name, mode=OUT, jdbcType=VARCHAR},
		#{sex, mode=INOUT, jdbcType=VARCHAR}
	)}	
</select>

猜你喜欢

转载自blog.csdn.net/Black1499/article/details/83373825