MyBatis 从浅入深 随笔整理

MyBatis ?

archetypeCatalog = internal

本文档单独出现的 _parameter 都标识为变量名

一. 三个基本要素:

核心接口和类

MyBatis 核心配置文件

SQL 映射文件

二.核心接口和类:

结构图:

(1) 每个 MyBatis 的哟 ing 有都以一个 SqlSessionFactory 对象的实例为核心

(2) 首先获取 SqlSessionFactoryBuilder 对象,可以根据 XML 配置文件或 Configuration 类的实例构建该对象

(3) 然后获取 SqlSessionFactory 对象,该对象实例可以通过 SqlSessionFactoryBuilder 对象来获得

(4) 有了 SqlSessionFactory 对象之后,就可以进而获取 SqlSession 实例, SqlSession 对象中完全包含以数据库为背景的所有执行 SQL 操作方法。可以用该实例来直接执行已映射的 SQL 语句

1.SqlSessionFactoryBuilder

作用:

构建 SqlSessionFactory

Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sfb = new SqlSessionFactoryBuilder().build(reader);

特点:

用过即丢

声明周期和作用域:

SqlSessionFactoryBuilder 是利用 XML 或者 Java 编码获得资源来购进啊 SqlSessionFactory 的,通过它可以构建多个 SessionFactory 。它的作用就是一个构建器,一旦我们构建了 SqlSessionFactory ,它的作用就已经完结,失去了存在的意义,这时我们就应该毫不犹豫的废弃它,将它回收。所有它的生命周期只存在与方法的局部,它的作用就是生成 SqlSessionFactory 对象

2.SqlSessionFactory

作用:

创建 SqlSession 实例

SqlSession sqlSession = sfb.openSession();

生命周期和作用域

SqlSessionFactory 对象一旦创建,就会在整个应用过程中始终存在。没有理由去销毁或再创建它,并且应用运行中也不建议多次创建实例,因此,最佳作用域是 Application

创建 SqlSessionFactory 实例时一般放在静态代码块中

而,最佳方案是使用依赖注入 ---Spring 框架来管理 SqlSessionFactory 的单例生命周期

3.SqlSession

作用:

是用于执行持久化操作的对象,类似于 JDBC 终端 Connection 。提供了面向数据库执行 SQL 命令所需的所有方法,可以通过 SqlSession 实例直接运行已映射的 SQL 语句

生命周期和作用域:

一个 SqlSession 对象对应着一次会话。非永久,用完即关,及时释放资源( close() 方法)

线程方面:

每个线程都有自己的 SqlSession 实例, SqlSession 实例不能被共享,也不是线程安全的。因此最佳的作用域范围是 request 作用域或者方法体作用域

两种使用方式:

1. 通过 SqlSession 实例来直接执行已映射的 SQL 语句

(1) 添加 xx.xml 文件 SQL 映射节点

(2) 根据需求利用 SqlSession 实例直接点出需要使用的方法,(方法传参 SQL 映射节点的 id 值)

2. 基于 mapper 接口方式操作数据

(1) 添加 xx.xml 文件 操作 SQL 节点

(2) 创建绑定映射语句的接口 xxx.java 并提供 ---> 和 SQL 映射节点 id 值相同的接口方法

(3) 利用 SqlSession 实例调用 getMapper ()方法 ,传参为接口名 .class    然后点出相应接口方法

注意: SqlSession 实例用完即关

三.核心配置文件:

Mybatis-config.xml

文档结构:

注意先后顺序!!!

Configuration 配置 (根节点)

Properties 可以配置在 Java 属性配置文件中

Settings 修改 MyBatis 在运行时的行为方式

TypeAliases 为 Java 类型命名一个别名

TypeHandlers 类型处理器

ObjectFactory 对象工厂

Plugins 插件

Environments 环境

Environment 环境变量

transactionManager 事物处理器

dataSource 数据源

Mappers 映射器

1. configuration 元素:

整个 xml 文件的根节点,相当于 MyBatis 的总管,所有配置信息都会存放在它里面, MyBatis 还提供了设置这些配置信息的方法:

(1) 可以从配置文件李获取属性值

(2) 通过程序直接设置

2. Properties 元素:

其描述都是外部化,可替代的属性

属性获取方式:

1. 引入外部 properties 文件

<properties resource=”数据源路径” />

引入外部文件后,在本 xml 文件中可直接使用 ${key 值 } 来获取对应的 value 值

2. 内部配置 ---> 元素中直接配置 property 属性

<properties>
<property name=”key值” value=”value值” />
......
</properties>

配置节点完成后, 在本 xml 文件中可直接使用 ${key 值 } 来获取对应的 value 值

3. 内外部结合使用

<properties resource=”外部数据源路径”>
<property name=”key值” value=”value值” />
......
</properties>

配置节点完成后, 在本 xml 文件中可直接使用 ${key 值 } 来获取对应的 value 值

如果外部配置和内部配置 配置了相同的 key   那么: resource 属性值的优先级高于 property 子节点配置的值

3. Settings 元素

作用是设置一些非常重要的设置选项,用于设置和改变 MyBatis 运行中的行为

设置项

描述

允许值

默认值

cacheEnabled

