Mybatis Notes

1 Mybatis 介绍

什么是MyBatis?MyBatis是一款持久层框架,用于简化JDBC开发。提升开发效率、降低资源浪费

  • 持久层:指的是数据访问层(dao),用来操作数据库
    在这里插入图片描述

1.1 快速入门

1.1.1 SQL数据准备:

-- 用户表
create table user(
    id int unsigned primary key auto_increment comment 'ID',
    name varchar(100) comment '姓名',
    age tinyint unsigned comment '年龄',
    gender tinyint unsigned comment '性别, 1:男, 2:女',
    phone varchar(11) comment '手机号'
) comment '用户表';

-- 测试数据
insert into user(id, name, age, gender, phone) VALUES (null,'白眉鹰王',55,'1','18800000000');
insert into user(id, name, age, gender, phone) VALUES (null,'金毛狮王',45,'1','18800000001');
insert into user(id, name, age, gender, phone) VALUES (null,'青翼蝠王',38,'1','18800000002');
insert into user(id, name, age, gender, phone) VALUES (null,'紫衫龙王',42,'2','18800000003');
insert into user(id, name, age, gender, phone) VALUES (null,'光明左使',37,'1','18800000004');
insert into user(id, name, age, gender, phone) VALUES (null,'光明右使',48,'1','18800000005');

1.1.2 pojo实体类:

public class User {
    private Integer id;   //id(主键)
    private String name;  //姓名
    private Short age;    //年龄
    private Short gender; //性别
    private String phone; //手机号
    
    //省略GET, SET方法
}

1.1.3 application.properties配置文件:

# 应用名称
spring.application.name=springboot_mybatis-crud
# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据源名称
spring.datasource.name=defaultDataSource
# 数据库连接地址
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC
# 数据库用户名&密码:
spring.datasource.username=root
spring.datasource.password=123456
#下面这些内容是为了让MyBatis映射
#指定Mybatis的Mapper文件
mybatis.mapper-locations=classpath:mappers/*xml
#指定Mybatis的实体目录
mybatis.type-aliases-package=com.sesameseed.mybatis.entity
# 配置mybatis的sql语句输出到控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

1.1.4 mapper数据库操作类:

import com.itheima.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;

@Mapper//生成该接口的实现类,并放到spring容器中
public interface UserMapper {
    
    //查询所有用户数据
    @Select("select id, name, age, gender, phone from user")
    public List<User> list();
    
}
注解解释
@Mapper1、表示是mybatis中的Mapper接口;2、注意:程序运行时:框架会自动生成接口实现类对象(代理对象),并交给Spring的IOC容器管理
@Select代表的就是select查询,用于书写select查询语句

1.1.5 单元测试类:

@SpringBootTest
public class MybatisQuickstartApplicationTests {
	
    @Autowired
    private UserMapper userMapper;
	
    @Test
    public void testList(){
        List<User> userList = userMapper.list();
        for (User user : userList) {
            System.out.println(user);
        }
    }
}

2 JDBC

2.1 JDBC介绍

JDBC: ( Java DataBase Connectivity ),使用Java语言操作关系型数据库的一套API。

注意:使用这套接口(JDBC)编程,真正执行是驱动jar包中的实现类

在这里插入图片描述

2.3 JDBC问题分析

原始JDBC程序,存在以下问题:

  1. 数据库链接的四要素(驱动、链接、用户名、密码)全部硬编码在java代码中
  2. 查询结果的解析及封装非常繁琐
  3. 每次查询数据库都需获取连接,操作完毕释放连接, 资源浪费, 性能降低

2.4 Mybatis与JDBC技术对比

Mybatis中:

  • 数据库连接四要素(驱动、链接、用户名、密码),都配置在springboot默认配置文件 application.properties中
  • 查询结果解析及封装,由mybatis自动完成映射封装,我们无需关注
  • 在mybatis中使用了数据库连接池技术,从而避免频繁创建连接、销毁连接而带来的资源浪费。
  • Mybatis提升开发效率、降低资源浪费

2.5 mybatis和jdbc的区别

JDBC(Java database connectivity)mybatis
① jdbc是java提供用于访问数据库的API,其允许Java程序通过标准SQL语句喻户数据库进行交互,jdbc提供连接数据库、执行SQL语句、处理事务的功能。① mybatis是开源持久层框架,其简化使用jdbc进行数据库访问的流程,通过更方便的方式来执行SQL语句、处理结果集。
② 支持对象与数据库表间表映射
③ mybatis可以看做是jdbc基础上一个数据访问层扩展,其提供了很多其他功能,如:缓存、动态SQL
④使用mybatis可以将SQL与Java代码进行解耦,通过配置文件将SQL与Java方法进行映射,从而简化数据访问的代码编写过程,并通过更好可读性与维护下性

总结:jdbc是Java提供用于访问数据库底层的API,而mybatis则是在jdbc基础上提供更高级、更方便、更易用的数据库访问框架,并提供诸如映射、缓存等新功能。

3 数据库连接池

3.1 数据库连接池介绍

没有使用数据库连接池:
客户端执行SQL语句:要先创建新连接对象,然后执行SQL语句,执行后又需关闭连接对象、释放资源,每次执行SQL都需要创建连接、销毁链接,频繁重复创建销毁的过程是浪费计算机性能。

数据库连接池是个容器,负责分配、管理数据库连接(Connection)

  • 程序在启动时,会在数据库连接池(容器)中,创建一定数量的Connection对象

允许应用程序重复使用一个现有数据库连接,而不重新建立

  • 客户端在执行SQL时,先从连接池中获取一个Connection对象,然后再执行SQL语句,SQL执行完后,释放Connection时就会把Connection对象归还给连接池(Connection对象可以复用
  • 客户端获取到Connection对象,但是Connection对象并没有去访问数据库(处于空闲),数据库连接池发现Connection对象的空闲时间 > 连接池中预设的最大空闲时间,此时数据库连接池会自动释放掉这个连接对象。

数据库连接池的好处:

1. 资源重用
2. 提升系统响应速度
3. 避免数据库连接遗漏

3.2 数据库连接池产品产品

常见的数据库连接池:C3P0、DBCP、Druid、Hikari (springboot默认),现在使用更多的是:Hikari、Druid (性能更优越)

  • Hikari(追光者) [默认的连接池]
    在这里插入图片描述

Druid(德鲁伊)

  • Druid连接池是阿里巴巴开源的数据库连接池项目
  • 功能强大,性能优秀,是Java语言最好的数据库连接池之一

Druid参考官方地址:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter

3.3 Druid引入项目

3.3.1 在pom.xml文件中引入依赖

<dependency>
    <!-- Druid连接池依赖 -->
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.8</version>
</dependency>

3.3.2 在application.properties中引入数据库连接配置

# 应用名称
spring.application.name=springboot_mybatis-crud
# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据源名称
spring.datasource.name=defaultDataSource
# 数据库连接地址
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC
# 数据库用户名&密码:
spring.datasource.username=root
spring.datasource.password=123456
#下面这些内容是为了让MyBatis映射
#指定Mybatis的Mapper文件
mybatis.mapper-locations=classpath:mappers/*xml
#指定Mybatis的实体目录
mybatis.type-aliases-package=com.sesameseed.mybatis.entity

# 配置mybatis的sql语句输出到控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

4 lombok

4.1 lombok介绍

Lombok是一个实用的Java类库,可以通过简单注解来简化、消除一些必须有但臃肿的Java代码。

注解作用
@Getter/@Setter为所有的属性提供get/set方法
@ToString会给类自动生成易阅读的 toString 方法
@EqualsAndHashCode根据类所拥有的非静态字段自动重写 equals 方法和 hashCode 方法
@Data(@Getter + @Setter + @ToString + @EqualsAndHashCode)
@NoArgsConstructor为实体类生成无参的构造器方法
@AllArgsConstructor为实体类生成除了static修饰的字段之外带有各参数的构造器方法。

4.2 lombok使用

4.2.1 在pom.xml文件中引入依赖

<!-- 在springboot的父工程中,已经集成了lombok并指定了版本号,故当前引入依赖时不需要指定version -->
<dependency>
  <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

4.2.2 pojo类代码引入

package com.sesameseed.pojo;



import lombok.*;

//@Getter
//@Setter
//@ToString
//@EqualsAndHashCode

@Data  //替代上边4个,不包含下方四个

@NoArgsConstructor //无参构造
@AllArgsConstructor //全参构造
public class User {

    private Integer id;
    private String name;
    private Short age;
    private Short gender;
    private String phone;
}

5 Mybatis基础操作

5.1 准备

实施前的准备工作:

  • 准备数据库表
  • 创建一个新的springboot工程,选择引入对应的起步依赖(mybatis、mysql驱动、lombok)
  • application.properties中引入数据库连接信息
  • 创建对应的实体类 Emp(实体类属性采用驼峰命名)
  • 准备Mapper接口 EmpMapper

5.1.1 创建一个新的springboot工程,选择引入对应的起步依赖(mybatis、mysql驱动、lombok)

在这里插入图片描述
5.1.2 application.properties中引入数据库连接信息

# 应用名称
spring.application.name=tlias-demo
# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据库连接地址
spring.datasource.url=jdbc:mysql://localhost:3306/tlias?serverTimezone=UTC
# 数据库用户名&密码:
spring.datasource.username=root
spring.datasource.password=123456
# 应用服务 WEB 访问端口
server.port=8080
#开启mybatis的日志输出
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#开启数据库表字段 到 实体类属性的驼峰映射
mybatis.configuration.map-underscore-to-camel-case=true

5.1.3 创建对应的实体类Emp(实体类属性采用驼峰命名

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {
    private Integer id;
    private String username;
    private String password;
    private String name;
    private Short gender;
    private String image;
    private Short job;
    private LocalDate entrydate;     //LocalDate类型对应数据表中的date类型
    private Integer deptId;
    private LocalDateTime createTime;//LocalDateTime类型对应数据表中的datetime类型
    private LocalDateTime updateTime;
}

5.1.4 准备Mapper接口:EmpMapper

注解解释
@Mappe当前接口为mybatis中Mapper接口,程序运行时自动创建接口的实现类对象代理对象,并交给Spring的IOC容器管理
@Mapper
public interface EmpMapper {

}

最终工程结构目录:
在这里插入图片描述

5.2 删除

5.2.1 删除功能实现

页面原型:
在这里插入图片描述

5.2.1.1 功能:根据主键删除数据

-- 需求:删除id=17的数据
-- SQL语句
delete from emp where id = 17;

5.2.1.2 Mapper(Dao)数据访问层(CRUD)方法

注解解释
@DELETE该注解标记的方法,表明此方法执行的是删除资源的操作,其返回值可以是void(没有返回值)
@Mapper
public interface EmpMapper {    
    //@Delete("delete from emp where id = 17")
    //public void delete();
    //以上delete操作的SQL语句中的id值写成固定的17,就表示只能删除id=17的用户数据
    //SQL语句中的id值不能写成固定数值,需要变为动态的数值
    //解决方案:在delete方法中添加一个参数(用户id),将方法中的参数,传给SQL语句
    
    /**
     * 根据id删除数据
     * @param id    用户id
     */
    @Delete("delete from emp where id = #{id}")//使用#{key}方式获取方法中的参数值
    public void delete(Integer id);    
}

