mybatis说明

目录

1.说明

2.配置文件

3.映射器

4.select标签

5.insert标签

6.update标签

7.delete标签

8.resultMap的特别说明

9.注解

10.关联(级联)查询

11.动态sql

12.mybatis分页

13缓存

14#{}和${}的区别


1.说明

MyBatis 是一个开源、轻量级的数据持久化框架,是 JDBC 和 Hibernate 的替代方案。MyBatis 内部封装了 JDBC,简化了加载驱动、创建连接、创建 statement 等繁杂的过程,开发者只需要关注 SQL 语句本身。

MyBatis 支持定制化 SQL、存储过程以及高级映射,可以在实体类和 SQL 语句之间建立映射关系,是一种半自动化的 ORM 实现。其封装性低于 Hibernate,但性能优秀、小巧、简单易学、应用广泛。

优点:

  • MyBatis 是免费且开源的。
  • 与 JDBC 相比,减少了 50% 以上的代码量。
  • MyBatis 是最简单的持久化框架,小巧并且简单易学。
  • MyBatis 相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL 写在 XML 中,和程序逻辑代码分离,降低耦合度,便于同一管理和优化,提高了代码的可重用性。
  • 提供 XML 标签,支持编写动态 SQL 语句。
  • 提供映射标签,支持对象与数据库的 ORM 字段关系映射。
  • 支持存储过程。MyBatis 以存储过程的形式封装 SQL,可以将业务逻辑保留在数据库之外,增强应用程序的可移植性、更易于部署和测试。

缺点:

  • 编写 SQL 语句工作量较大,对开发人员编写 SQL 语句的功底有一定要求。
  • SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

2.配置文件

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 配置 -->
    <properties /><!-- 属性 -->
    <settings /><!-- 设置 -->
    <typeAliases /><!-- 类型命名 -->
    <typeHandlers /><!-- 类型处理器 -->
    <objectFactory /><!-- 对象工厂 -->
    <plugins /><!-- 插件 -->
    <environments><!-- 配置环境 -->
        <environment><!-- 环境变量 -->
            <transactionManager /><!-- 事务管理器 -->
            <dataSource /><!-- 数据源 -->
        </environment>
    </environments>
    <databaseIdProvider /><!-- 数据库厂商标识 -->
    <mappers /><!-- 映射器 -->
</configuration>

mybatis-config.xml 文件中的元素节点是有一定顺序的,节点位置必须按以上位置排序,否则会编译错误。

现在使用spring boot集成mybatis一般不需要配置太多东西,大部分配置在spring boot的配置文件中。

3.映射器

  • 通过 XML 文件方式实现,比如我们在 mybatis-config.xml 文件中描述的 XML 文件,用来生成 mapper。
  • 通过注解的方式实现,使用 Configuration 对象注册 Mapper 接口。

(1)XML实现映射器

XML 定义映射器分为两个部分:接口和XML。

接口

package net.biancheng.mapper;
import java.util.List;
import net.biancheng.po.Website;
public interface WebsiteMapper {
    public List<Website> selectAllWebsite();
}

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.WebsiteMapper">

    <!-- 查询所有网站信息 -->
    <select id="selectAllWebsite"
        resultType="net.biancheng.po.Website">
        select * from website
    </select>

</mapper>
  • namespace 用来定义命名空间,该命名空间和定义接口的全限定名一致。
  • <select> 元素表明这是一条查询语句,属性 id 用来标识这条 SQL。id要和接口中的方法名保持一致。

(2)注解方式

使用注解的方式实现映射器,只需要在接口中使用 Java 注解,注入 SQL 即可。

package net.biancheng.mapper;

import java.util.List;
import org.apache.ibatis.annotations.Select;
import net.biancheng.po.Website;

public interface WebsiteMapper2 {
    @Select(value = "select * from website")
    public List<Website> selectAllWebsite();
}

虽然这里注解的方式看起来比 XML 简单,但是现实中我们遇到的 SQL 会比该例子复杂得多。如果 SQL 语句中有多个表的关联、多个查询条件、级联、条件分支等,显然这条 SQL 就会复杂的多。因此不建议使用这种方式。

4.select标签

执行 SQL 语句时可以定义参数,参数可以是一个简单的参数类型,例如 int、float、String;也可以是一个复杂的参数类型,例如 JavaBean、Map 等。MyBatis 提供了强大的映射规则,执行 SQL 后,MyBatis 会将结果集自动映射到 JavaBean 中。