对在此配置文件下的所有 cache 进行全局性开 / 关设置

True|false

True

lazyLoadingEnabled

全局性设置懒加载。如果设为 false ,则所有相关联的都会被初始化加载

True|false

True

autoMappingBehavior

MyBatis 对于 resultMap 自动映射的匹配级别

NONE|PARTIAL|FULL

PARTIAL

Settings 标签配置的是配置 MyBatis 框架运行时的一些行为,例如:

(1) 缓存

(2) 延迟加载

(3) 结果集控制

(4) 执行器

(5) 分页设置

(6) 命名规则

等一系列控制性参数,其所有的 settings 配置都放在父标签 settings 标签中

4. typeAliases 元素

作用是配置类型别名,通过与 MyBatis 的 SQL 映射文件相关联,减少输入多余的完整类名

两种方式:

1. 直接指定到类

<typeAliases>
<typeAlias alias=”别名” type=”类的全路径” />
......
</typeAliases>

弊端:

如果一个项目有多个 POJO 的时候需要一一配置

2. 指定包,检索类

解决上面直接指定到类的弊端

通过 package 的 name 属性直接指定包名, MyBatis 会自动扫描指定包下的 JavaBean ,并默认设置一个别民,默认名称为 JavaBean 的非限定类名

<typeAliases>
<typeAlias alias=”别名” type=”某包的全路径” />
</typeAliases>

Xx.xml 配置:

直接写包下的某个类名即可

另外:

对于基础数据类型等, Mybatis 已经为许多常见的 Java 类型内建了相应的类型别名,一般都是与其映射类型一致,并且它们都是大小写不敏感的

5. Environments 元素:

MyBatis 可以配置多套运行环境,如:

(1) 开发环境

(2) 测试环境

(3) 生成环境

等,我们可以灵活选择不同的配置,从而将 SQL 映射应用到不同的数据库环境上。

这些不同的运行环境,就可以听过 environments 元素来配置,但是不管增加几套运行环境,都必须要明确选择出当前的唯一一个运行环境。

Because :

每个数据库都是对应一个 SqlSessonFactory 实例,需要指明哪个运行环境将被创建,并把运行环境中设置的参数传递给 SqlSessionFactoryBuilder 。

<environments default="development">
    <!-- 开发环境 -->
    <environment id="development">
        <transactionManager type="JDBC">
            <property name="" value=""/>
        </transactionManager>
        <dataSource type="UNPOOLED">
            <property name="driver" value="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${user}"/>
            <property name="password" value="${pwd}"/>
        </dataSource>
    </environment>
    <!-- 测试环境 -->
    <environment id="test">
        <transactionManager type=""></transactionManager>
        <dataSource type=""></dataSource>
    </environment>
</environments>

几个关键点:

(1) 默认的运行 ID

通过 environments 节点的 default 属性来指定当前的运行环境 ID ,对于子节点 environment 环境 ID 的命名要确保唯一性

(2) TransactionManager 事物管理器

设置其类型为 JDBC ( MyBatis 有两种事物管理类型,即 JDBC , MANAGED ),直接使用 JDBC 的提交和回滚功能,依赖于数据源获得连接来管理事物的声明周期

(3) 配置数据库连接对象

DataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

MyBatis 提供了三种数据源类型

(1)UNPOOLED

(2)POOLED

(3)JNDI

一般使用 POOLED 数据源类型。该类型的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间,是 MyBatis 实现的简单的数据库连接池类型,它使数据库的连接可被复用,不必在每次请求时都去创建一个物理连接

对于高并发的 Web 应用是一种流行的处理方式,有利于快速响应请求

6. Mappers 元素:

映射器,用来定义 SQL 的映射语句,我们只需要告诉 MyBatis 去哪里找到这些 SQL 语句即可,即去哪里找相应的 SQL 映射文件(可以使用类资源或者 URL 资源等方式)

仅只是找到指定映射文件,其更详细的信息配置在每个 SQL 映射文件中

常用的两种映射方式:

1. 使用类资源路径获取路径

<mappers>
    <mapper resource="本项目中SQL映射文件 资源路径"/>
</mappers>

2. 使用 URL 获取资源

<mappers>
    <mapper url="本地或者网络上的SQL映射文件 资源路径"/>
</mappers>

四. DTD 文件的引入:

用 IDEA 不需要手动引入 DTD 文件   略略略!!!

五. SQL 映射文件:

MyBatis 强大之处: SQL 映射语句    魅力所在 非常简单

使用 SQL 映射文件配置可减少 50% 以上的代码量

MyBatis 专注于 SQL 极大限度地进行 SQL 调优,保证性能

几个顶级元素配置:

1. mapper :

映射文件的根元素节点,只有一个属性 namespace (命名空间)

namespace 作用:

1. 区分不同的 mapper ,全局唯一

2. 保定 DAO 接口,即面向接口。

① 当 namespace 绑定一个接口后,可以不用写该接口的实现类, MyBatis 会通过接口的完整限定名查找到对应的 mapper 配置来执行 SQL 语句。因此 namespace 的命名必须要跟接口同名(包结构 + 接口名)

namespace 命名要求:

1. 命名必须跟某个 DAO 接口命名,同属于 DAO 层,故代码结构上,映射文件与该 DAO 接口放置在同一 package 下,如果不放在同一 package 下,需指定其对应接口的资源路径,,接口命名,配置文件命名习惯上以 Mapper 结尾

2. 在不同的 mapper 文件中,子元素的 id 可以相同, MyBatis 通过 namespace 和子元素的 id 进行联合区分。接口中的方法与映射文件中 SQL 语句 id 应一一对应

2. chche :

配置给定命名空间的缓存

3. cache-ref :

从其他命名空间引用的缓存配置

4. resultMap :

用来描述数据库结果集和对象的对应关系

5. Sql :

通过 sql 片段达到代码重复利用

可以重用的 SQL 块,也可以被其他语句使用

我一般用来封装常用的表字段

如:

<sql id="co">
  `SUBWAYNAME`,`STARTSTATION`,`ENDSTATION`,`STATIONNUM`,`STARTIME`,`PRICE`
</sql>

使用时:

在标签内插入语句

<include refid="co"/>

6.Insert

映射插入语句

<insert id="add" parameterType="com.metro.entity.SubwayInfo">
  INSERT INTO `subwayInfo`(
  <include refid="co"/>
  )
  VALUES (#{subwayName},#{startStation},#{endStation},#{stationNum},#{starTime},#{price})
</insert>
 

7. Update :

映射更新语句

<update id="up" parameterType="com.metro.entity.SubwayInfo">
  UPDATE `subwayInfo`
    <set>
      <if test="subwayName!=null and "".equals(subwayName.trim())">SUBWAYNAME=#{subwayName},</if>
      <if test="startStation!=null and "".equals(startStation.trim())">STARTSTATION=#{startStation},</if>
    </set>
  WHERE id=#{id}
</update>

8. Delete :

映射删除语句

<delete id="del" parameterType="INTEGER">
  DELETE from subwayInfo where id=#{id}
</delete>

9. Select :

映射查询语句

<select id="listAll" resultMap="subwayInfo">
  SELECT id,<include refid="co"/> FROM subwayInfo
</select>

别名与 Java 类型映射:

别名

映射类型

string

String

byte

Byte

long

Long

short

Short

int

Integer

integer

Integer

arraylist

Arraylist

double

Double

float

Float

boolean

Boolean

date

Date

map

Map

hashmap

HashMap

list

List

SQL 映射标签详解:

1.resultMap

映射查询结果,两种方案:

1) 使用 resultType 做自动映射

<!--resultType 大小写不敏感:不需 resultMap 标签映射,,依靠实体类字段名与数据库相应字段名相同进行映射 
   严重受限 
  -->

字段名与 POJO 的属性名必须一致。若不一致,则需要给字段起别名,保证别名与属性名一致

不推荐使用

2) 通过 resultMap 来映射自定义结果

使用 resultMap 标签做自定义结果映射,字段名可以不一致,并且还可以指定要显示的列,比较灵活,应用广泛

<resultMap type="com.metro.entity.SubwayInfo" id="subwayInfo">
  <!--typeHandler="" 可做数据类型转换-->
<!--column="" 数据库字段property=”” 实体类字段-->
  <id column="ID" jdbcType="INTEGER" property="id" />
  <result column="SUBWAYNAME" jdbcType="VARCHAR" property="subwayName"/>
  <result column="STARTSTATION" jdbcType="VARCHAR" property="startStation"/>
  <result column="ENDSTATION" jdbcType="VARCHAR" property="endStation"/>
  <result column="STATIONNUM" jdbcType="INTEGER" property="stationNum"/>
  <result column="STARTIME" jdbcType="VARCHAR" property="starTime"/>
  <result column="PRICE" jdbcType="INTEGER" property="price"/>
</resultMap>
 

jdbcType : 插入空值时,需要指定 jdbcType

Mybatis insert 空值报空值异常,但是 pl/sql 不会提示错误,主要原因是 mybayis 无法进行转换

各属性标签:

Id :唯一标识,此 id 用于 sql 映射元素 resultMap 属性的引用

Type :表示该 resultMap 的映射结果类型

result 子节点:用于标识一些简单属性

返回类型 resultType 与 resultMap ?

这里的 resultType 与 resultMap 跟上面所说的那个不一样,,这里探讨的是 Sql 映射标签  返回类型

1.resultType:

直接表示返回类型,包括基础数据类型和复杂数据类型

2. resultMap :

对外部 resultMap 定义的引用,对应外部 resultMap 的 id ,表示返回的结果集映射到哪一个 resultMap 上。应用场景:数据库字段信息与对象属性不一致或者需要做复杂的联合查询以便自由控制映射结果

3. resultType 和 resultMap 的关联

无论是 resultType 还是 resultMap ,其实查询出来的每个字段值都放在一个对应的 Map 里面,其键是字段名,值是其对应的值

然后呢,当 select 元素提供的返回值类型是 resultType 的时候, MuBatis 会将 Map 里面的键值对取出赋给 resultType 所指定的对象对应的属性 ( 即调用对应的对象里的属性的 setter 方法进行填充 )

··所以呢,其实每个查询的返回类型都是 resultMap

1) 只是当我们提供的返回值类型属性是 resultType 的时候,会自动把对应的赋值 resultType 所指定对象的属性