5.2.1.3 测试类方法

注解解释
@Autowired从Spring的IOC容器中,获取到类型是EmpMapper的对象,并注入到声明此注解的类中
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
    @Autowired //从Spring的IOC容器中,获取类型是EmpMapper的对象并注入
    private EmpMapper empMapper;

    @Test
    public void testDel(){
        //调用删除方法
        empMapper.delete(16);
    }
}

5.2.2 日志输入

application.properties配置类中开启日志:

#指定mybatis输出日志的位置, 输出控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

控制台日志打印出,并且打印出预编译SQL
在这里插入图片描述

5.2.3 预编译SQL

预编译优势

  • 性能更高编译一次将编译后SQL缓存起来,再次执行语句时,不会再次编译。(只是输入的参数不同)
  • 更安全(防止SQL注入,将敏感字进行转义,保障SQL安全性。)
    在这里插入图片描述

5.2.4 SQL注入

关键代码块

' or '1'='1

SQL编译后:
由于1=1是永远成立,因而可以完成注入,SQL客户端也提示always true
在这里插入图片描述

5.2.5 参数占位符

Mybatis中提供的参数占位符有两种:${...}#{...}

#{…}说明

  • 执行SQL时,将#{…}替换为?,生成预编译SQL,自动设置参数值
  • 使用时机:参数传递,都使用#{…}.优先使用

