MyBatis学习总结

1.MyBatis学习

这次用了一周多的时间学习了mybatis。

简介

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

什么是持久层

持久层,可以理解成数据 保存在 数据库或者 硬盘一类可以保存很长时间的设备里面,不像放在内存中那样断电就消失了,也就是把数据存在持久化设备上,mybatis就是持久层。

官方文档 https://mybatis.org/mybatis-3/zh/index.html

2.搭建环境

  1. 我们使用maven来构建项目,直接构建一个空的项目。
  2. 导入如下依赖
<!--导入mybatis依赖 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>

<!--数据库驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.23</version>
</dependency>

<!-- 测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
</dependency>
  1. 在resources文件夹下书写配置文件mybatis-config.xml
<?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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="XXX"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="com/yxx/dao/DepartmentDao.xml"/>
    </mappers>
</configuration>
  1. 创建一个工具类MybatisUtil 在工具类中得到SqlSession
public class MybatisUtil {
    private static SqlSessionFactory sqlSessionFactory;
    static{
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //获取session从sqlSessionFactory工厂中
     public SqlSession getSqlSession(){
        return  sqlSessionFactory.openSession();
     }
}
  1. 创建实体类Department 这里省略get set tostring 构造方法 (下面的也是)
public class Department {
    private int id;
    private String departmentName;
}

对应数据库

create table department(
	id int primary key,
    departmentName varchar(20)
)
  1. dao层 DepartmentDao
public interface DepartmentDao {
    //查询全部
    public List<Department> findAll();
    }

DepartmentDao.xml 这个名字应用上面dao层名字对应

<?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">
<!--namespace绑定一个对应的XXXDao -->
<mapper namespace="com.yxx.dao.DepartmentDao">
    <select id="findAll" resultType="com.yxx.pojo.Department">
        select * from yxx.department
    </select>
</mapper>
  1. 测试
@Test
public void test(){
    MybatisUtil mybatisUtil = new MybatisUtil();
    SqlSession session = mybatisUtil.getSqlSession();
    DepartmentDao mapper = session.getMapper(DepartmentDao.class);

    List<Department> list = mapper.findAll();
    for (Department department : list) {
        System.out.println(department);
    }
    session.close();//一定记得使用完后关闭session
}

3.配置文件(xml)

管网里有很多配置的东西 我只会用一些

3.1.属性 properties

属性你可以从外部进行配置,并可以进行动态替换。也可以在xml文件中进行配置

  • 从外部配置

我们需要导入properties文件

<!--这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置 -->
<properties resource="db.properties"/>
  • 在xml文件中配置
<properties resource="db.properties">
    <property name="username" value="root"/>
    <property name="password" value="123"/>
</properties>

db.properties中配置的属性

driver=com.mysql.cj.jdbc.Driver
url=XXX
username=root
pwd=123456

导入属性配置文件就可以在xml如下使用

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${pwd}"/>
        </dataSource>
    </environment>
</environments>

读取顺序 :他会先去外部properties文件中找属性,如果没找到再从xml文件中配置的属性中找

3.2.设置(Settings)

这个官方就更多了,我也只知道熟悉几个

  • mapUnderscoreToCamelCase

是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。

  • logImpl

指定 MyBatis 所用日志的具体实现,未指定时将自动查找。

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

3.3.配置别名 (typeAliases)

配置别名后就不需要每次在返回值ResultType中书写全限定类名,只需要写你自己配置的别名即可

你只需要在mybatis的配置文件中加上如下几行

<!--类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写
type:类型
 alias : 别名
-->
<typeAliases>
    <typeAlias type="com.yxx.pojo.Department" alias="Department"/>
</typeAliases>

当你的项目中有很多实体类是,这样一个一个的配置别名会显得代码太多,所以我们还有一种方法配置别名,那就是扫描包,但是这种方法你不能自己定义别名,他的别名就是类的名字

<typeAliases>
    <package name="com.yxx.pojo"/>
</typeAliases>

3.4.mappers 映射器

mappers映射器是为了告诉我们mybatis到哪里去找到我们写的sql语句

官网中有四种方式 但我感觉常用的也就三种把

  • 使用resource引用
<mappers>
    <mapper resource="com/yxx/dao/DepartmentDao.xml"/>
</mappers>
  • 使用映射器接口实现类的完全限定类名
<mappers>
    <mapper class="com.yxx.dao.DepartmentDao"/>
</mappers>

使用这种方法必须保证类的名字与书写sql中的名字一样