2) 我们提供的返回类型是 resultMap 的时候,因为 Map 不能很好的表示领域模型,我们就需要通过进一步的定义把它转化为对应的实体对象

开发经验:

当返回类型是 resultMap 时,主要用在进行复杂联合查询上

进行简单查询时,使用 resultType 足以

注意: 在 select 元素中, resultType 和 resultMap 本质上是一样的,都是 Map 数据结构

明确一点。。。两种绝对不能同时存在,只能二者选其一使用

4. resultMap 的自动映射级别

resultMap 自动映射级别默认的是 PARTIAL ,为自动装配,他会自动装配所有查询出来并且实体类里拥有的字段

如果想要选择部分字段进行映射,希望没有映射的字段是不能在后台查询并输出的,则需要在核心配置文件 settings 中设置 resultMap 的自动映射级别( autoMappingBehavior )为 NONE ,即禁止自动匹配

<settings>
    <setting name="autoMappingBehavior" value="NONE"/>
</settings>

注意点:

在 MyBatis 中,使用 resultMap 能够进行自动映射匹配的前提是字段名和属性名需要一致,在默认映射级别( PARTIAL )情况下:

1) 若一致,即使没有做属性名和字段名的匹配映射,也可以在后台获取到未匹配过的属性值

2) 若不一致,且在 resultMap 里没有做映射,那么就无法在后台获取并输出

2.Select

属性:

1) Id

命名空间中唯一的标识符,可以被用来引用这条语句

由于我们常用的映射方法是基于 Mapper 接口,所有 id 值需跟对应的接口方法名一致

2) ParameterType :

标识查询语句传入参数的类型的 完全限定名或别名 。

支持继承数据类型和复杂数据类型

MyBatis 有内建的类型别名,,上面有!!!

除了内建的类型别名外,还可以为自定义的类设置别名。。。在核心配置文件的 typeAliases 元素中设置

在映射文件中可直接使用别名,以减少配置文件的代码量

注:

1.  基本情况下只允许传入一个参数,如果要传入多个参数,,就要对多个参数进行封装后续还可以使用 @Param 注解实现多参数入参

2. Mybatis 传入参数类型可以是 Java 继承数据类型,但是只适用于一个参数的情况,通过 #{ 参数名 } 即可获取传入的值。若是多参数入参,需要复杂数据类型来支持,包括 Java 实体类, map ,通过 #{ 属性名 } 或者 #{Map 的 key} 来获取传入的参数值

#{ 值 } :写法是 OGNL 表达式

如果是 实体类对象 和基本数据类型多参数传参

取值时: 实体类对象 .属性名

3) ResultType :

查询语句返回结果类型的完全限定名或别名,命名与 ParameterType 大体一致

3. Insert , Update , Delete

属性:

1) id :

与 select 元素的 id 一样,是命名空间中唯一的标识符,可以呗用来引用这条语句

2) ParameterType :

与 select 元素其属性一样,是传入参数的类型的完全限定名或别名

注意点:

对于增删改( insert , update , delete )这类数据库更新操作

1) 该类型的操作本身默认返回执行 SQL 影响的行数,所以 DAO 层的接口方法的返回值一般设置为 int 类型。最好不要返回 boolean 类型

2)Insert , update , delete 元素中均没有 resultType 属性,只有查询操作需要对返回结果类型( resultType/resultMap )进行相应的指定

3) 在测试类中,当 sqlSession 执行完 sql 操作之后,需要进行 commit ,完成数据的插入操作。若在执行的过程中抛了异常,那么就必须在 catch 中进行回滚,以此来保证数据的一致性,同时设置 count 为 0

六. 使用 @param 注解实现多参数入参

int del(@Param("uid")Integer id,@Param("xxx")String x);

解析:

使用注解 @Param 来传入多个参数,并且注解内可以将变量名重命名为其他名字,,只会影响 SQL 映射文件的使用,不会影响 java 类中的真实使用。。在映射文件中使用 #{ 注解内的值 } 来获取其传入的值,,,

还有啊,使用注解,装配的入参,参数类型必须是引用类型(复杂类型或 String ,或者基本数据类型的包装类,对象等)使用 int 等值值类型,会报错

如果是 实体类对象 和基本数据类型多参数传参

取值时:

实体类: #{ 注解内的值 . 属性名 }

基本: #{ 名 }

扩展:

使用多参数入参,必须使用 @Param 注解方式,若不使用,则会报错,报错信息类似于 Pararmeter ‘参数名’ not found 。

Reason :

深入 MyBatis 源码,我们可以发现, MyBatis 的参数类型为 Map

1) 若使用 @Param 注解参数,那么就会记录指定的参数名为 key

2) 若参数前没有加 @Param ,那么就会使用“ param ” + 它的序号座位 Map 的 key 。

故而进行多参数入参时,若没有使用 @Param 指定参数 key ,那么在映射的 SQL 语句中获取不到 #{ 参数名 } ,从而报错

经验:

相信大家学了 @Param 注解入参就会有疑惑,既然有高级货,为什么不早拿出来???

其实呢,普通入参方式也不是一无是处,下面我们来谈论一下 《在 MyBatis 中参数入参,何时需要封装成对象入参,何时又需要使用多参数入参???》

