# 入门:简单 静态 SQL 映射

## 获取 新增数据id? (MySQL 、 Oracle)

原生 使用 statement 的方法

mysql 支持自增主键,自增主键值的获取,mybatis 也是利用 statement.getGeneratedKey()

  • useGeneratedKeys="true" 使用自增组件获取主键值策略
  • keyProperty 指定对应的主键属性,也就是 mybatis 获取到主键值以后,封装到 javaBean 的哪个属性
  • keyColumn 指定 数据库查出的表中,哪一列是 id

<mark>Oracle 怎么搞?</mark>

## @Param

  • POJO:
    如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入 pojo
    #{属性名}:取出传入的 pojo 的属性值

  • Map:
    如果多个参数不是业务模型中的数据,没有对应的 pojo ,为了方便传入 map
    #{key}:取出key 对应的 value

  • TO:
    如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个 TO(Transfer Object) 数据传输对象

    class Page{
    	int index;
    	int size;
    }
    



# 进阶1(@Param 和 参数名映射 源码分析)

以 selectById 为例,step into

来到 MapperProxy 的 invoke 方法

MapperProxy 实现了 InvocationHandler 接口

当前要执行的方法 ⇒ 参数 method

继续看 invoke 方法 (下面图)
第一行 Object.class.equals(method.getDeclaringClass())
判断如果是 Object 的方法(如 toString 方法)

<mark>直接放行</mark>


否则,进行处理(下图)

首先,final MapperMethod mapperMethod = cachedMapperMethod(method);
把 method 包装成 MapperMethod
(这块不是重点,暂时跳过)

看上图 第二句代码 mapperMethod.execute(sqlSession, args);

作用:执行mapper方法

先看传入的参数

args 的 1 是我们传入的 参数 id

sqlsession

下面 step into execute方法


从 command 里面获取数据
知道是 增删查改的 哪个方法(这里是 select)

首先会执行执行 method.convertArgsToSqlCommandParam(args) (看下图)
把 args 传入的 参数 转换为 param sql语句

select 方法有点特殊(下图)

会先判断返回类型

返回 void?多个?Map?图标(Cursor)?或者一个对象(else)

可以看到(下图)

查询一个的情况,底层调用的 还是 sqlSession 的 selectOne 方法 ()

只不过 这里需要传入的是 处理好的 param

所以 , step into method.convertArgsToSqlCommandParam(args); 看看是怎么玩的


可以看到(上图)
names 已经有值了
值怎么来的?

ParamNameResolver 构造时候 赋值的(下图)

(上图)最后,map 把 name 值 put 进去
然后 上面 有 isUseActualParamName 方法,值 根据 配置 确定的

如果配置了为 true 并且是 jdk1.8 之后的。
那么,就<mark>使用方法签名中的名称作为语句参数名称</mark>

好,知道 names 的值是怎么来的了,
现在 ,回到 getNamedParams 方法 (下图)
第一层 if 判断 args 的数目

如果 数目为0 ⇒ return null
如果 数目为1 ⇒ 返回第一个
否则 遍历取值

如:names={0=id,1=lastName}param={id:args[0] , lastName:args[1]}

前者用于 匹配参数名,后者用于 根据参数名 找 参数值


# 入门 :#{} 和 ${} 的区别

  • #{}:是以预编译的形式,将参数设置到 sql 语句中,PreparedStatement <mark>防止 sql 注入</mark>
  • ${}:取出的值直接拼装在 sql 语句中;<mark>会有安全问题</mark>

    但也有使用情况:
    如 列名的拼接
    select * from ${year}_salary where xxx;
    select * from tbl_employee order by ${f_name} ${order} ;


#{}:更丰富的用法
规定参数的一些规则:

## JdbcType.class

jdbcType 通常需要在某种特定条件下被设置:
在我们数据为 null 的时候, 有些数据库(如Oracle)可能不能识别 mybatis 对 null 的默认处理

<mark>如 Oracle 在这种情况下就会报错</mark>
原因:JdbcType OTHER , 无效的类型,因为 mybatis 对所有的 null 映射都是 原生 Jdbc 的 OTHER 类型。而 Oracle 无法识别 OTHER 类型

在 JdbcType.class 里面可以看到所有的 类型

对于 上面 Oracle 不支持 OTHER 类型,
<mark>解决方法有两个</mark>如下:

  1. (在 #{…} 里面 加 jdbcType=NULL

  2. 全局配置(jdbcTypeForNull)

# 入门:map映射

第一种 map 映射(【一条数据】key:属性名、value:属性值)

第二种 map 映射(【多条数据】key:对象 id、value:对象)


# 入门:select 标签 中的属性

# 入门 :resultMap


resultMap resultType 只能 二选一

# 入门:resultMap - 级联 - 一对多

# 入门:association 联合 查询

# 入门:association 分步查询(为延时加载做铺垫)


# 入门:延时加载(按需加载)

上面的 分步查询有一个问题
:我们每次查询 Employee 对象的时候,将把 Dept 一起查询出来。
我们希望,部门信息在我们使用的时候再去查询。
.
实现起来其实很简单。
<mark>只需要在 分段查询的基础上 加上两个配置</mark>

  • lazyLoadingEnabled 懒加载 。 设置所有的懒加载
  • aggressiveLazyLoading 如果开启,懒加载也会完全的加载全部属性。否则,只有需要时候才加载


# 入门:collection 联合查询


# 入门:collection 分步查询

## <mark>传多列</mark>时候怎么搞?

## fetchType

# 入门:discriminator 鉴别器

鉴别器:mybatis 可以使用 discriminator 判断某列的值,然后根据某列的值改变封装行为
封装 Employee:
如果查出的是女生,就把部门信息查询出来,否则不查询
如果是男生,把 last_name 这一列的值赋值给 email