mybatis学习笔记重点梳理

Mybatis温习笔记

  1. 如果使用注解和 XML 文件两种方式同时定义,那么 XML 方式将覆盖掉注解方式。
  2. ${}与#{}的区别

(1)#{}是参数占位符,即预编译。

解析时将传入的数据都当成一个字符串,会对传入的变量自动加一个单引号。
如:user_id = #{userId},如果传入的值是111,那么解析成sql时的值为user_id = ‘111’,如果传入的值是id,则解析成的sql为user_id = ‘id’。

(2)${}是字符串替换符,即SQL拼接。

解析时,会将传入的参数直接显示生成在sql中,且不加任何引号。
如:user_id = ${userId},如果传入的值是111,那么解析成sql时的值为user_id = 111 , 如果传入的值是id,则解析成的sql为user_id = id。

(3)用$的情况

MyBatis排序时使用order by 动态参数时需要注意,用$而不是#。

默认情况下,使用#{}格式的语法会导致MyBatis创建预处理语句属性并以它为背景设置安全的值(比如?)。这样做很安全,很迅速也是首选做法,有时你只是想直接在SQL语句中插入一个不改变的字符串。比如,像ORDER BY,你可以这样来使用:
ORDER BY ${columnName},这里MyBatis不会修改或转义字符串。

${}方式一般用于传入数据库对象,例如传入表名
Select * from ${tableName} where user_id = #{userId}

sql执行过程
(1)#{}:编译好SQL后语句再去取值
(2)${}:取值以后再去编译SQL语句

3. (重点)多对多关联关系
由于多对多的关系比较复杂,会增加理解和关联的复杂度,所以应用较少。MyBatis 没有实现多对多级联,推荐通过两个一对多级联替换多对多级联,以降低关系的复杂度,简化程序。

例如,一个订单可以有多种商品,一种商品可以对应多个订单,订单与商品就是多对多的级联关系。可以使用一个中间表(订单记录表)将多对多级联转换成两个一对多的关系。
示例
下面以订单和商品(实现“查询所有订单以及每个订单对应的商品信息”的功能)为例讲解多对多关联查询。
(1)创建数据表
创建 order(订单),product(商品)和 order_details(订单和商品中间表),SQL 语句如下。