来, let ’ s go   :

1) 一般情况下呢,超过 4 个参数最好封装成对象入参(特别是在常规的增加和修改操作时,字段较多,封装成对象比较方便,也省的一个个记参数名了,也不是)

2) 对于参数固定的业务方法呢,最好使用多参数入参,原因是这种方法比较灵活,代码的可读性高,可以清晰地看出接口方法中所需的参数是什么。并且对于固定的接口方法,参数一般是固定的,所以直接多参数入参即可,无需封装对象(比如修改个人密码,根据用户 id 删除用户,根据用户 id 查看用户明细,都可以采用这种方式)

需要注意的是:

当参数为基础数据类型时,不管是多参数入参还是单独一个参数,都需要使用 @Param 注解来进行参数的传递

还有一定记住一点。。使用多参数入参时。在 SQL 映射文件中使用 OGNL 表达式获取传开的值的时候 其 key 值是 @Param ()括号中的值

七. ResultMap 实现高级结果映射

我们先回顾一下前面提到的 resultMap 的基本配置

基本配置:

1>  属性

1)id : resultMap 的唯一标识

2)Type :标识该 resultMap 的映射结果类型(通常时 Java 实体类)

2>  字节点

1)id :一般对应数据库中该行的主键 id ,设置此项可以提升 MyBatis 性能

2)Result :映射到 JavaBean 的某个“简单类型”属性,如:基础数据类型,包装类等

子节点 id 和 result 均可实现最基本的结果集映射,将列映射到简单数据类型的属性。

这两者唯一不同的是 :在比较对象实例时 id 将作为结果集的标识属性。有助于提高整体性能,特别是应用缓存和潜逃结果映射的时候

高级结果映射,两个配置项:

1>Association

映射到 JavaBean 的某个“复杂类型”属性,比如 JavaBean 类,即 JavaBean 内部嵌套一个复杂数据类型( JavaBean )属性,这种情况就属于复杂类型的关联。

需要注意的是:

Association 仅处理一对一的关联关系(比如,每个用户都会有一个角色,两表关系一对一)

<resultMap type="com.metro.entity.User" id="user">
  <id column="ID" jdbcType="INTEGER" property="id" />
  <result column="NAME" jdbcType="VARCHAR" property="name"/>
  <result column="PWD" jdbcType="VARCHAR" property="pwd"/>
  <association property="role" javaType="Roel">
    <id property="rid" column="RID"/>
    <result property="rname" column="RNAME"/>
  </association>
</resultMap>
<select id="listAll" resultMap="user" parameterType="Integer">
  SELECT u.* r.id AS RID,r.name AS RNAME FROM user u,role r
  WHERE u.rid=r.id AND u.id=#{uid}
</select>

分析其属性:

Association 元素:

1>javaType:

完整 Java 类名或者别名。

1) 若映射到一个 JavaBean ,则 MyBatis 通常会自行检测到其类型;

2) 若映射到一个 HashMap ,则应该明确指定 JavaType ,来确保所需行为。此处为 Role

2>Property:

映射数据库列的实体对象的属性。此处为在 User 里定义的属性 role

其子节点元素:

1>Id:

不多说,看下面

2>Result:

不多说,看下面

上两者共同属性:

1)property :

映射数据库列的实体对象的属性,上面的例子为 Role 的属性

2)colunm :

数据库对象的列名或别名

注意:

Id 子元素 在嵌套结果映射中扮演了一个 非常重要 的角色 ,应该指定一个或者多个属性来唯一标识这个结果集。实际上,即使没有值得 id , MyBatis 也会工作,但是会导致严重的性能开销,所以最好选择尽量少的属性来唯一标识结果,主键或者联合主键均可

最后:

了解了关于 association 的基本用法以及使用场景,一些复用大佬,就会想到这个东东可不可以复用呢,,

答案是肯定的, association 提供了另一个属性: resultMap 。(相信说到这里大家已经指定怎么做了,每次就是跟 select 等映射标签一样,,搞呗, resultMap= “某 resultMap 元素 id 值”)通过这个属性可以扩展一个 resultMap 来进行联合映射,这样就可以使其结果映射重复使用。当然,若不需要复用,也可安装之前的写法,直接嵌套这个联合结果映射,根据具体业务而定

<resultMap type="com.metro.entity.User" id="user">
  <id column="ID" jdbcType="INTEGER" property="id" />
  <result column="NAME" jdbcType="VARCHAR" property="name"/>
  <result column="PWD" jdbcType="VARCHAR" property="pwd"/>
  <association property="role" javaType="Roel" resultMap="role"/>
</resultMap>

<resultMap id="role" type="Role">
  <id property="rid" column="RID"/>
  <result property="rname" column="RNAME"/>
</resultMap>

引用外部 resultMap 的好处:可以达到复用的效果,并且整体的结构较为清晰,特别适合 association 的结果映射比较多的情况

返回对象

Association 可以处理一对一的关联关系,那么对于一对多的关联关系的处理,它就心有余而力不足了,,,,,怎么办呢?嗯,用 collection 元素

2>Collection