${…}说明

  • 拼接SQL。直接将参数拼接在SQL中,存在SQL注入问题。

5.3 新增

功能:新增员工信息
在这里插入图片描述

5.3.1 新增功能实现

SQL语句:

insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values ('songyuanqiao','宋远桥',1,'1.jpg',2,'2012-10-09',2,'2022-10-01 10:00:00','2022-10-01 10:00:00');

5.3.1.1 Mapper(Dao)数据访问层(CRUD)方法

@Mapper
public interface EmpMapper {
//说明:#{...} 里面写的名称是对象的属性名
    @Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")
    public void insert(Emp emp);
}

5.3.1.2 测试类方法

@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
    @Autowired
    private EmpMapper empMapper;

    @Test
    public void testInsert(){
        //创建员工对象
        Emp emp = new Emp();
        emp.setUsername("tom");
        emp.setName("汤姆");
        emp.setImage("1.jpg");
        emp.setGender((short)1);
        emp.setJob((short)1);
        emp.setEntrydate(LocalDate.of(2000,1,1));
        emp.setCreateTime(LocalDateTime.now());
        emp.setUpdateTime(LocalDateTime.now());
        emp.setDeptId(1);
        //调用添加方法
        empMapper.insert(emp);
    }
}

5.3.1.3 日志输出
在这里插入图片描述

5.3.2 主键返回

概念:在数据添加成功后,需要获取插入数据库数据主键

5.3.2.1 Mapper(Dao)数据访问层(CRUD)方法

@Mapper
public interface EmpMapper {
    
    //会自动将生成的主键值,赋值给emp对象的id属性
    @Options(useGeneratedKeys = true,keyProperty = "id")
    @Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")
    public void insert(Emp emp);
}

5.3.2.2 测试类

@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
    @Autowired
    private EmpMapper empMapper;

    @Test
    public void testInsert(){
        //创建员工对象
        Emp emp = new Emp();
        emp.setUsername("jack");
        emp.setName("杰克");
        emp.setImage("1.jpg");
        emp.setGender((short)1);
        emp.setJob((short)1);
        emp.setEntrydate(LocalDate.of(2000,1,1));
        emp.setCreateTime(LocalDateTime.now());
        emp.setUpdateTime(LocalDateTime.now());
        emp.setDeptId(1);
        //调用添加方法
        empMapper.insert(emp);

        System.out.println(emp.getDeptId());
    }
}

5.4 更新

功能:修改员工信息
在这里插入图片描述

5.4.1 更新功能实现

SQL语句:

update emp set username = 'linghushaoxia', name = '令狐少侠', gender = 1 , image = '1.jpg' , job = 2, entrydate = '2012-01-01', dept_id = 2, update_time = '2022-10-01 12:12:12' where id = 18;