CREATE TABLE `order` (
  `oid` int(11) NOT NULL AUTO_INCREMENT,
  `ordernum` int(25) DEFAULT NULL,
  `userId` int(11) DEFAULT NULL,
  PRIMARY KEY (`oid`),
  KEY `userId` (`userId`),
  CONSTRAINT `order_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;

insert  into `order`(`oid`,`ordernum`,`userId`) values (1,20200107,1),(2,20200806,2),(3,20206702,3),(4,20200645,1),(5,20200711,2),(6,20200811,2),(7,20201422,3),(8,20201688,4),(9,NULL,5);

DROP TABLE IF EXISTS `orders_detail`;

CREATE TABLE `orders_detail` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `orderId` int(11) DEFAULT NULL,
  `productId` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

insert  into `orders_detail`(`id`,`orderId`,`productId`) values (1,1,1),(2,1,2),(3,1,3),(4,2,3),(5,2,1),(6,3,2);

DROP TABLE IF EXISTS `product`;

CREATE TABLE `product` (
  `pid` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(25) DEFAULT NULL,
  `price` double DEFAULT NULL,
  PRIMARY KEY (`pid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

insert  into `product`(`pid`,`name`,`price`) values (1,'Java教程',128),(2,'C语言教程',138),(3,'Python教程',132.35);

(2)创建持久化类
Order 类代码如下。

package net.biancheng.po;

import java.util.List;

public class Order {
    private int oid;
    private int ordernum;
    private List<Product> products;

    /*省略setter和getter方法*/

    @Override
    public String toString() {
        return "Order [id=" + oid + ", ordernum=" + ordernum + ", products=" + products + "]";
    }

}

Product 类方法如下。

package net.biancheng.po;

import java.util.List;

public class Product {
    private int pid;
    private String name;
    private Double price;

    // 多对多中的一个一对多
    private List<Order> orders;

    /*省略setter和getter方法*/

    @Override
    public String toString() {
        return "Product [id=" + pid + ", name=" + name + ", price=" + price + "]";
    }
}

(3)创建接口和映射文件
OrderMapper 接口代码如下。

package net.biancheng.mapper;

import java.util.List;

import net.biancheng.po.Order;

public interface OrderMapper {
    public List<Order> selectAllOrdersAndProducts();
}
OrderMapper.xml 代码如下。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="net.biancheng.mapper.OrderMapper">

    <resultMap type="net.biancheng.po.Order" id="orderMap">
        <id property="oid" column="oid" />
        <result property="ordernum" column="ordernum" />

        <collection property="products"
            ofType="net.biancheng.po.Product">
            <id property="pid" column="pid" />
            <result property="name" column="name" />
            <result property="price" column="price" />
        </collection>
    </resultMap>

    <select id="selectAllOrdersAndProducts" parameterType="Integer"
        resultMap="orderMap">
        SELECT o.oid,o.`ordernum`,p.`pid`,p.`name`,p.`price` FROM
        `order` o
        INNER JOIN orders_detail od ON o.oid=od.`orderId`
        INNER JOIN
        product p
        ON p.pid = od.`productId`
    </select>
</mapper>

4. 关联关系查询
在 MyBatis 中,通过 resultMap元素的子元素 association处理一对一级联关系。示例代码如下。

<association property="studentCard" column="cardId"
            javaType="net.biancheng.po.StudentCard"
            select="net.biancheng.mapper.StudentCardMapper.selectStuCardById" />

在association元素中通常使用以下属性。
property:指定映射到实体类的对象属性(大白话就是类的字段)。
column:指定表中对应的字段(即查询返回的列名)。
javaType:指定映射到实体对象属性的类型(类的全路径地址)。
select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。(子查询)

一对一关联查询可采用以下两种方式:
单步查询,通过关联查询实现
分步查询,通过两次或多次查询,为一对一关系的实体 Bean 赋值

5. forEach标签

Mybatis foreach 标签用于循环语句,它很好的支持了数据和 List、set 接口的集合,并对此提供遍历的功能。语法格式如下。

参数值

foreach 标签主要有以下属性,说明如下。
item:表示集合中每一个元素进行迭代时的别名。
index:指定一个名字,表示在迭代过程中每次迭代到的位置。
open:表示该语句以什么开始(既然是 in 条件语句,所以必然以(开始)。
separator:表示在每次进行迭代之间以什么符号作为分隔符(既然是 in 条件语句,所以必然以,作为分隔符)。
close:表示该语句以什么结束(既然是 in 条件语句,所以必然以)开始)。

**注意:**使用 foreach 标签时,最关键、最容易出错的是 collection 属性,该属性是必选的,但在不同情况下该属性的值是不一样的,主要有以下 3 种情况:
如果传入的是单参数且参数类型是一个 List,collection 属性值为 list。
如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array。
如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key。

6. bind标签
bind 标签可以通过 OGNL 表达式自定义一个上下文变量。
比如,按照网站名称进行模糊查询,SQL 映射文件如下。

<select id="selectWebsite" resultType="net.biancheng.po.Website">
    <bind name="pattern" value="'%'+_parameter+'%'" />
    SELECT id,name,url,age,country
    FROM website
    WHERE name like #{pattern}
</select>

bind 元素属性如下。

  • value:对应传入实体类的某个字段,可以进行字符串拼接等特殊处理。
  • name:给对应参数取的别名。
    以上代码中的“_parameter”代表传递进来的参数,它和通配符连接后,赋给了 pattern,然后就可以在 select 语句中使用这个变量进行模糊查询,不管是 MySQL 数据库还是 Oracle 数据库都可以使用这样的语句,提高了可移植性。

7. trim标签
trim 一般用于去除 SQL 语句中多余的 AND 关键字、逗号,或者给 SQL 语句前拼接 where、set 等后缀,可用于选择性插入、更新、删除或者条件查询等操作。trim 语法格式如下。

<trim prefix="前缀" suffix="后缀" prefixOverrides="忽略前缀字符" suffixOverrides="忽略后缀字符">
    SQL语句
</trim>

trim 中属性说明如下。

  • prefix 给SQL语句拼接的前缀,为 trim 包含的内容加上前缀
  • suffix 给SQL语句拼接的后缀,为 trim 包含的内容加上后缀
  • prefixOverrides 去除 SQL 语句前面的关键字或字符,该关键字或者字符由 prefixOverrides 属性指定。
  • suffixOverrides 去除 SQL 语句后面的关键字或者字符,该关键字或者字符由 suffixOverrides 属性指定。

8. Mybatis分页原理
MyBatis 的分页功能是基于内存的分页,即先查询出所有记录,再按起始位置和页面容量取出结果;
limit #{from},#{pageSize}
例子:limit 0,10

注意:实现分页的方法中第一个参数为 limit 的起始位置(下标从 0 开始),而不是用户输入的真正页码(页码从1开始)。用户输入的页码应该转换为 limit 的起始位置下标,即:起始位置下标=(页码-1)*页面容量,那么这个转换操作必然不能在 DAO 层实现,需要在业务层实现。所以我们在测试类中传入的参数为下标,而不是页码。

9. Mybatis缓存机制
MyBatis 提供了一级缓存和二级缓存的支持。默认情况下,MyBatis 只开启一级缓存。

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

在参数和 SQL 完全一样的情况下,我们使用同一个 SqlSession 对象调用同一个 mapper 的方法,往往只执行一次 SQL。因为使用 SqlSession 第一次查询后,MyBatis 会将其放在缓存中,再次查询时,如果没有刷新,并且缓存没有超时的情况下,SqlSession 会取出当前缓存的数据,而不会再次发送 SQL 到数据库。

由于 SqlSession 是相互隔离的,所以如果你使用不同的 SqlSession 对象,即使调用相同的 Mapper、参数和方法,MyBatis 还是会再次发送 SQL 到数据库执行,返回结果。

二级缓存
二级缓存是全局缓存,作用域超出 session 范围之外,可以被所有 SqlSession 共享。

一级缓存缓存的是 SQL 语句,二级缓存缓存的是结果对象。
二级缓存的配置
1)MyBatis 的全局缓存配置需要在 mybatis-config.xml 的 settings 元素中设置,代码如下。

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