作用和 association 元素的作用差不多一样,事实上,它们非常类似,也是映射到 JavaBean 的某个“复杂类型”属性,只不过这个属性是一个集合列表,即 JavaBean 内部嵌套一个复杂数据类型(集合)属性。和使用 association 元素一样,我们使用嵌套查询,或者从连接中嵌套结果集

<resultMap id="role" type="Role">
  <id property="rid" column="RID"/>
  <result property="rname" column="RNAME"/>
  <collection property="juris"  ofType="Juris" resultMap="ju"/>
</resultMap>
<resultMap id="ju" type="Juris">
  <id property="jid" column="JID"/>
  <result property="jname" column="JNAME"/>
</resultMap>

与 association 用法基本一致,就是多出来了一个 ofType 属性

ofType 属性呢:代表的是 Java 类名或者别名,即集合所包含的类型

返回集合

欢迎加入Java高级架构学习交流群:375989619

我们提供免费的架构资料 以及免费的解答 

不懂得问题都可以来问我们老师,之后还会有职业生涯规划,以及面试指导

我们每天晚上八点也有公开课免费学习:
10年架构师分享经验,Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并发等架构技术

加群条件:
1、具有1-5工作经验的,面对目前流行的技术不知从何下手,需要突破技术瓶颈的。
2、在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的。
3、如果没有工作经验,但基础非常扎实,对java工作机制,常用设计思想,常用java开发框架掌握熟练的。
4、觉得自己很牛B,一般需求都能搞定。但是所学的知识点没有系统化,很难在技术领域继续突破的。
5. 群号:375989619高级架构群备注好信息!

八. ResultMap 自动映射级别和 MyBatis 缓存

(一) resultMap 自动映射级别

resultMap 自动映射的三个匹配级别:

1> NONE:

禁止自动匹配

2> PARTIAL :

默认自动映射级别,自动匹配所以属性,有内部嵌套的( association 、 collection )的除外

3> FULL :

自动匹配所有,所有所有!!!

详解:

用法:

在核心配置中配置:

<settings>
    <setting name="autoMappingBehavior" value="匹配级别"/>
</settings>

NONE 禁止自动匹配不用多说,就是不自动匹配嘛,手动映射啥就匹配啥并且赋值,否则没值,并且上面也举过例子,所有不再做过多阐述

主要讲解 PARTIAL 与 FULL 的区别

可以私下里做个例子,发现没有设置 autoMappingBehavior 的时候,也就是默认情况下( PARTIAL ),若是普通数据类型的属性,会自动匹配所有,,,,,但是呢若是内部嵌套( association 或 collection )(集合或实体对象),那么输出结果就是 null 。也就是说它不会自动匹配,除非手工设置 autoMappingBehavior 的 value 为 FULL (自动匹配所有)

修改后你会发现 autoMappingBehavior 的 value 为 FULL (自动匹配所有)之后,未作映射的字段(任何类型)均有值输出

(二) MyBatis 缓存

如大多数持久化框架一样, MyBatis 提供了一级缓存和二级缓存的支持

一级缓存缓存的是 SQL 语句,二级缓存缓存的是结果对象

1. 一级缓存

一级缓存是基于 PerpetualCache ( MyBatis 自带)的 HashMap 本地缓存,作用范围为 session 域内,当 session flush 或者 close 之后,该 session 中所有的 cache 就会清空

配置:

因为 MyBatis 默认就是一级缓存,所以删除所有关于缓存的配置,就是一级缓存

2. 二级缓存

二级缓存就是 global caching ,它超出 session 范围之外,可以被所有的 SqlSession 共享,开启它只需要在 MyBatis 的核心文件 settings 中设置即可

三种配置方法:

为 cache 赋值: true 为二级缓存。 false 为一级缓存

1) MyBatis 的全局 cache 配置

在核心文件配置:

<settings>
    <setting name="cacheEnabled" value="false"/>
</settings>

2) 针对 mapper 的 namespace

1) 在 mapper 文件(也就是 SQL 映射文件)中设置缓存,默认情况下是没有开启缓存

2) 需要注意的是, global caching 发作用域是针对 mapper 的 namespace 而言的,即只有 namespace 内的查询才能共享这个 cache

<mapper namespace="SubwayInfo">
  <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
。。。。
</mapper>

代表创建了一个 LRU缓存,并每隔60秒刷新,最大存储512个对象,而且返回的对象被认为是只读的。

evicition收回策略,默认是LRU

( 1)LRU最近最少使用策略,一处做长时间不被使用的对象。

( 2)FIFO先进先出策略,按对象进入缓存的顺序来移除它们。

( 3)SOFT软引用策略,移除基于垃圾回收器状态和软引用规则的对象。

( 4)WEAK弱引用策略,更积极地移除基于垃圾收集器状态和弱引用规则的对象。

3) 针对个别查询进行调整

配置这个的前提是必须配置好上面 2 )针对 mapper 的 namespace

如果需要对个别查询进行调整,可以单独设置 cache :

<select id="listAll" resultMap="subwayInfo" useCache="true">
。。。
</select>

详解一二级缓存的区别:

一级缓存基于 sqlSession默认开启,在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap用于存储缓存数据。不同的SqlSession之间的缓存数据区域是互相不影响的。