参数的传递使用#{参数名},相当于告诉 MyBatis 生成 PreparedStatement 参数。对于 JDBC,该参数会被标识为“?”。

常用标签属性

parameterType属性可以不写,mybatis能够推断出参数的类型。

传递参数的几种方式

(1)使用Map传递参数

public List<Website> selectWebsiteByMap(Map<String, String> params);
<!-- 根据name和url模糊查询网站信息 -->
<select id="selectWebsiteByMap" resultType="net.biancheng.po.Website" parameterType="map">
    SELECT id,NAME,url FROM website
    WHERE name LIKE CONCAT ('%',#{name},'%')
    AND url LIKE CONCAT ('%',#{url},'%')
</select>
Map<String,String> paramsMap = new HashMap<String,String>();
paramsMap.put("name","编程");
paramsMap.put("url","biancheng");
websiteMapper.selectWebsiteByMap(paramsMap);

使用 Map 传递参数虽然简单易用,但是由于这样设置参数需要键值对应,业务关联性不强,开发人员需要深入到程序中看代码,造成可读性下降。此方式不推荐。

(2)使用注解传递参数

public List<Website> selectWebsiteByAn(@Param("name") String name, @Param("url") String url);
<!-- 根据name和url模糊查询网站信息 -->
<select id="selectWebsiteByAn" resultType="net.biancheng.po.Website">
    SELECT id,NAME,url FROM website
    WHERE name LIKE CONCAT ('%',#{name},'%')
    AND url LIKE CONCAT ('%',#{url},'%')
</select>

当我们把参数传递给后台时,MyBatis 通过 @Param 提供的名称就会知道 #{name} 代表 name 参数,提高了参数可读性。但是如果这条 SQL 拥有 10 个参数的查询,就会造成可读性下降,增强了代码复杂性。

(3)使用JavaBean传递参数

public List<Website> selectWebsiteByAn(Website website);
<!-- 根据name和url模糊查询网站信息 -->
<select id="selectWebsiteByAn" resultType="net.biancheng.po.Website">
    SELECT id,NAME,url FROM website
    WHERE name LIKE CONCAT ('%',#{name},'%')
    AND url LIKE CONCAT ('%',#{url},'%')
</select>

(4)区别

使用 Map 传递参数会导致业务可读性的丧失,继而导致后续扩展和维护的困难,所以在实际应用中我们应该果断废弃该方式。

使用 @Param 注解传递参数会受到参数个数的影响。当 n≤3 时,它是最佳的传参方式,因为它更加直观;当 n>3 时,多个参数将给调用带来困难。

当参数个数大于 3 个时,建议使用 JavaBean 方式。

5.insert标签

 insert 标签用来定义插入语句,执行插入操作。当 MyBatis 执行完一条插入语句后,就会返回其影响数据库的行数。

(1)常用标签属性

注意:

insert 标签中没有 resultType及resultMap 属性,只有查询操作才需要对返回结果类型进行相应的指定。

传递参数的几种方式同select的一致。

(2)主键回填

我们知道,MySQL、SQL Server 等数据库表可以采用自动递增的字段作为其主键,oracle需要创建自增序列才能实现自增。当向这样的数据库表插入数据时,即使不指定自增主键的值,数据库也会根据自增规则自动生成主键并插入到表中。
以mysql为例,插入数据的时候指定主键自增,所以插入的项目不包含主键,一些特殊情况下,我们需要将这个刚刚生成的主键回填到请求对象(原本不包含主键信息的请求对象)中,供其他业务使用。此时,我们就可以通过在 insert 标签中添加  keyProperty 和 useGeneratedKeys 属性,来实现该功能。

<!--添加一个网站信息,成功后将主键值返回填给id(po的属性)-->
<insert id="addWebsite" parameterType="net.biancheng.po.Website" keyProperty="id" useGeneratedKeys="true">
    insert into Website (name,url) values(#{name},#{url})
</insert>

将useGeneratedKeys设置为true,代表将自增的值设置到keyProperty设置的属性值中,mysql支持多个主键,但是只有一个主键可以设置为自增。所以keyProperty只能设置一个值。

(3)自定义主键

如果在实际项目中,若数据库不支持主键自动递增(例如 Oracle),或者取消了主键自动递增的规则,我们可以使用 MyBatis 的 <selectKey> 标签自定义生成主键,具体配置代码如下。

<!-- 添加一个网站,#{name}为 net.biancheng.po.Website 的属性值 -->
<insert id="insertWebsite" parameterType="net.biancheng.po.Website">
    <!-- 先使用selectKey标签定义主键,然后再定义SQL语句 -->
    <selectKey keyProperty="id" resultType="Integer" order="BEFORE">
        select if(max(id) is null,1,max(id)+1) as newId from Website
    </selectKey>
    insert into Website (id,name,url) values(#{id},#{name},#{url})
</insert>



使用序列的方式

    <insert id="insertOrders" parameterType="com.example.demo.entity.Orders">
        INSERT INTO 语句
        <selectKey keyProperty="id" order="BEFORE" resultType="int">
            SELECT SEQ_ID.nextval from dual
        </selectKey>
    </insert>

<selectKey> 标签中属性说明如下:

  • keyProperty:用于指定主键值对应的 PO 类的属性。
  • order:该属性取值可以为 BEFORE 或 AFTER。BEFORE 表示先执行 <selectKey> 标签内的语句,再执行插入语句;AFTER 表示先执行插入语句再执行 <selectKey> 标签内的语句。

6.update标签

update 标签用于定义更新语句,执行更新操作。当 MyBatis 执行完一条更新语句后,会返回一个整数,表示受影响的数据库记录的行数。
(1)常用标签属性

注意:

insert 标签中没有 resultType及resultMap 属性,只有查询操作才需要对返回结果类型进行相应的指定。

传递参数的几种方式同select的一致。

(2)示例

int updateWebsite(String name);
<!--update 标签-->
<update id="updateWebsite" parameterType="string">
    update website set name = #{name}
</update>

7.delete标签

delete 标签用于定义 delete 语句,执行删除操作。当 MyBatis 执行完一条更新语句后,会返回一个整数,表示受影响的数据库记录的行数。

(1)常用标签属性

注意:

insert 标签中没有 resultType及resultMap 属性,只有查询操作才需要对返回结果类型进行相应的指定。

传递参数的几种方式同select的一致。

8.resultMap的特别说明

resultMap 是 MyBatis 中最复杂的元素,主要用于解决实体类属性名与数据库表中字段名不一致的情况,可以将查询结果映射成实体对象。

(1)构成

<resultMap id="" type="">
    <constructor><!-- 类再实例化时用来注入结果到构造方法 -->
        <idArg/><!-- ID参数,结果为ID -->
        <arg/><!-- 注入到构造方法的一个普通结果 --> 
    </constructor>
    <id/><!-- 用于表示哪个列是主键 -->
    <result/><!-- 注入到字段或JavaBean属性的普通结果 -->
    <association property=""/><!-- 用于一对一关联 -->
    <collection property=""/><!-- 用于一对多、多对多关联 -->
    <discriminator javaType=""><!-- 使用结果值来决定使用哪个结果映射 -->
        <case value=""/><!-- 基于某些值的结果映射 -->
    </discriminator>
</resultMap>
  • <resultMap> 元素的 type 属性表示需要的 POJO,id 属性是 resultMap 的唯一标识。
  • 子元素 <constructor> 用于配置构造方法。当一个 POJO 没有无参数构造方法时使用。
  • 子元素 <id> 用于表示哪个列是主键。允许多个主键,多个主键称为联合主键。
  • 子元素 <result> 用于表示 POJO 和 SQL 列名的映射关系。
  • 子元素 <association>、<collection> 和 <discriminator> 用在级联的情况下。关于级联的问题比较复杂,在后面进行介绍。

id 和 result 元素都有以下属性。常用的为前两个。

(2)示例

<!--使用自定义结果集类型 -->
<resultMap type="net.biancheng.po.Website" id="myResult">
    <!-- property 是 net.biancheng.po.Website 类中的属性 -->
    <!-- column是查询结果的列名,可以来自不同的表 -->
    <id property="id" column="id" />
    <result property="uname" column="name" />
</resultMap>

 resultMap 元素的属性 id 代表这个 resultMap 的标识,type 标识需要映射的 POJO。我们可以使用 MyBatis 定义好的类的别名或自定义类的全限定名。

这里使用 property 元素指定 Website 的属性名称 uname,column 表示数据库中 website 表的 SQL 列名 name,将 POJO 和 SQL 的查询结果一 一对应。

<select id="selectAllWebsite" resultMap="myResult">
    select id,name,url from website
</select>

可以发现 SQL 语句的列名和 myResult 中的 column 一一对应。resultMap的内容是上面定义中id的内容。

(3)resultMap的继承

resultMap中可以使用extends来继承另一个resultMap,这种常用于这种情况,当实体类A继承了实体类B,在xml文件中已经存在了实体类A的resultMap,此时如果需要创建一个实体类B的resultMap。就可以使用继承的方式,如下:

    <resultMap type="BmsBillsTypeDetail" id="BmsBillsTypeDetailResult">
            <result property="id" column="id"/>
            <result property="billsTypeId" column="bills_type_id"/>
            <result property="expenseId" column="expense_id"/>
            <result property="expenseName" column="expense_name"/>
            <result property="createBy" column="create_by"/>
            <result property="createTime" column="create_time"/>
            <result property="updateBy" column="update_by"/>
            <result property="updateTime" column="update_time"/>
    </resultMap>

    <resultMap type="com.qingdao2world.settle.domain.vo.BmsBillsTypeDetailVo" extends="BmsBillsTypeDetailResult" id="BmsBillsTypeDetailVoResult">
        <result property="billsTypeName" column="billsTypeName"/>
    </resultMap>

如果是在同一个xml中,extends后面指定要继承的resultMap的名字就可以了,如果不在同一个xml中,则需要指定全限定类名。

(4)resultMap和resultType的区别

resultmap与resulttype的区别为:对象不同、描述不同、类型适用不同

一、对象不同

1、resultmap:resultMap如果查bai询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。

2、resulttype:resultType使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。

二、描述不同

1、resultmap:resultMap对于一对一表连接的处理方式通常为在主表的pojo中添加嵌套另一个表的pojo,然后在mapper.xml中采用association节点元素进行对另一个表的连接处理。

2、resulttype:resultType无法查询结果映射到pojo对象的pojo属性中,根据对结构集查询遍历的需要选择使用resultType还是resultMap。

三、类型适用不同

1、resultmap:mybatis中在查询进行select映射的时候,返回类型可以用resultType,也可以用resultMap。

2、resulttype:resultType是直接表示返回类型的,而resultMap则是对外部ResultMap的引用,但是resultType跟resultMap不能同时存在。

如果你要用resulttype返回一个复杂对象的话,就必须返回这个对象的所有属性。

说白了就是resultmap和resulttype都是作为sql的返回结果集,但是resultmap适用于pojo类和表字段不匹配以及复杂表的关联查询,不可以直接返回返回类型必须是外部自己定义的resultmap的引用但是resultType可以直接返回返回类型,但是实体类对应关系一定要名字完全对应。当然resulttype也可以实现关联查询的只是这需要思考出方法来实现。

9.注解

常用注解有@Param,其他注解不常用,暂不介绍。

10.关联(级联)查询

级联关系是一个数据库实体的概念,有 3 种级联关系,分别是一对一级联、一对多级联以及多对多级联。

  • 一对多的关系,如角色和用户的关系。通俗的理解就是,一家软件公司会存在许多软件工程师,公司和软件工程师就是一对多的关系。
  • 一对一的关系。每个软件工程师都有一个编号(ID),这是他在公司的标识,它与工程师是一对一的关系。
  • 多对多的关系,有些公司一个角色可以对应多个用户,但是一个用户可以兼任多个角色。通俗的说,一个人既可以是总经理,同时也是技术总监,而技术总监这个职位可以对应多个人,这就是多对多的关系。

(1)一对一关联查询

在 <association> 元素中通常使用以下属性。

  • property:指定映射到实体类的对象属性。
  • column:指定表中对应的字段(即分布查询的查询条件)。
  • javaType:指定映射到实体对象属性的类型。
  • select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。

一对一关联查询可采用以下两种方式:

  • 单步查询,通过关联查询实现
  • 分步查询,通过两次或多次查询,为一对一关系的实体 Bean 赋值

单步查询:

实体类:

ackage net.biancheng.po;
public class Student {
    private int id;
    private String name;
    private int sex;
    private StudentCard studentCard;
    /*省略setter和getter方法*/
    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + ", sex=" + sex + ", studentCard=" + studentCard + "]";
    }
}
<resultMap type="net.biancheng.po.Student" id="cardAndStu2">
    <id property="id" column="id" />
    <result property="name" column="name" />
    <result property="sex" column="sex" />
    <!-- 一对一级联查询 -->
    <association property="studentCard"
        javaType="net.biancheng.po.StudentCard">
        <id property="id" column="id" />
        <result property="studentId" column="studentId" />
    </association>
</resultMap>
<select id="selectStuById2" parameterType="Integer"
    resultMap="cardAndStu2">
    SELECT s.*,sc.studentId FROM student s,studentCard sc
    WHERE
    s.cardId = sc.id AND s.id=#{id}
</select>

 通过两个表的连接查出来两个表的数据,在association标签中,property的内容是实体类中属性名,javaType是指属性名对应的类型。后面的则是实体类中的属性。

分布查询:

<mapper namespace="net.biancheng.mapper.StudentCardMapper">
    <select id="selectStuCardById"
        resultType="net.biancheng.po.StudentCard">
        SELECT * FROM studentCard WHERE id = #{id}
    </select>
</mapper>
<mapper namespace="net.biancheng.mapper.StudentMapper">
    <!-- 一对一根据id查询学生信息:级联查询的第一种方法(嵌套查询,执行两个SQL语句) -->
    <resultMap type="net.biancheng.po.Student" id="cardAndStu1">
        <id property="id" column="id" />
        <result property="name" column="name" />
        <result property="sex" column="sex" />
        <!-- 一对一级联查询 -->
        <association property="studentCard" column="cardId"
            javaType="net.biancheng.po.StudentCard"
            select="net.biancheng.mapper.StudentCardMapper.selectStuCardById" />
    </resultMap>
    <select id="selectStuById1" parameterType="Integer"
        resultMap="cardAndStu1">
        select * from student where id=#{id}
    </select>
</mapper>

property:设置属性名。

javaType:设置属性类型。

column:设置查询条件。

select:设置查询语句。

(2)一对多关联查询

在 MyBatis 中,通过 <resultMap> 元素的子元素 <collection> 处理一对多级联关系,collection 可以将关联查询的多条记录映射到一个 list 集合属性中。示例代码如下。

<collection property="orderList"
        javaType="net.biancheng.po.Order" column="id"
        select="net.biancheng.mapper.OrderMapper.selectOrderById" />

在 <collection> 元素中通常使用以下属性。

  • property:指定映射到实体类的对象属性。
  • column:指定表中对应的字段(即分布查询的查询条件)。
  • javaType:指定映射到实体对象属性的类型。
  • select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。

一对多关联查询可采用以下两种方式:

  • 分步查询,通过两次或多次查询,为一对多关系的实体 Bean 赋值
  • 单步查询,通过关联查询实现

单步查询:

ackage net.biancheng.po;
import java.util.List;
public class User {
    private int id;
    private String name;
    private String pwd;
    private List<Order> orderList;
    /*省略setter和getter方法*/
    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", orderList=" + orderList + "]";
    }
}
<!-- 一对多 根据id查询用户及其关联的订单信息:级联查询的第二种方法(单步查询) -->
<resultMap type="net.biancheng.po.User" id="userAndOrder2">
    <id property="id" column="id" />
    <result property="name" column="name" />
    <result property="pwd" column="pwd" />
    <!-- 一对多级联查询,ofType表示集合中的元素类型 -->
    <collection property="orderList"
        ofType="net.biancheng.po.Order">
        <id property="oId" column="oId" />
        <result property="ordernum" column="ordernum" />
    </collection>
</resultMap>
<select id="selectUserOrderById2" parameterType="Integer"
    resultMap="userAndOrder2">
    SELECT u.*,o.id as oId,o.ordernum FROM `user` u,`order` o
    WHERE
    u.id=o.`userId` AND u.id=#{id}
</select>

 分布查询:

<!-- 根据id查询订单信息 -->
<select id="selectOrderById" resultType="net.biancheng.po.Order"
    parameterType="Integer">
    SELECT * FROM `order` where userId=#{id}
</select>
<!-- 一对多 根据id查询用户及其关联的订单信息:级联查询的第一种方法(分步查询) -->
<resultMap type="net.biancheng.po.User" id="userAndOrder1">
    <id property="id" column="id" />
    <result property="name" column="name" />
    <result property="pwd" column="pwd" />
    <!-- 一对多级联查询,ofType表示集合中的元素类型,将id传递给selectOrderById -->
    <collection property="orderList"
        ofType="net.biancheng.po.Order" column="userId"
        select="net.biancheng.mapper.OrderMapper.selectOrderById" />
</resultMap>
<select id="selectUserOrderById1" parameterType="Integer"
    resultMap="userAndOrder1">
    select * from user where id=#{id}
</select>

各属性说明和一对多查询一致。

分布查询的好处在于可以实现延迟加载。开启延迟加载之后会根据你要获取的信息来执行对应的sql,如上面的sql一部分是获取用户的信息,一部分是获取订单的信息,如果实际代码中只使用了用户的信息,则获取订单信息的sql则不会执行。

开启延迟加载的配置:

<setting name="lazyLoadingEnabled” value="true"/>

开启延迟加载的场合,也可以在association或collection中通过属性设置个别sql是延迟加载还是立即加载。

延迟加载:

 立即加载:

(3)多对多关联查询

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

11.动态sql

动态 SQL 是 MyBatis 的强大特性之一。在 JDBC 或其它类似的框架中,开发人员通常需要手动拼接 SQL 语句。根据不同的条件拼接 SQL 语句是一件极其痛苦的工作。例如,拼接时要确保添加了必要的空格,还要注意去掉列表最后一个列名的逗号。而动态 SQL 恰好解决了这一问题,可以根据场景动态的构建查询。

(1)if标签

MyBatis if 类似于 Java 中的 if 语句,是 MyBatis 中最常用的判断语句。使用 if 标签可以节省许多拼接 SQL 的工作,把精力集中在 XML 的维护上。

语法:

<if test="判断条件">
    SQL语句
</if>

 当判断条件为 true 时,才会执行所包含的 SQL 语句。

示例:

<select id="selectAllWebsite" resultMap="myResult">
    select id,name,url from website where 1=1
    <if test="name != null">
        AND name like #{name}
    </if>

    <if test="url!= null">
        AND url like #{url}
    </if>
</select>

多个if可以一起使用,也可以嵌套使用。

嵌套if示例:

 <if test="record.orderNo != null">
            and
            <if test="record.orderNos != null and record.orderNos.size() != 0">
                (
            </if>
            t1.order_no LIKE CONCAT("%",#{record.orderNo},"%")
            <if test="record.orderNos != null and record.orderNos.size() != 0">
                or
            </if>
        </if>

(2) choose-when-otherwise标签

MyBatis 中动态语句 choose-when-otherwise 类似于 Java 中的 switch-case-default 语句。由于 MyBatis 并没有为 if 提供对应的 else 标签,如果想要达到<if>...<else>...</else> </if> 的效果,可以借助 <choose>、<when>、<otherwise> 来实现。
语法:

<choose>
    <when test="判断条件1">
        SQL语句1
    </when >
    <when test="判断条件2">
        SQL语句2
    </when >
    <when test="判断条件3">
        SQL语句3
    </when >
    <otherwise>
        SQL语句4
    </otherwise>
</choose>

choose 标签按顺序判断其内部 when 标签中的判断条件是否成立,如果有一个成立,则执行相应的 SQL 语句,choose 执行结束;如果都不成立,则执行 otherwise 中的 SQL 语句。这类似于 Java 的 switch 语句,choose 为 switch,when 为 case,otherwise 则为 default。

示例:

<mapper namespace="net.biancheng.mapper.WebsiteMapper">
    <select id="selectWebsite"
        parameterType="net.biancheng.po.Website"
        resultType="net.biancheng.po.Website">
        SELECT id,name,url,age,country
        FROM website WHERE 1=1
        <choose>
            <when test="name != null and name !=''">
                AND name LIKE CONCAT('%',#{name},'%')
            </when>
            <when test="url != null and url !=''">
                AND url LIKE CONCAT('%',#{url},'%')
            </when>
            <otherwise>
                AND age is not null
            </otherwise>
        </choose>
    </select>
</mapper>

(3)where标签

where 标签主要用来简化 SQL 语句中的条件判断,可以自动处理 AND/OR 条件。

语法:

<where>
    <if test="判断条件">
        AND/OR ...
    </if>
</where>

if 语句中判断条件为 true 时,where 关键字才会加入到组装的 SQL 里面,否则就不加入。where 会检索语句,它会将 where 后的第一个 SQL 条件语句的 AND 或者 OR 关键词去掉。

示例:

<select id="selectWebsite" resultType="net.biancheng.po.Website">
    select id,name,url from website
    <where>
        <if test="name != null">
            AND name like #{name}
        </if>
        <if test="url!= null">
            AND url like #{url}
        </if>
    </where>
</select>

(4)set标签

在 Mybatis 中,update 语句可以使用 set 标签动态更新列。set 标签可以为 SQL 语句动态的添加 set 关键字,剔除追加到条件末尾多余的逗号。

语法:

        <set>
            <if test="name!=null">name=#{name},</if>
            <if test="url!=null">url=#{url},</if>
        </set>

示例:

<?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.WebsiteMapper">
    <!--使用set元素动态修改一个网站记录 -->
    <update id="updateWebsite"
        parameterType="net.biancheng.po.Website">
        UPDATE website
        <set>
            <if test="name!=null">name=#{name},</if>
            <if test="url!=null">url=#{url},</if>
        </set>
        WHERE id=#{id}
    </update>
</mapper>

(4)foreach标签

对于一些 SQL 语句中含有 in 条件或者是批量插入多条数据的场合,需要迭代条件集合来生成的情况,可以使用 foreach 来实现 SQL 条件的迭代。 

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

语法:

<foreach item="item" index="index" collection="list|array|map key" open="(" separator="," close=")">
    参数值
</foreach>

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。
  • 如果参数使用了@Param注解,collection 属性值为注解的名字,如果是一个javabean,collection 属性值为注解的名字.list的名字或者注解的名字.array的名字。

(5)bind标签

每个数据库的拼接函数或连接符号都不同,例如 MySQL 的 concat 函数、Oracle 的连接符号“||”等。这样 SQL 映射文件就需要根据不同的数据库提供不同的实现,显然比较麻烦,且不利于代码的移植。幸运的是,MyBatis 提供了 bind 标签来解决这一问题。

bind 标签可以通过 OGNL 表达式自定义一个上下文变量。        

语法:

<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 数据库都可以使用这样的语句,提高了可移植性。

示例:

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

(6)trim标签

在 MyBatis 中除了使用 if+where 实现多条件查询,还有一个更为灵活的元素 trim 能够替代之前的做法。
trim 一般用于去除 SQL 语句中多余的 AND 关键字、逗号或者给 SQL 语句前拼接 where、set 等后缀,可用于选择性插入、更新、删除或者条件查询等操作。

语法:

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

属性说明:

 示例:

 trim 实现与 where 元素相同的效果

<select id="selectWebsite" resultType="net.biancheng.po.Website">
    SELECT id,name,url,age,country
    FROM website
    <trim prefix="where" prefixOverrides="and">
        <if test="name != null and name !=''">
            AND name LIKE CONCAT ('%',#{name},'%')
        </if>
        <if test="url!= null">
            AND url like concat ('%',#{url},'%')
        </if>
    </trim>
</select>

 trim 实现与 set 元素相同的效果

    <update id="updateBmsBillMemo" parameterType="BmsBillMemo">
        update bms_bills_memo      
          <trim prefix="SET" suffixOverrides=",">
            <if test="supplierId != null">supplier_id =
                #{supplierId},
            </if>
            <if test="supplierName != null">supplier_name =
                #{supplierName},
            </if>
            <if test="delFlag != null">del_flag =
                #{delFlag},
            </if>
          </trim>
        where id = #{id}
    </update>

trim用于插入

    <insert id="insertBmsBillMemo" parameterType="BmsBillMemo" useGeneratedKeys="true"
            keyProperty="id">
        insert into bms_bills_memo
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="supplierId != null">supplier_id,
            </if>
            <if test="supplierName != null">supplier_name,
            </if>
            <if test="clientId != null">client_id,
            </if>
            <if test="clientName != null and clientName != ''">client_name,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="supplierId != null">#{supplierId},
            </if>
            <if test="supplierName != null">#{supplierName},
            </if>
            <if test="clientId != null">#{clientId},
            </if>
            <if test="clientName != null and clientName != ''">#{clientName},
            </if>
        </trim>
    </insert>

(7)sql和include标签

定义和引用标签,当同一个mapper下的很多方法都会用到一些查询字段,或者where条件。将其定义为常量,提高代码的复用性和可读性。

示例:

 <!-- 查询字段 -->
    <sql id="Base_Column_List">
        ID,MAJOR,BIRTHDAY,AGE,NAME,HOBBY
    </sql>
    
    <select id="selectStudent" resultMap="com.example.demo.entity.Student">
        SELECT
        <include refid="Base_Column_List" />
        FROM
        student
        <include refid="Example_Where_Clause" />
    </select>
//where条件
        <where>
            <include refid="selectBmsDayBillsDetailForWhere"/>
        </where>

12.mybatis分页

MyBatis 的分页功能是基于内存的分页,即先查询出所有记录,再按起始位置和页面容量取出结果。

/**
*
* @param site
* @param currentPageNo 起始位置
* @param pageSize      页面容量
* @return
*/
public List<Website> selectWebsite(@Param("site") Website site, @Param("from") Integer currentPageNo,
        @Param("pageSize") Integer pageSize);
<select id="selectWebsite" resultType="net.biancheng.po.Website">
    SELECT id,name,url,age,country
    FROM website
    <trim prefix="where" prefixOverrides="and">
        <if test="site.name != null and site.name !=''">
            AND name LIKE CONCAT ('%',#{site.name},'%')
        </if>
        <if test="site.url!= null and site.url !=''">
            AND url LIKE CONCAT ('%',#{site.url},'%')
        </if>
        ORDER BY id limit #{from},#{pageSize}
    </trim>
</select>

相比原来的 selectWebsite 方法,增加了两个参数,起始位置(from)和页面容量(pageSize)。

查询语句中增加 limit 关键字。现在基本使用第三方分页工具进行分页,如pageHelper。

13缓存

缓存可以将数据保存在内存中,是互联网系统常常用到的。目前流行的缓存服务器有 MongoDB、Redis、Ehcache 等。缓存是在计算机内存上保存的数据,读取时无需再从磁盘读入,因此具备快速读取和使用的特点。
和大多数持久化框架一样,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 语句,二级缓存缓存的是结果对象。

MyBatis 的全局缓存配置需要在 mybatis-config.xml 的 settings 元素中设置,代码如下。

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

14#{}和${}的区别

MyBatis获取参数值的两种方式: ${}和#{}
${}的本质就是字符串拼接,#{}的本质就是占位符赋值
${}使用字符串拼接的方式拼接Sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号;但是#{}使用占位符赋值的方式拼接Sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号

#{} 是预编译处理,像传进来的数据会加个" "(#将传入的数据都当成一个字符串,会对自动传入的数据加引号)

${} 就是字符串替换。直接替换掉占位符。$方式一般用于传入数据库对象,例如传入表名.

使用 ${} 的话会导致 sql 注入。什么是 SQL 注入呢?比如 select * from user where id = ${value}

value 应该是一个数值吧。然后如果对方传过来的是 001  and name = tom。这样不就相当于多加了一个条件嘛?把SQL语句直接写进来了。如果是攻击性的语句呢?001;drop table user,直接把表给删了

所以为了防止 SQL 注入,能用 #{} 的不要去用 ${}

如果非要用 ${} 的话,那要注意防止 SQL 注入问题,可以手动判定传入的变量,进行过滤,一般 SQL 注入会输入很长的一条 SQL 语句。

示例(传递单个参数):

 使用#{}时不需要加单引号,使用${}时需要加单引号。

mapper接口方法的参数为单个的字面量类型可以通过${}和#{}以任意的名称获取参数值,但是需要注意${}的单引号问题。一般使用传递过来的参数的名字。

示例(传递多个参数):

mapper接口万法的参数为多个时,此时MyBatis会将这些参数放在一map集合中,以两种方式进行存储。

  a>以argo,arg1...为键,以参数为值
  b>以param1,param2...为键,以参数为值
因此只需要通过#{}和${}以键的方式访问值即可,但是需要注意的单引号问题。

也可以使用传递的参数名进行访问。

示例(以map形式传递多个参数)

mapper接口方法的参数有多个时,可以手动将这些参数放在一个map 中存储。因此只需要通过#{}和${}以键的方式访问值即可,但是需要注意的单引号问题。

示例(以javabean形式传递多个参数):

mapper接口方法的参数是实体类类型的参数,只需要通过${}和#{}以属性的方式访问属性值即可,但是需要注意的单引号问题 。

示例(使用@Param注解)

使用@Param注解命名参数
此时MyBatis会将这些参数放在一个map集合中,以两种方式进行存储
*a>以@Param注解的值为键,以参数为值
*b>以param1,param2...为键,以参数为值
因此只需要通过#谷和以键的方式访问值即可,但是需要注意的单引号问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值