5.4.1.1 Mapper(Dao)数据访问层(CRUD)方法

@Mapper
public interface EmpMapper {
    /**
     * 根据id修改员工信息
     * @param emp
     */
    @Update("update emp set username=#{username}, name=#{name}, gender=#{gender}, image=#{image}, job=#{job}, entrydate=#{entrydate}, dept_id=#{deptId}, update_time=#{updateTime} where id=#{id}")
    public void update(Emp emp);   
}

5.4.1.2 测试类方法

@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
    @Autowired
    private EmpMapper empMapper;

    @Test
    public void testUpdate(){
        //要修改的员工信息
        Emp emp = new Emp();
        emp.setId(23);
        emp.setUsername("songdaxia");
        emp.setPassword(null);
        emp.setName("老宋");
        emp.setImage("2.jpg");
        emp.setGender((short)1);
        emp.setJob((short)2);
        emp.setEntrydate(LocalDate.of(2012,1,1));
        emp.setCreateTime(null);
        emp.setUpdateTime(LocalDateTime.now());
        emp.setDeptId(2);
        //调用方法,修改员工数据
        empMapper.update(emp);
    }
}

5.5 问:为什么不把pojo中的实体类对象,利用IoC思想注入到IoC容器中?

答:IOC容器是单例的 把pojo放到IOC容器中,会发生数据共享,线程安全问题。

单例模式:1、这种模式涉及到一个单一的类;
2、该类负责创建自己的对象,同时确保只有单个对象被创建;
3、这个类提供一种访问其唯一对象的方式,可以直接访问,不需要实例化该类的对象
注意:

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。

线程安全问题:多个对象操作同一资源,联想此处pojo类想法,pojo实体类要经常被创建,而IoC是单例设计模式的,会发生线程安全问题。

5.6 查询

功能:根据ID查询
在这里插入图片描述

5.6.1 查询功能实现

SQL语句:

select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp;

5.6.1.1 Mapper(Dao)数据访问层(CRUD)方法

@Mapper
public interface EmpMapper {
    @Select("select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp where id=#{id}")
    public Emp getById(Integer id);
}

5.6.1.2 测试类方法

@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
    @Autowired
    private EmpMapper empMapper;

    @Test
    public void testGetById(){
        Emp emp = empMapper.getById(1);
        System.out.println(emp);
    }
}

5.6.1.3 执行结果
在这里插入图片描述

5.6.2 数据封装

引入:5.6.1.3 执行结果中deptId,createTime,updateTime这几个字段没有值
原因:

  • 实体类属性名和数据库表查询返回字段名一致,mybatis会自动封装
  • 如果实体类属性名和数据库表查询返回的字段名不一致不能自动封装

解决方案:

  • 起别名
  • 结果映射
  • 开启驼峰命名

开启驼峰命名(推荐):如果字段名与属性名符合驼峰命名规则,mybatis会自动通过驼峰命名规则映射

  • 驼峰命名规则: abc_xyz => abcXyz
  • 表中字段名:abc_xyz
  • 类中属性名:abcXyz
# 在application.properties配置类中添加:
mybatis.configuration.map-underscore-to-camel-case=true

注意:使用驼峰命名前提: 实体类的属性与数据库表中字段名严格遵守驼峰命名

5.7 条件查询

在员工管理列表页面,需要根据条件查询员工信息,查询条件包括:姓名、性别、入职时间。
在这里插入图片描述

5.7.1 条件查询功能实现

SQL语句:

select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time 
from emp 
where name like '%张%' 
     and gender = 1 
     and entrydate between '2010-01-01' and >'2020-01-01 ' 
order by update_time desc;

5.7.1.1 Mapper(Dao)数据访问层(CRUD)方法:

此方法相比较与where name like '%${name}%'解决SQL注入风险

  • 使用MySQL提供的字符串拼接函数:concat('%' , '关键字' , '%')
@Mapper
public interface EmpMapper {