一级缓存的作用域是 SqlSession范围的,当在同一个sqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据库中查询的数据写到缓存(内存),

第二次查询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。

需要注意的是,如果 SqlSession执行了DML操作(增删改),并且提交到数据库,MyBatis则会清空SqlSession中的一级缓存,这样做的目的是为了保证缓存中存储的是最新的信息,避免出现脏读现象。

当一个 SqlSession结束后该SqlSession中的一级缓存也就不存在了。

关闭一级缓存后,再次访问,需要再次获取一级缓存,然后才能查找数据,否则会抛出异常。

二级缓存是

mapper级别的缓存。使用二级缓存时,多个SqlSession使用同一个Mapper的sql语句去操作数据库,得到的数据会存在二级缓存区域,它同样是使用HashMap进行数据存储。相比一级缓存SqlSession,二级缓存的范围更大,多个Sqlsession可以共用二级缓存,二级缓存是跨SqlSession的。

二级缓存的作用域是 mapper的同一个namespace。不同的sqlSession两次执行相同的namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则第一次执行完毕会将数据库中查询的数据写到缓存,第二次查询会从缓存中获取数据,不再去底层数据库查询,从而提高效率。

最后:

对于 MyBatis 缓存的内容仅做了解即可,因为面对一定的数据量,内置的 Cache 方式就派不上用场了

并且对查询结果集做缓存并不是 MyBatis 框架擅长的,它专心做的应该是 SQL 映射,所有我们一般采用 OSCache 、 Memcached 等专门的缓存服务器来做更为合理

九. 动态 SQL 咯

很简单。。也就是标签元素,,没什么技术含量

记住拿过来用就好、

各标签元素

为了支持用户多样化操作

1.If

<if test="subwayName!=null and "".equals(subwayName.trim())">AND SUBWAYNAME=#{subwayName}</if>

""   代表空字符串     某些原因, “”不可用

test 内 的返回结果为 true 走标签内部结构

否则不进,注意,这里面没有 else 标签,要想实现 if-else 结构标签,后面有新引入的标签,这里不做详细讲解,请继续去阅读

2.Where

可以智能 的处理 where 和 and 所处的位置

因为我们要动态拼接, where 语法: where xxx=x and yyy=y

有时呢,我们拼接 sql 的时候 where 子句不好拼接,因为我们确定不了到底哪里是第一个条件的开头, where 到底拼接到哪个语句前面,,所以就诞生了这个 where 标签:

<select id="listAll" resultMap="subwayInfo" >
  SELECT id,<include refid="co"/> FROM subwayInfo

  <where>
    <if test="subwayName!=null and "".equals(subwayName.trim())">AND SUBWAYNAME=#{subwayName}</if>
    <if test="startStation!=null and "".equals(startStation.trim())">AND STARTSTATION=#{startStation},</if>

  </where>
</select>

在这里我们会发现 sql 因为有两个 if 条件不清楚到底会符合一条还是两条,或者说都不符合, where 将要放的位置会异常尴尬

然后呢,可有看到我们这个例子的 sql 我并没有写 where    为什么呢?因为无论执行的怎样我们的 where 都会为什么自动在最前面拼上 where 子句,,如果呢,后面标签内都不符合那么 where 不会拼接

做的很牛逼!!!

3.Choose

这里,,, choose ,就是上面所说的可以实现 if-else 结构的标签,,没有久等吧,嘿嘿嘿

Choose 标签呢,他又有两个子标签来配合他实现结构功能分别是:

When

If 语句或者 else if 语句

Otherwise

Else 语句

<choose> 
  <when test="">xx</when>
  <when test="">yy</when>
  <otherwise>zz</otherwise>
</choose>

比如上面的语句

最后只会出现一个值, xx 或者 yy 或者 zz

第一个 when 就充当了 if()

第二个 when 就充当了 else if()    

就算后面更多 when 也充当的是 else if() 语句   ( test 为 true 输出,但 when 只会执行一道,靠前者优先)

有人疑惑了,那最后的 else 呢?

Ok ,,,当然是之前没有出场的 otherwise 标签了, (所以条件不满足的时候走)

学过选择结构,不傻 的相信都 一点就懂,不多说

4.Set

Set 元素主要用于更新操作,它的主要功能和 where 元素差不多,主要在包含的语句前输出宇哥 set ,若包含的语句是以逗号结束的,会自动把改逗号忽略掉,再配合 id 元素就可以动态地更新需要修改的字段;而不需修改的字段,则可以不再更新(因为有的时候在 update 操作中使用多个 if 或者别的选择标签,若一部分没有执行,则导致在语句末尾残留多余的逗号, 解决此问题 )

Tip:

<update id="up" parameterType="com.metro.entity.SubwayInfo">
  UPDATE `subwayInfo`
    <set>
      <if test="subwayName!=null and "".equals(subwayName.trim())">
SUBWAYNAME=#{subwayName},
</if>
    <if test="startStation!=null and "".equals(startStation.trim())">
STARTSTATION=#{startStation},
</if>
    </set>
  WHERE id=#{id}
</update>
 

5.Trim

更为灵活的元素,,,可以替代之前的