  • 扫描包
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
  <package name="com.yxx.dao"/>
</mappers>

4.sql模糊查询

代码中的注解写的很清楚

  • dao层中的xml
<!--模糊查询 两种方法
方法一List<Department> departmentList = mapper.fuzzyQuery("%部%"); 查询时加上转义符%
方法二select * from yxx.department where departmentName like "%"#{value}"%"
直接写死在查询语句中
-->
<select id="fuzzyQuery" resultType="com.yxx.pojo.Department">
    select * from yxx.department where departmentName like "%"#{value}"%"
</select>
  • 测试
@Test
public void fuzzyQuery(){
    MybatisUtil mybatisUtil = new MybatisUtil();
    SqlSession session = mybatisUtil.getSqlSession();
    DepartmentDao mapper = session.getMapper(DepartmentDao.class);

    List<Department> departmentList = mapper.fuzzyQuery("部");
    for (Department department : departmentList) {
        System.out.println(department);
    }
    session.close();
}

5.解决属性名与数据库列名不对应

这个我们需要使用结果映射,因为这里不涉及多对一和一对多所以简单结果映射即可

resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的数千行代码。ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

当你的pojo类属性与数据库的列名不同时即可使用,一对多,多对一时使用

当属性与数据库的列名不同时

Department类

public class Department {
    private int id;
    private String name;
}

数据库中列名

在这里插入图片描述

name 与 departmentName不同时当我们直接查询

在这里插入图片描述

查出来name为null 为什么为null了,当然是因为没有找到name属性因为我们没有对应数据库中列表的名字

有两种解决问题的方法

  • 直接给查询语句加别名
<select id="findAll" resultType="departmen">
        select id,departmentName as name from yxx.department
</select>
  • 使用ResultMap结果映射

可以只映射不相同的部分

<resultMap id="departmentmap" type="department">
    <!--column:对应的列表名 property:对用pojo类的属性名 -->
    <result column="id" property="id"/><!--这里可以不写 因为id对应了列表名 -->
    <result column="departmentName" property="name"/>
</resultMap>
<select id="findAll" resultMap="departmentmap">
    select * from yxx.department
</select>

6.mybatis分页

思考为什么需要分页?

减少数据的处理量,不分页一下查询的东西太多

6.1.Limit分页

使用limit语句分页

  • dao
//limit分页查询
public List<Department> queryByLimit(Map<String,Object> map);
  • xml
<!--startIndex 表示开始的位置, pageSize 表示一页的大小-->
<select id="queryByLimit" parameterType="map" resultType="Department">
	select * from department limit #{startIndex},#{pageSize}
</select>
  • 测试
@Test
public void limit(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    DepartmentDao mapper = sqlSession.getMapper(DepartmentDao.class);
    Map<String,Object> map = new HashMap<>();
    map.put("startIndex",0);
    map.put("pageSize",3);
    List<Department> departments = mapper.queryByLimit(map);
    for (Department department : departments) {
        System.out.println(department);
    }
    sqlSession.close();
}
  • 结果

在这里插入图片描述

6.2.RowBounds分页

不使用sql语句实现分页。这个方法其实不怎么好用因为他不能用获取mapper的形式来使用sql语句。他必须如下使用,很jj麻烦

sqlSession.selectList("com.yxx.dao.DepartmentDao.queryByRowBounds", null, rowBounds);
  • dao
//RowBounds分页查询
public List<Department> queryByRowBounds();
  • xml
<select id="queryByRowBounds" resultType="Department">
    select * from department
</select>
  • 测试
@Test
public void rowBounds(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();

    RowBounds rowBounds = new RowBounds(1,2);
    List<Department> departments = sqlSession.selectList("com.yxx.dao.DepartmentDao.queryByRowBounds", null, rowBounds);
    for (Department department : departments) {
        System.out.println(department);
    }
    sqlSession.close();
}
  • 结果

在这里插入图片描述

6.3.使用插件分页(pagehelper)

我还没学

7.使用注解写sql

使用注解写sql可以免去写XXXDao.xml文件也可以免去配置mappers,但是他只能完成简单的CURD复杂的还是必须写XXXDao.xml并配置mappers

简单的sql语句可以用注解来简化代码,但是复杂的sql或者pojo与数据库列名不对应时,使用注解不能达到我们想要的效果