    @Select("select * from emp " +
            "where name like concat('%',#{name},'%') " +
            "and gender = #{gender} " +
            "and entrydate between #{begin} and #{end} " +
            "order by update_time desc")
    public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end);
}

5.7.1.2 执行结果:生成的SQL是预编译的SQL语句(性能高、安全)
在这里插入图片描述

5.7.2 参数名说明

在上述所编写条件查询功能中,我们需保证接口中方法形参名和SQL语句中参数占位符名相同。

当方法中形参名和SQL语句中占位符参数名不相同时,报以下错误:
在这里插入图片描述
参数名在不同的SpringBoot版本中,处理方案:

  • 在springBoot2.x版本(保证参数名一致)
    在这里插入图片描述
  • 在springBoot1.x版本中,单独使用mybatis的@Param注解来指定SQL语句中的参数名
    在这里插入图片描述

6 Mybatis的XML配置文件

Mybatis的开发有两种方式:

  • 注解方式:来完成一些简单的增删改查功能
  • XML方式:实现复杂的SQL功能

6.1 XML配置文件规范

  1. XML映射文件名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名
  2. XML的namespace属性Mapper接口全限定名一致
  3. XML中sql语句的idMapper接口中方法名一致,并保持返回类型一致
    在这里插入图片描述
标签resultType属性
就是用于编写select查询语句查询返回的单条记录所封装的类型

6.2 XML配置文件实现

6.2.1 编写XML映射文件

xml映射文件中dtd约束,直接从mybatis官网复制

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="">
</mapper>

6.2.2 配置:XML映射文件namespace属性为Mapper接口全限定名

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--此处写代码-->
</mapper>

6.2.3 配置:XML映射文件中sql语句id与Mapper接口中的方法名一致,并保持返回类型一致

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">

    <!--查询操作-->
    <select id="list" resultType="com.itheima.pojo.Emp">
        select * from emp
        where name like concat('%',#{name},'%')
              and gender = #{gender}
              and entrydate between #{begin} and #{end}
        order by update_time desc
    </select>
</mapper>

6.2.4 执行结果
在这里插入图片描述

6.3 MybatisX的使用

在这里插入图片描述

7 Mybatis动态SQL

标签作用
<if>判断条件是否成立。用test属性进行条件判断,如果条件为true,则拼接SQL
<where>会在子元素有内容的情况下才插入where子句,自动去除子句的开头的AND或OR
<set>动态的在SQL语句中插入set关键字,并会删掉额外的逗号。(用于update语句中)
<foreach>遍历deleteByIds方法中传递的参数ids集合)
<sql>定义可重用的SQL片段
<include>通过属性refid,指定包含的SQL片段

SQL语句随着用户输入或外部条件变化而变化,称为:动态SQL

如:在页面原型中,列表上方条件是动态的,可以不传递的,也可以只传递其中1个或2个或全部。
在这里插入图片描述

7.1 动态SQL-if

标签作用
<if>判断条件是否成立。用test属性进行条件判断,如果条件为true,则拼接SQL
<where>会在子元素有内容的情况下才插入where子句,自动去除子句的开头的AND或OR
<set>动态的在SQL语句中插入set关键字,并会删掉额外的逗号。(用于update语句中)
<foreach>遍历deleteByIds方法中传递的参数ids集合)
<sql>定义可重用的SQL片段
<include>通过属性refid,指定包含的SQL片段

<if>:用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL。

<if test="条件表达式">
   要拼接的sql语句
</if>

7.1.1 动态条件查询功能实现

7.1.1.1 原有的SQL语句:

<select id="list" resultType="com.itheima.pojo.Emp">
        select * from emp
        where name like concat('%',#{name},'%')
              and gender = #{gender}
              and entrydate between #{begin} and #{end}
        order by update_time desc
</select>

7.1.1.2 改装成动态SQL语句

标签功能
<where>会在子元素有内容的情况下才插入where子句,自动去除子句的开头的AND或OR
<select id="list" resultType="com.itheima.pojo.Emp">
        select * from emp
        <where>
        <!-- where直接作为关键字出现此,会出现下图7.1.1.2.error错误 -->
             <!-- if做为where标签的子元素 -->
             <if test="name != null">
                 and name like concat('%',#{name},'%')
             </if>
             <if test="gender != null">
                 and gender = #{gender}
             </if>
             <if test="begin != null and end != null">
                 and entrydate between #{begin} and #{end}
             </if>
        </where>
        order by update_time desc
</select>

where直接作为关键字出现
测试类方法,打印结果error

@Test
public void testList(){
   //姓名为null
   List<Emp> list = empMapper.list(null, (short)1, null, >null);
   for(Emp emp : list){
       System.out.println(emp);
  }
}

图7.1.1.2.error
图7.1.1.2.error

7.1.1.3 测试方法

@Test
public void testList(){
    //性别数据为null、开始时间和结束时间也为null
    List<Emp> list = empMapper.list("张", null, null, null);
    for(Emp emp : list){
        System.out.println(emp);
    }
}

7.1.1.4 执行的SQL语句
在这里插入图片描述

7.1.2 动态更新员工功能实现

动态更新员工信息,如果更新时传递有值,则更新;如果更新时没有传递值,则不更新

7.1.2.1 Mapper(Dao)数据访问层(CRUD)方法

修改Mapper接口

@Mapper
public interface EmpMapper {
    //删除@Update注解编写的SQL语句
    //update操作的SQL语句编写在Mapper映射文件中
    public void update(Emp emp);
}

7.1.2.2 修改Mapper映射的XML文件

标签功能
<set>动态的在SQL语句中插入set关键字,并会删掉额外的逗号。(用于update语句中)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">

    <!--更新操作-->
    <update id="update">
        update emp
        <!-- 使用set标签,代替update语句中的set关键字 -->
         <!-- 此处直接使用set关键字,会发生下图7.1.2.2.error错误-->
        <set>
            <if test="username != null">
                username=#{username},
            </if>
            <if test="name != null">
                name=#{name},
            </if>
            <if test="gender != null">
                gender=#{gender},
            </if>
            <if test="image != null">
                image=#{image},
            </if>
            <if test="job != null">
                job=#{job},
            </if>
            <if test="entrydate != null">
                entrydate=#{entrydate},
            </if>
            <if test="deptId != null">
                dept_id=#{deptId},
            </if>
            <if test="updateTime != null">
                update_time=#{updateTime}
            </if>
        </set>
        where id=#{id}
    </update>
</mapper>

图7.1.2.2.error:

测试类方法

@Test
public void testUpdate2(){
       //要修改的员工信息
       Emp emp = new Emp();
       emp.setId(20);
      emp.setUsername("Tom222");      
      //调用方法,修改员工数据
       empMapper.update(emp);
}


在这里插入图片描述
7.1.2.3 测试方法返回结果

@Test
public void testUpdate2(){
        //要修改的员工信息
        Emp emp = new Emp();
        emp.setId(20);
        emp.setUsername("Tom111");
        emp.setName("汤姆111");

        emp.setUpdateTime(LocalDateTime.now());

        //调用方法,修改员工数据
        empMapper.update(emp);
}

在这里插入图片描述

7.2 动态SQL-foreach

案例:员工删除功能(既支持删除单条记录,又支持批量删除)
在这里插入图片描述

功能所对应的示例SQL语句:

delete from emp where id in (1,2,3);

7.2.1 动态SQL-foreach功能实现

7.2.1.1 Mapper(Dao)数据访问层(CRUD)方法

@Mapper
public interface EmpMapper {
    //批量删除
    public void deleteByIds(List<Integer> ids);
}

7.2.1.1 Mapper(Dao)数据访问层对应的XML映射文件

标签作用
<foreach>遍历deleteByIds方法中传递的参数ids集合)

方法示例:

<foreach collection="集合名称" item="集合遍历出来的元素/项" separator="每一次遍历使用的分隔符" 
         open="遍历开始前拼接的片段" close="遍历结束后拼接的片段">
</foreach>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
    <!--删除操作-->
    <delete id="deleteByIds">
        delete from emp where id in
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </delete>
</mapper> 

注意:#{id}必须与item中id名字保持一致==
在这里插入图片描述

执行的SQL结果
在这里插入图片描述

7.3 动态SQL-sql&include

标签作用
<sql>定义可重用的SQL片段
<include>通过属性refid,指定包含的SQL片段

问题:xml映射文件中配置的SQL,有时存在很多重复片段,存在很多冗余代码
在这里插入图片描述

  • 因此对重复代码片段进行抽取,通过<sql>标签封装到SQL片段,再通过<include>标签进行引用。
    在这里插入图片描述
    SQL片段: 抽取重复的代码
<sql id="commonSelect">
	select id, username, password, name, gender, >image, job, entrydate, dept_id, create_time, >update_time from emp
</sql>

通过<include> 标签在原来抽取的地方进行引用

<select id="list" resultType="com.itheima.pojo.Emp">
   <include refid="commonSelect"/>
   <where>
       <if test="name != null">
           name like concat('%',#{name},'%')
     </if>
    <if test="gender != null">
          and gender = #{gender}
      </if>
      <if test="begin != null and end != null">
           and entrydate between #{begin} and #{end}
       </if>
   </where>
   order by update_time desc
</select>

8 mybatis一对多查询

8.1 一对多查询、嵌套查询概念

一对多查询:指在关系型数据库中,两个表间存在一对多关系(例如部门表员工表,一个部门可以包含多个用户,多个用户可以属于一个部门)。

嵌套查询:在我们进行一对多查询同时,需要使用多个SQL语句来查询多个表,并通过特定关键字或条件将多个结果集关联起来,例如:

select id,name,sex,hiredate,sys_dept,deptno,dno from dept,emp where emp.deptno = dept.deptno

8.2 < resultMap >标签

< resultMap >标签主要用来定义映射规则、级联的更新以及定义类型转换器等。

<resultMap id = "" type="">
	<constructor><!--类型实例化时用来注入结果到构造方法-->
	</constructor>
	<id></id><!--用来标识哪个是主键-->
	<result></result><!--注入到字段或JavaBean属性的普通结果-->
	<association property=""></association ><!--用于一对一关联-->
	<collection property=""></collection ><!--用于一对多、多对多关联-->
</resultMap>

8.3 为什么使用< resultMap >

  • ① 当字段与pojo属性不一致时,使用resultMap自定义映射规则。
  • ② 当需要完成复杂映射时,例如(1对1,1对多)时,必须使用resultMap。

8.4 mybatis一对多案例

8.4.1 定义两个表

① 表一:emp(员工表:id、name、sex、depno),表二:dept(部门表:deptno、dname),其:一个部门可以包含多员工、多个员工属于一个部门。

定义两个实体类:

  • Emp(id,name,dept,hiredate)
  • Dept(deptno、dname、List< Emp >)

8.4.2 Mapper的XML映射书写

<select id = "select" resultMap="empResultMap">
select id,name,sex,hiredate,sys_dept,deptno,dno from dept,emp where emp.deptno = dept.deptno
</select><!--此条SQL由于跟实体类是不能一一对应的,因此并不能成功映射-->

<!--自定义映射规则-->
<resultMap id = "empResultMap" type="com.jiao.entity.Emp">
	<id property="id" column="id"></id>
	<result property="sex" column="sex"></result>
	<result property="name" column="name"></result >
	<result property="hiredate" column="hiredate"></result >
   
   <association property="dept" javaType= "com.jiao.entity.Dept">
   		<id property="depno" column="depno"></id>
   		<result property="dname" column="dname"></result >
   </association>
 </resultMap>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值