Trim 元素也会自动识别其标签内是否有返回值,若有返回值,会在自己包含的内容前加上某些前缀 ( prefix ) ,也可在其后加上某些后缀 ( suffix ) ,也可把包含内容的首部某些内存覆盖(忽略) ( prefixOverrides ) ,或者把尾部的某些内容覆盖 ( suffixOverrides ) ,无可厚非的强大,可以利用 trim 替代 where 或者 set 元素,实现相同效果

小 Tip :

<trim prefix="where" prefixOverrides="and | or">
<if Test=””> and xx = x </if>
。。。
</trim>

不会报错,,前面的 and 会自动消除,,贼智能,,后缀处理同理

再解释一个各属性:

1> Prefix :

前缀,作用是通过自动识别是否有返回值后,在 trim 包含的内容上加上前缀,一般用于 where

2> Suffix :

后缀,作用是在 trim 包含的内容上加上后缀

3> prefixOverrides :

对于 trim 包含内容的首部进行指定内容(如 where 子句中的 and 和 or )的忽略。

4> suffixOverrides :

对于 trim 包含内容的首尾部进行指定内容的忽略。

6.Foreach

当 MyBatis 入参为数组类型的时候,就需要使用 foeach 来进行迭代取值了

Foreach 呢,主要用于构建 in 条件中,他可以在 sql 语句中迭代一个集合,,主要属性有:

1) Item :

表示集合中每个元素进行迭代时的别名

2) Index :

指定一个名称,用于表示在迭代过程中,每次迭代到的位置

3) Close :

表示该语句以什么结束(这里以 in 条件语句举例,,所以用“ ) ”结束,具体根据需求而定)

4) Open :

表示该语句以什么开始(这里以 in 条件语句举例,,所以用“ ( ”开始,具体根据需求而定)

5)Separator

表示在每次进行迭代之间以什么符号作为分隔符(这里以 in 条件语句举例,,,所以用“ , ”分隔)

6) Collection :

最关键并最容易出错的属性,需要格外注意,改属性必须指定 不同情况下,该属性的值是不一样的。注意有三种情况:

a. 若入参为单参数且参数类型是一个 List 的时候, collection 的属性值为 list

b. 若从入参为单参数且参数类型是一个数组的时候, collection 属性值为 array

c. 若传入参数为多参数,就需要把它们封装为一个 Map 进行处理

最后来个关于 collection 三种情况的例子吧:

1.单参数List的类型:

<select id="dynamicForeachTest" parameterType="java.util.List" resultType="Blog">            select * from t_blog where id in        
<foreach collection="list" index="index" item="item" open="(" separator="," close=")">        
  #{item}            
 </foreach>        
</select>

上述 collection的值为list,对应的Mapper是这样的

public List dynamicForeachTest(List ids); 

测试代码:

      @Test
     public void dynamicForeachTest() {
          SqlSession session = Util.getSqlSessionFactory().openSession();      
          BlogMapper blogMapper = session.getMapper(BlogMapper.class);
           List ids = new ArrayList();
           ids.add(1);
           ids.add(3);
          ids.add(6);
          List blogs = blogMapper.dynamicForeachTest(ids);          
        for (Blog blog : blogs)              
            System.out.println(blog);         
        session.close();      
}    

2.单参数array数组的类型:

<select id="dynamicForeach2Test" parameterType="java.util.ArrayList" resultType="Blog">     
select * from t_blog where id in     
<foreach collection="array" index="index" item="item" open="(" separator="," close=")">          
#{item}     
</foreach>
 </select>    

上述 collection为array,对应的Mapper代码:

public List dynamicForeach2Test(int[] ids); 

对应的测试代码:

  @Test
  public void dynamicForeach2Test() {
          SqlSession session = Util.getSqlSessionFactory().openSession();
          BlogMapper blogMapper = session.getMapper(BlogMapper.class);
          int[] ids = new int[] {1,3,6,9};
          List blogs = blogMapper.dynamicForeach2Test(ids);
          for (Blog blog : blogs)
           System.out.println(blog);    
         session.close();   
}

3.自己把参数封装成Map的类型

 <select id="dynamicForeach3Test" parameterType="java.util.HashMap" resultType="Blog">          select * from t_blog where title like "%"#{title}"%" and id in        
<foreach collection="ids" index="index" item="item" open="(" separator="," close=")">              
 #{item}         
 </foreach> 
</select>

上述 collection的值为ids,是传入的参数Map的key,对应的Mapper代码:

public List dynamicForeach3Test(Map params); 

对应测试代码:

@Test
    public void dynamicForeach3Test() {
        SqlSession session = Util.getSqlSessionFactory().openSession();
         BlogMapper blogMapper = session.getMapper(BlogMapper.class);
          final List ids = new ArrayList();
          ids.add(1);
          ids.add(2);
          ids.add(3);
          ids.add(6);
         ids.add(7);
         ids.add(9);
        Map params = new HashMap();
         params.put("ids", ids);
         params.put("title", "中国");
        List blogs = blogMapper.dynamicForeach3Test(params);
         for (Blog blog : blogs)
             System.out.println(blog);
         session.close();
     }

7.Include

最后提一句这个标签。。。怕忘了,,用于引用外部 sql 标签

猜你喜欢

转载自blog.csdn.net/J_java1/article/details/81706599