Mybatis 知识点

文章目录
  1. 1. jdbc 问题
  2. 2. Mybatis 架构
  3. 3. #{} 和 ${} 区别
  4. 4. parameterType 和 resultType
  5. 5. Mapper 接口开发规范
  6. 6. 动态 sql
    1. 6.1. if 标签 & where 标签
    2. 6.2. foreach 标签
  7. 7. 关联查询
    1. 7.1. 数据模型
    2. 7.2. 一对一查询
      1. 7.2.1. 使用 resultType
        1. 7.2.1.1. pojo 类
        2. 7.2.1.2. Mapper.xml
      2. 7.2.2. 使用 resultMap
        1. 7.2.2.1. pojo 类
        2. 7.2.2.2. Mapper.xml
    3. 7.3. 一对多查询
      1. 7.3.1. pojo 类
      2. 7.3.2. Mapper.xml

jdbc 问题

  1. 数据库连接创建、释放频繁造成系统资源浪费,从而影响系统性能。如果使用数据库连接池可解决此问题。
  2. Sql 语句在代码中硬编码,造成代码不易维护,实际应用中 sql 变化的可能较大,sql 变动需要改变 java 代码。
  3. 使用 preparedStatement 向占有位符号传参数存在硬编码,因为 sql 语句的 where 条件不一定,可能多也可能少,修改 sql 还要修改代码,系统不易维护。
  4. 对结果集解析存在硬编码(查询列名),sql 变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成 pojo 对象解析比较方便。

Mybatis 架构

Mybatis 架构图

  1. mybatis配置
    SqlMapConfig.xml,此文件作为 mybatis 的全局配置文件,配置了 mybatis 的运行环境等信息。
    mapper.xml 文件即sql映射文件,文件中配置了操作数据库的 sql 语句。此文件需要在SqlMapConfig.xml 中加载。
  2. 通过 mybatis 环境等配置信息构造 SqlSessionFactory 即会话工厂。
  3. 由会话工厂创建 sqlSession 即会话,操作数据库需要通过 sqlSession 进行。
  4. mybatis 底层自定义了 Executor 执行器接口操作数据库,Executor 接口有两个实现,一个是基本执行器、一个是缓存执行器。
  5. Mapped Statement 也是 mybatis 一个底层封装对象,它包装了 mybatis 配置信息及 sql 映射信息等。mapper.xml 文件中一个 sql 对应一个 Mapped Statement 对象,sql 的 id 即是 Mapped statement 的id。
  6. Mapped Statement 对 sql 执行输入参数进行定义,包括 HashMap、基本类型、pojo,Executor 通过Mapped Statement 在执行 sql前将输入的 java 对象映射至 sql 中,输入参数映射就是 jdbc 编程中对preparedStatement 设置参数。
  7. Mapped Statement 对 sql 执行输出结果进行定义,包括 HashMap、基本类型、pojo,Executor 通过Mapped Statement 在执行 sql 后将输出结果映射至 java 对象中,输出结果映射过程相当于 jdbc 编程中对结果的解析处理过程。

#{} 和 ${} 区别

  • #{} 表示一个占位符号,通过 #{} 可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换。#{} 可以有效防止 sql 注入。#{} 可以接收简单类型值或 pojo 属性值。如果parameterType 传输单个简单类型值,#{} 括号中可以是 value 或其它名称。
  • ${} 表示拼接 sql 串,通过 ${} 可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换,${}可以接收简单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值,${} 括号中只能是 value。

parameterType 和 resultType

  • parameterType:指定输入参数类型,mybatis 通过 ognl 从输入对象中获取参数值拼接在 sql 中。
  • resultType:指定输出结果类型,mybatis 将 sql 查询结果的一行记录数据映射为 resultType 指定类型的对象。如果有多条数据,则分别进行映射,并把对象放到容器List中。

Mapper 接口开发规范

  1. Mapper.xml 文件中的 namespace 与 Mapper 接口的类路径相同
  2. Mapper.xml 中定义的每个 statement 的 id 与 Mapper 接口方法名相同
  3. Mapper.xml 中定义的每个 sql 的 parameterType 的类型与 Mapper 接口方法的输入参数类型相同
  4. Mapper.xml 中定义的每个 sql 的 resultType 的类型与 Mapper 接口方法的输出参数类型相同

动态 sql

if 标签 & where 标签

Mapper.xml 文件