2)在 mapper 文件(如 WebMapper.xml)中设置缓存,默认不开启缓存。需要注意的是,二级缓存的作用域是针对 mapper 的 namescape 而言,即只有再次在 namescape 内(net.biancheng.WebsiteMapper)的查询才能共享这个缓存,代码如下。

<mapper namescape="net.biancheng.WebsiteMapper">
    <!-- cache配置 -->
    <cache
        eviction="FIFO"
        flushInterval="60000"
        size="512"
        readOnly="true" />
    ...
</mapper>

以上属性说明如下。
(1)eviction 代表的是缓存回收策略,目前 MyBatis 提供以下策略。

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

(2)flushInterval 刷新间隔时间,单位为毫秒,这里配置的是 100 秒刷新,如果省略该配置,那么只有当 SQL 被执行的时候才会刷新缓存。
(3)size 引用数目,正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。这里配置的是 1024 个对象。
(4)readOnly 只读,默认值为 false,意味着缓存数据只能读取而不能修改,这样设置的好处是可以快速读取缓存,缺点是没有办法修改缓存。
3)在 mapper 文件配置支持 cache 后,如果需要对个别查询进行调整,可以单独设置 cache,代码如下。

<select id="getWebsiteList" resultType="net.biancheng.po.Website" usecache="true">
    ...
</select>

对于 MyBatis 缓存仅作了解即可,因为面对一定规模的数据量,内置的 Cache 方式就派不上用场了,并且对查询结果集做缓存并不是 MyBatis 所擅长的,它专心做的应该是 SQL 映射。对于缓存,采用 OSCache、Memcached 等专门的缓存服务器来做更为合理。

你可能感兴趣的:(数据库,数据库,sql)