第三章 映射器

第三章 映射器

映射器的主要元素

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。
resultSetType FORWARD_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 来保证行为与期望的相一致。
jdbcType JDBC 类型,所支持的 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>
    

猜你喜欢

转载自blog.csdn.net/weixin_35133235/article/details/106750818
今日推荐