<select id="selectAllData" parameterType="CfContentLikeRecordDto"
resultType="CfContentLikeRecord">
SELECT *
FROM cf_content
<!-- where标签可以自动添加where,同时处理sql语句中第一个and关键字 -->
<WHERE>
<if test="key != null and key !='' ">
and `key` = #{key}
</if>
<if test="startTime != null and startTime !='' ">
<![CDATA[ AND create_Time >= #{startTime}]]>
</if>
<if test="endTime != null and endTime !='' ">
<![CDATA[ AND create_Time <= #{endTime}]]>
</if>
</WHERE>
</select>

foreach 标签

向 sql 传递数组或 List,mybatis 使用 foreach 解析,如下:

根据多个 id 查询用户信息

查询 sql:

SELECT * FROM user WHERE id IN (1,10,24)

<!-- 根据ids查询用户 -->
<select id="queryUserByIds" parameterType="queryVo" resultType="user">
SELECT * FROM `user`
<where>
<!-- foreach标签,进行遍历 -->
<!-- collection:遍历的集合,这里是 QueryVo 的 ids 属性 -->
<!-- item:遍历的项目,可以随便写,,但是和后面的 #{}里面要一致 -->
<!-- open:在前面添加的sql片段 -->
<!-- close:在结尾处添加的sql片段 -->
<!-- separator:指定遍历的元素之间使用的分隔符 -->
<foreach collection="ids" item="item" open="id IN (" close=")" separator=",">
#{item}
</foreach>
</where>
</select>

关联查询

数据模型

关联查询数据模型

一对一查询

需求:查询所有订单信息,关联查询下单用户信息。

注意:因为一个订单信息只会是一个人下的订单,所以从查询订单信息出发关联查询用户信息为一对一查询。如果从用户信息出发查询用户下的订单信息则为一对多查询,因为一个用户可以下多个订单。

使用 resultType

使用 resultType,改造订单 pojo 类,此 pojo 类中包括了订单信息和用户信息

这样返回对象的时候,mybatis 自动把用户信息也注入进来了

pojo 类

OrderUser 类继承类包括了 Order 类的所有字段,只需要定义用户的信息字段即可

public class OrderUser extends Order {
private String username;
private String address;
}
Mapper.xml
<!-- 查询订单,同时包含用户数据 -->
<select id="queryOrderUser" resultType="orderUser">
SELECT
o.id, o.user_id, userId, o.number, o.createtime, o.note, u.username, u.address
FROM
`order` o
LEFT JOIN `user` u
ON o.user_id = u.id
</select>

使用 resultMap

使用 resultMap,定义专门的 resultMap 用于映射一对一查询结果

pojo 类

在 Order 类中加入 User 属性,user 属性中用于存储关联查询的用户信息,因为订单关联查询用户是一对一关系,所以这里使用单个 User 对象存储关联查询的用户信息

public class Order {
private int id; // 订单id
private Integer userId; // 用户id
private String number; // 订单号
private Date createTime; // 订单创建时间
private String note; // 备注
private User user; // 用户信息
}
Mapper.xml
<resultMap type="order" id="orderUserResultMap">
<id property="id" column="id" />
<result property="userId" column="user_id" />
<result property="number" column="number" />
<result property="createTime" column="createtime" />
<result property="note" column="note" />

<!-- association :配置一对一属性 -->
<!-- property:order里面的User属性名 -->
<!-- javaType:属性类型 -->
<association property="user" javaType="user">
<!-- id:声明主键,表示user_id是关联查询对象的唯一标识-->
<id property="id" column="user_id" />
<result property="username" column="username" />
<result property="address" column="address" />
</association>
</resultMap>

<!-- 一对一关联,查询订单,订单内部包含用户属性 -->
<select id="queryOrderUserResultMap" resultMap="orderUserResultMap">
SELECT
o.id, o.user_id, o.number, o.createtime, o.note, u.username, u.address
FROM
`orders` o
LEFT JOIN `user` u
ON o.user_id = u.id
</select>

一对多查询

案例:查询所有用户信息及用户关联的订单信息。

用户信息和订单信息为一对多关系。

pojo 类

public class User {
private int id;
private String username; // 用户姓名
private String sex; // 性别
private Date birthday; // 生日
private String address; // 地址

private List<Order> orders;
}

Mapper.xml

<resultMap type="user" id="userOrderResultMap">
<id property="id" column="id" />
<result property="username" column="username" />
<result property="birthday" column="birthday" />
<result property="sex" column="sex" />
<result property="address" column="address" />

<!-- 配置一对多的关系 -->
<collection property="orders" javaType="list" ofType="order">
<!-- 配置主键,是关联Order的唯一标识 -->
<id property="id" column="oid" />
<result property="number" column="number" />
<result property="createtime" column="createtime" />
<result property="note" column="note" />
</collection>
</resultMap>

<!-- 一对多关联,查询订单同时查询该用户下的订单 -->
<select id="queryUserOrder" resultMap="userOrderResultMap">
SELECT
u.id, u.username, u.birthday, u.sex, u.address,
o.id oid, o.number, o.createtime, o.note
FROM
`user` u
LEFT JOIN `order` o
ON u.id = o.user_id
</select>