  • dao
@Select("select * from department where id = #{id}")
public Department queryById(@Param("id") String id);
  • 测试
@Test
public void queryById(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    DepartmentDao mapper = sqlSession.getMapper(DepartmentDao.class);
    Department department = mapper.queryById("101");
    System.out.println(department);
    sqlSession.close();
}

8.多对一 一对多处理

在这里插入图片描述

对于学生而言 一个学生关联一个老师

对于老师而言 一个老师管理多个学生

8.1.搭建环境

  • 搭建数据库
CREATE TABLE `teacher` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, 秦老师); 

CREATE TABLE `student` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  `tid` INT(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fktid` (`tid`),
  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO `student` (`id`, `name`, `tid`) VALUES (1, 小明, 1); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (2, 小红, 1); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (3, 小张, 1); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (4, 小李, 1); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES (5, 小王, 1);
  • 实体类pojo

Student:

public class Student {
    private int id;
    private String sname;
    private Teacher teacher;//实现多对一
}

Teacher:

public class Teacher {
    private int id;
    private String tname;
    /*
    老师应该这样写的因为一个老师管理多个学生
    但我们这里只讨论多以一
    一对多在下面在讨论
    private List<Student> students;
    */
}
  • mapper(dao) 接口
public interface TeacherMapper {
}

public interface StudentMapper {
}

8.2.多对一

一个学生关联一个老师

关联的不同之处是,你需要告诉 MyBatis 如何加载关联。MyBatis 有两种不同的方式加载关联:

  • 嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
  • 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。
  • 嵌套select查询
  1. mapper(dao)
    public Student getStudent(@Param("id") int id);
  1. xml
<mapper namespace="com.yxx.dao.StudentMapper">
<!--select嵌套查询下面三个是一体的 -->

    <select id="getStudent" resultMap="studentMap">
        select * from student where id = #{id}
    </select>
    <resultMap id="studentMap" type="student">
        <result property="id" column="id"/>
        <result property="sname" column="sname"/>
        <!--复杂的属性我们需要单独处理
           association:对象
           collection:集合
        -->
        <association property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
    </resultMap>
    <select id="getTeacher" resultType="teacher">
        select * from teacher where id = #{tid}
    </select>
</mapper>
  1. 测试
@Test
public void getStudent(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    Student student = mapper.getStudent(1);
    System.out.println(student);
    sqlSession.close();
}

这种方式虽然很简单,但在大型数据集或大型数据表上表现不佳。这个问题被称为“N+1 查询问题”。 概括地讲,N+1 查询问题是这样子的:

  • 你执行了一个单独的 SQL 语句来获取结果的一个列表(就是“+1”)。
  • 对列表返回的每条记录,你执行一个 select 查询语句来为每条记录加载详细信息(就是“N”)。

这个问题会导致成百上千的 SQL 语句被执行。有时候,我们不希望产生这样的后果。

好消息是,MyBatis 能够对这样的查询进行延迟加载,因此可以将大量语句同时运行的开销分散开来。 然而,如果你加载记录列表之后立刻就遍历列表以获取嵌套的数据,就会触发所有的延迟加载查询,性能可能会变得很糟糕。

  • 关联的嵌套结果映射
  1. mapper(dao)
//嵌套结果映射
public Student getStudent2(@Param("id") int id);
  1. xml
<select id="getStudent2" resultMap="studentMap2">
    select s.id sid,s.sname sname,t.tname tname,t.id tid
    from student s,teacher t
    where s.tid = t.id and s.id = #{id}
</select>
<resultMap id="studentMap2" type="student">
    <result property="id" column="sid"/>
    <result property="sname" column="sname"/>
    <association property="teacher" javaType="teacher">
        <result property="id" column="tid"/>
        <result property="tname" column="tname"/>
    </association>
</resultMap>
  1. 测试
@Test
public void getStudent2(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    Student student = mapper.getStudent2(1);
    System.out.println(student);
    sqlSession.close();
}

8.3.一对多

前面说了多对一的例子是一个学生关联一个老师,那么反过来就是一个老师管理(集合)多个学生。

因为前面老师没做一对多的属性,所以这里需要加上

teacher类:

也有两种方式实现

  • 集合的嵌套 Select 查询
  1. mapper(dao)
    //集合的嵌套 Select 查询
    public Teacher getTeacherSelect(@Param("id") int id);
  1. xml
<!--嵌套select查询(子查询) -->
<select id="getTeacherSelect" resultMap="teachermap">
    select * from teacher where id = #{id}
</select>
<resultMap id="teachermap" type="teacher">
    <!--实体类与数据库列名相对应的可以不写映射-->
    <result property="id" column="id"/>
    <result property="tname" column="tname"/>
    <!--复杂类型单独处理
    javaType
    ofType 确认List里泛型的东西
    -->
    <collection property="students" column="id" javaType="ArrayList" ofType="student" select="getStudent"/>
</resultMap>
<select id="getStudent" resultType="student">
    select * from student where tid = #{id}
</select>
  1. 测试
@Test
public void getTeacher(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
    Teacher teacher = mapper.getTeacherSelect(1);
    System.out.println(teacher);
    sqlSession.close();
}

你可能会立刻注意到几个不同,但大部分都和我们上面学习过的关联元素非常相似。 首先,你会注意到我们使用的是集合元素。 接下来你会注意到有一个新的 “ofType” 属性。这个属性非常重要,它用来将 JavaBean(或字段)属性的类型和集合存储的类型区分开来。 所以你可以按照下面这样来阅读映射:

<collection property="students" column="id" javaType="ArrayList" ofType="student" select="getStudent"/>

读作: “students是一个存储 student的 ArrayList 集合”

在一般情况下,MyBatis 可以推断 javaType 属性,因此并不需要填写。所以很多时候你可以简略成:

<collection property="students" column="id" ofType="student" select="getStudent"/>
  • 集合的嵌套结果映射
  1. mapper(dao)
//集合的嵌套结果映射
public Teacher getTeacherSelect2(@Param("id") int id);
  1. xml
<!-- 集合的嵌套结果映射-->
<select id="getTeacherSelect2" resultMap="teachermap2">
    select t.id tid,t.tname tname,s.id sid,s.sname sname
    from teacher t,student s
    where t.id = #{id} and t.id = s.tid
</select>
<resultMap id="teachermap2" type="teacher">
    <result property="id" column="tid" />
    <result property="tname" column="tname" />
    <collection property="students" javaType="ArrayList" ofType="student">
        <result property="id" column="sid"/>
        <result property="sname" column="sname"/>
    </collection>
</resultMap>
  1. 测试
@Test
public void getTeacher2(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
    Teacher teacher = mapper.getTeacherSelect2(1);
    System.out.println(teacher);
    sqlSession.close();
}

9.动态sql

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

9.1.搭建环境

  • 数据库
/*
 Navicat Premium Data Transfer

 Source Server         : test
 Source Server Type    : MySQL
 Source Server Version : 80018
 Source Host           : localhost:3306
 Source Schema         : mybatis

 Target Server Type    : MySQL
 Target Server Version : 80018
 File Encoding         : 65001

 Date: 09/05/2021 15:16:33
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for blog
-- ----------------------------
DROP TABLE IF EXISTS `blog`;
CREATE TABLE `blog`  (
  `id` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '博客id',
  `title` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '博客标题',
  `author` varchar(30) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '博客作者',
  `create_time` datetime(0) NOT NULL COMMENT '创建时间',
  `view` int(30) NOT NULL COMMENT '浏览量'
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

  • 实体类Blog
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;
    private int view;
}
  • 插入数据写的java代码
@Test
public void addTest(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Blog blog = new Blog();
    blog.setId(IDUtil.getId());
    blog.setTitle("java");
    blog.setAuthor("喻老师");
    blog.setCreateTime(new Date());
    blog.setView(100);
    mapper.addBlog(blog);

    blog.setId(IDUtil.getId());
    blog.setTitle("c");
    blog.setAuthor("喻老师");
    blog.setCreateTime(new Date());
    blog.setView(100);
    mapper.addBlog(blog);

    blog.setId(IDUtil.getId());
    blog.setTitle("c++");
    blog.setAuthor("喻老师");
    blog.setCreateTime(new Date());
    blog.setView(100);
    mapper.addBlog(blog);

    blog.setId(IDUtil.getId());
    blog.setTitle("python");
    blog.setAuthor("喻老师");
    blog.setCreateTime(new Date());
    blog.setView(100);
    mapper.addBlog(blog);
    sqlSession.close();
}
  • 生产id用的一个UUID
package com.yxx.util;

import org.junit.Test;

import java.util.UUID;

public class IDUtil {
    public static String getId(){
        return UUID.randomUUID().toString();
    }

    @Test
    public void test(){
        System.out.println(IDUtil.getId());
    }
}

9.2.if

用例子来说明

  • mapper(dao)
//动态if语句
public List<Blog> queryIf(Map map);
  • xml
   <!--满足if条件就拼接语句 -->
<select id="queryIf" parameterType="map" resultType="blog">
    select * from blog
    where 1 = 1
    <if test="title != null">
        and title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</select>
  • 测试
@Test
public void queryIf(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();

    //map.put("title","java");
    //map.put("author","喻老师");
    List<Blog> blogs = mapper.queryIf(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }

    sqlSession.close();
}

可以看出我们是屏蔽了map的两个参数,这个时候就sql就应该没有拼接语句 应当查出所有

在这里插入图片描述

可以看出并没有拼接语句

当我们传入一个title参数时

明显可以看到拼接了一条sql语句,传入另一个或者两个类似

9.3.choose、when、otherwise

有得时候我们只想要多个条件中的一个,针对这种情况,就该用choose元素了,和java中的switch语句类似

还是上面的例子,但是策略变为:传入了 “title” 就按 “title” 查找,传入了 “author” 就按 “author” 查找的情形。若两者都没有传入,就返回全部

  • mapper(dao)
//动态choose语句
public List<Blog> queryChoose(Map map);
  • xml
<select id="queryChoose" parameterType="map" resultType="blog">
    select * from blog
    where 1 = 1
    <choose>
        <when test="title != null">
            and title = #{title}
        </when>
        <when test="author != null">
            and author = #{author}
        </when>
        <otherwise>
            and 1 = 1
        </otherwise>
    </choose>
</select>
  • 测试
@Test
public void queryChooseTest(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();

    map.put("title","java");
    //map.put("author","喻老师");
    List<Blog> blogs = mapper.queryChoose(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }

    sqlSession.close();
}

在这里插入图片描述

我们发现他拼接title = ?,因为他在choose里第一个满足。

如果map传入两个参数,还是只会选择一个条件拼接,他会选择第一个满足的条件拼接

9.4.trim、where、set

9.4.1.where

前面几个例子已经方便的解决了动态sql的问题,但是仔细看看我们前面写的sql语句

select * from blog
where 1 = 1

这个where 1 = 1是不是不符合规范,但是我们将1 = 1去掉,如果下面的语句不满足条件,查询语句就变成了

select * from blog
where 

这样的查询就有问题,有bug查不出数据。如果下面语句满足条件,查询语句就变成了

select * from blog
where and

这样查询语句还是有问题,这样这个问题就很难解决,当然你可以像前面一样写一个不规范的1 = 1,这样的确可以解决这个问题。那就没必要学习trim、where、set

我们只需要加上where标签即可

<!--满足if条件就拼接语句 -->
    <select id="queryIf" parameterType="map" resultType="blog">
        select * from blog
        <where>
        <if test="title != null">
            and title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
        </where>
    </select>

测试

@Test
public void queryIf(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();

    map.put("title","java");
    //map.put("author","喻老师");
    List<Blog> blogs = mapper.queryIf(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }

    sqlSession.close();
}
  • 但我们传入一个参数时
select * from blog WHERE title = ?

直接帮我们拼接了一个语句

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>

9.4.2.set

用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列

  • mapper(dao)
//动态set语句
public int querySet(Map map);
  • xml
<update id="querySet" parameterType="map" >
    update blog
    <set>
        <if test="title != null"> title = #{title},</if>
        <if test="author != null"> author = #{author},</if>
        <if test="view != null"> view = #{view}</if>
    </set>
    where id = #{id}
</update>
  • 测试
@Test
public void querySetTest(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();

    map.put("id","9ddde969-6b61-46df-a056-8ee9add64e75");
    map.put("author","小明");
    mapper.querySet(map);
    sqlSession.close();
}

这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

来看看与 set 元素等价的自定义 trim 元素吧:

<trim prefix="SET" suffixOverrides=",">
...
</trim>

9.5.foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)

  • mapper(dao)
//动态Foreach语句
public List<Blog> queryForeach(Map map);
  • xml
<select id="queryForeach" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <if test="ids != null">
            id in 
            <!-- open:开始
			separator:分隔符
			close:结束
-->
            <foreach collection="ids" item="id" open="(" separator="," close=")">
                #{id}
            </foreach>
        </if>
    </where>
</select>
  • 测试
@Test
public void queryForeachTest(){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();
    ArrayList<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    if(list.size() > 0)
        map.put("ids",list);
    mapper.queryForeach(map);
    sqlSession.close();
}

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值