第三章 映射器

第三章 映射器

映射器的主要元素

SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):

  • cache – 该命名空间的缓存配置。
  • cache-ref – 引用其它命名空间的缓存配置。
  • resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
  • parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
  • sql – 可被其它语句引用的可重用语句块。
  • insert – 映射插入语句。
  • update – 映射更新语句。
  • delete – 映射删除语句。
  • select – 映射查询语句。
select元素

select元素帮助我们从数据库读取数据,组装数据给业务层。

select允许配置很多属性来配置每条语句的行为细节

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

说明:

(1)传递多个参数

​ ①使用Map传递参数,可读性比较差

​ ②使用注解的方式传递,多参数调用比较困难

​ @Param

​ ③使用JavaBean,参数多于5个建议使用

(2)使用resultMap映射结果集

​ 简单使用

<resultMap id="userMapper" type="user">
    <result javaType="string" jdbcType="VARCHAR" column="name" property="name" typeHandler="mybatis.MyStringTypeHandler"/>
    <result column="age"  property="age" />
</resultMap>
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">
属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
parameterMap用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys(仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。
keyProperty(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn(仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。
databaseId如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。

使用:

(1)user表的id必须时自增字段

(2)useGeneratedKeys=“true” keyProperty=“id”

<insert id="insertUser" parameterType="user" useGeneratedKeys="true" keyProperty="id">
    insert into user(name,age) value (#{name},#{age});
</insert>
sql元素

可重用的SQL代码块,以便在其他语句中使用

(1)定义

<sql id="userColumns">name,age </sql>

(2)使用

<select id="selectUsers" resultType="map">
  select
    <include refid="userColumns"></include>,
  from user
</select>
resultMap结果映射器

resultMap 元素是 MyBatis 中最重要最强大的元素.ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

(1)resultMap元素的构成

<resultMap id="userMapper1" type="user">
    <constructor/>
   
    <id/>
    <result/>
    
    <association property=""/>
    <collection property=""/>
    <discriminator javaType="">
        <case value=""></case>
    </discriminator>
</resultMap>
  • constructor - 用于在实例化类时,注入结果到构造方法中
    • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
    • arg - 将被注入到构造方法的一个普通结果
  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
  • result – 注入到字段或 JavaBean 属性的普通结果
  • association – 一个复杂类型的关联;许多结果将包装成这种类型
    • 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
  • collection – 一个复杂类型的集合
    • 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
  • discriminator – 使用结果值来决定使用哪个resultMap
    • case – 基于某些值的结果映射
      • 嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射

id & result属性

属性描述
property映射到列结果的字段或属性。如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。
column数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。
javaType一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
jdbcTypeJDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。
typeHandler我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。

(2)使用map存储结果集

<select id="selectUsers" resultType="map">
  select id, name
  from user
  where id = #{id}
</select>

(3)使用POJO存储结果集

<select id="selectUsers" resultType="bases.User">
  select id, name
  from user
  where id = #{id}
</select>

(4)级联

​ 1》如果我们获取用户信息的时候,还想获取用户的身份证信息,我们把这种情况叫做级联

​ 2》级联中存在三种对应关系:一对一,一对多,多对多

​ 3》在mybatis中级联分为三种:

​ association、代表一对一,和身份证就是一对一的级联关系

①User有一个SelfIdCard的属性

package bases;

/**
 * @author PitterWang
 * @create 2020/6/9
 * @since 1.0.0
 */
public class User {
   private Long id;

   private String name;

   private Long age;

   private SelfIdCard selfIdCard;
   public User() {
   }

   public User(String name, Long age) {
      this.name = name;
      this.age = age;
   }



   public Long getId() {
      return id;
   }

   public void setId(Long id) {
      this.id = id;
   }

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public Long getAge() {
      return age;
   }

   public void setAge(Long age) {
      this.age = age;
   }

   @Override
   public String toString() {
      return "User{" +
            "id=" + id +
            ", name='" + name + '\'' +
            ", age=" + age +
            '}';
   }

   public SelfIdCard getSelfIdCard() {
      return selfIdCard;
   }

   public void setSelfIdCard(SelfIdCard selfIdCard) {
      this.selfIdCard = selfIdCard;
   }
}
package bases;

/**
 * 〈身份表〉
 *
 * @author PitterWang
 * @create 2020/6/12
 * @since 1.0.0
 */
public class Position {

   private Long userId;

   private String name;

   public Position() {
   }

   public Position(Long userId, String name) {
      this.userId = userId;
      this.name = name;
   }

   public Long getUserId() {
      return userId;
   }

   public void setUserId(Long userId) {
      this.userId = userId;
   }

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

}

​ ②配置文件中使用association

<resultMap id="userMapper" type="user">
    <id column="id" property="id"></id>
    <result javaType="string" jdbcType="VARCHAR" column="name" property="name"/>
    <result column="age"  property="age" />
    <association property="selfIdCard" column="id" select="mybatis.UserMapper.getUserSelfIdCard"/>
</resultMap>
<resultMap id="selfIdCardMapper" type="selfIdCard">
    <result column="idcard" property="idcard"/>
    <result column="atWhere"  property="atWhere" />
</resultMap>
    <select id="getUserByName" parameterType="java.lang.String" resultMap="userMapper">
        select * from user where name = #{name }
    </select>
    <select id="getUserSelfIdCard" parameterType="java.lang.Long" resultMap="selfIdCardMapper">
            select * from  selfidcard where user_id = #{userId }
    </select>

​ collection、代表一对多

​ ①User有一个Position List的属性

package bases;

import java.util.List;

/**
 * @author PitterWang
 * @create 2020/6/9
 * @since 1.0.0
 */
public class User {
   private Long id;

   private String name;

   private Long age;

   private SelfIdCard selfIdCard;

   private List<Position> positions;
   public User() {
   }

   public User(String name, Long age) {
      this.name = name;
      this.age = age;
   }



   public Long getId() {
      return id;
   }

   public void setId(Long id) {
      this.id = id;
   }

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public Long getAge() {
      return age;
   }

   public void setAge(Long age) {
      this.age = age;
   }

   @Override
   public String toString() {
      return "User{" +
            "id=" + id +
            ", name='" + name + '\'' +
            ", age=" + age +
            '}';
   }

   public SelfIdCard getSelfIdCard() {
      return selfIdCard;
   }

   public void setSelfIdCard(SelfIdCard selfIdCard) {
      this.selfIdCard = selfIdCard;
   }

   public List<Position> getPositions() {
      return positions;
   }

   public void setPositions(List<Position> positions) {
      this.positions = positions;
   }
}
package bases;

/**
 * 〈身份表〉
 *
 * @author PitterWang
 * @create 2020/6/12
 * @since 1.0.0
 */
public class Position {

   private Long userId;

   private String name;

   public Position() {
   }

   public Position(Long userId, String name) {
      this.userId = userId;
      this.name = name;
   }

   public Long getUserId() {
      return userId;
   }

   public void setUserId(Long userId) {
      this.userId = userId;
   }

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

}

​ ②配置文件中使用collection

<resultMap id="userMapper" type="user">

    <id column="id" property="id"></id>
    <result javaType="string" jdbcType="VARCHAR" column="name" property="name"/>

    <result column="age"  property="age" />

    <association property="selfIdCard" column="id" select="mybatis.UserMapper.getUserSelfIdCard"/>

    <collection property="positions" column="id" select="mybatis.UserMapper.getPosition"/>
</resultMap>

​ discriminator 鉴别器,它可以根据实际选择采用那个类作为实例,允许你根据特定的条件去关联不同的结果集。比如人分为男人和女人,但是,实例化一个人的时候,要根据情况去去实例化时男人还时女人

<resultMap id="vehicleResult" type="Vehicle">
  <id property="id" column="id" />
  <result property="vin" column="vin"/>
  <result property="year" column="year"/>
  <result property="make" column="make"/>
  <result property="model" column="model"/>
  <result property="color" column="color"/>
  <discriminator javaType="int" column="vehicle_type">
    <case value="1" resultMap="carResult"/>
    <case value="2" resultMap="truckResult"/>
  </discriminator>
</resultMap>

<resultMap id="carResult" type="Car">
  <result property="doorCount" column="door_count" />
</resultMap>

<resultMap id="truckResult" type="Car1">
  <result property="doorCount" column="door_count" />
</resultMap>

或者可以使用如下方法
<resultMap id="vehicleResult" type="Vehicle">
  <id property="id" column="id" />
  <result property="vin" column="vin"/>
  <result property="year" column="year"/>
  <result property="make" column="make"/>
  <result property="model" column="model"/>
  <result property="color" column="color"/>
  <discriminator javaType="int" column="vehicle_type">
    <case value="1" resultType="carResult">
      <result property="doorCount" column="door_count" />
    </case>
    <case value="2" resultType="truckResult">
      <result property="boxSize" column="box_size" />
      <result property="extendedCab" column="extended_cab" />
    </case>
    <case value="3" resultType="vanResult">
      <result property="powerSlidingDoor" column="power_sliding_door" />
    </case>
    <case value="4" resultType="suvResult">
      <result property="allWheelDrive" column="all_wheel_drive" />
    </case>
  </discriminator>
</resultMap>

​ 4》使用级联的问题

​ 性能和N+1问题:可以方便的处理获取数据了,多层关联时,超过三层就尽快少用,因为不利于维护,并且复杂度增加。

​ 如果我们我们去一个用户信息,就把这个用户下的所有的信息都查出来了,这样就照成了N+1问题,为了避免这样问题发生,考虑延迟加载

​ 延迟加载,当我们不使用的时候,我们不去加载,但使用时才去加载。

​ 开启延迟加载的方法时再mybatis全局配置中加入

<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
</settings>
缓存cache

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

​ Mybatis的默认开启一级缓存是相对于sqlSession而已。在参数和SQL完全一样的情况下,我们使用一个sqlSession对应调用同一个Mapper方法,往往只执行一次Sql,因为第一次查完反正缓存里,以后再查时去缓存的数据

​ MyBatis默认不开启二级缓存是相对于SqlSessionFactory,二级缓存开启需要配置。,主要,如果开启二级缓存,返回的POJO必须实现Serializable接口。

开启这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。

  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。

  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。

  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。

  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。

  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

    cache的几个属性

    <cache
      eviction="FIFO"  //清除策略
      flushInterval="60000"
      size="512"
      readOnly="true"/>
    

    ①清除策略

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

    ②flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。

    ③size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

    ④readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

    (2)自定义缓存

    ①需要实现 org.apache.ibatis.cache.Cache 接口

    ②开启使用自定义缓存,并设置参数

    cache type="com.domain.something.MyCustomCache">
      <property name="cacheFile" value="/tmp/my-custom-cache.tmp"/>
    </cache>
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值