【MyBatis】基础全网最全,看这篇就够了

一、原始JDBC开发存在的问题

package com.qf.java2107.test;
​
import org.junit.Test;
​
import java.math.BigDecimal;
import java.sql.*;
​
/**
 * @author ghy
 * @version 1.0
 * @date 2021-12-22
 **/
public class JdbcTest {
​
    /**
     * 存在的问题:
     *      1. 需要频繁的手动获取连接
     *      2. 需要手动封装查询结果集
     *      3. 需要手动释放资源
     *      4. SQL硬编码
     *   有自己封装的工具类,也有DBUtils等工具类API。但是都没有从根本上解决上面的问题
     */
    @Test
    public void testJdbc(){
​
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
​
        try {
            //1.反射加载驱动
            Class.forName("com.mysql.jdbc.Driver");
​
            //2.获取数据库连接
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/java2106_hotel", "root", "root");
​
            //3.编写SQL
            String sql = "SELECT * FROM t_user WHERE user_id = ?";
​
            //4.获取执行SQL的载体对象,预编译SQL
            preparedStatement = connection.prepareStatement(sql);
            //填充占位符
            preparedStatement.setLong(1, 3);
​
            //5.执行SQL
            resultSet = preparedStatement.executeQuery();
​
            //6.处理结果
            if (resultSet.next()) {
                //user_id : 结果集的列名
                long userId = resultSet.getLong("user_id");
                String username = resultSet.getString("username");
                BigDecimal balance = resultSet.getBigDecimal("balance");
                System.out.println(userId + "," + username + "," + balance );
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
​
            //7.释放资源
            //先开后关
            try {
                if(null != resultSet) {
                    resultSet.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if(null != preparedStatement) {
                    preparedStatement.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if(null != connection) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
​
    }
}
  • 存在的问题

    • 需要频繁的手动获取连接
    • 需要手动封装查询结果集
    • 需要手动释放资源
    • SQL硬编码

二、ORM框架

  • 通过ORM框架解决JDBC存在的问题

1、ORM

  • Object Relation Mapping:对象关系映射

Java对象 数据库表
类名 表名
属性名 列名【字段名】
对象 行【记录】

2、框架

  • 概述:就是一个半成品软件

    • 需要使用框架帮我们完成JavaEE应用的开发

  • 作用

    • 能够帮我们快速有效的开发JavaEE应用

    • 有自己对应用场景的完整解决方案

三、Mybatis

1、概述

  • 是一款开源的、优秀的、支持定制SQL的ORM框架

  • 官网:mybatis – MyBatis 3 | 简介

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

2、快速入门

  • 参考官网案例

3.2.1 实现步骤

  1. 创建数据库表

  2. 导入依赖

  3. 实体类

  4. Mapper接口【Dao接口】

  5. SQL映射文件

  6. 全局配置文件

  7. 测试

3.2.2 具体实现

  • 创建数据库表

CREATE TABLE `t_user` (
  `user_id` bigint(100) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `username` varchar(20) DEFAULT NULL COMMENT '用户名',
  `password` varchar(32) DEFAULT NULL COMMENT '密码',
  `nick_name` varchar(20) DEFAULT NULL COMMENT '昵称',
  `is_admin` tinyint(4) DEFAULT NULL COMMENT '是否管理员 0:否  1:是',
  `phone` varchar(11) DEFAULT NULL COMMENT '手机',
  `gender` tinyint(4) DEFAULT NULL COMMENT '性别 0:保密 1:男 2:女',
  `birth` date DEFAULT NULL COMMENT '生日',
  `user_status` tinyint(4) DEFAULT NULL COMMENT '状态(是否激活) 0:否 1:是',
  `user_create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `user_update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `is_delete` tinyint(4) DEFAULT NULL COMMENT '是否删除 0:否 1:是',
  `is_member` tinyint(4) DEFAULT NULL COMMENT '是否会员 0:否 1:是',
  `balance` decimal(20,2) DEFAULT NULL COMMENT '账户余额',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COMMENT='这是一个用户表';
​
​
insert  into `t_user`(`user_id`,`username`,`password`,`nick_name`,`is_admin`,`phone`,`gender`,`birth`,`user_status`,`user_create_time`,`user_update_time`,`is_delete`,`is_member`,`balance`) values 
(1,'aa','e10adc3949ba59abbe56e057f20f883e','666',1,'13566778899',1,'1990-07-18',0,'2021-10-15 10:56:40','2021-10-22 10:56:43',0,1,10000.00),
(2,'bb','bb','666',1,'111111',1,'1990-07-18',0,'2021-11-02 14:20:25','2021-11-03 09:25:58',0,1,1111.00),
(3,'cc','mark123','666',1,'13512341234',0,'1990-07-18',0,'2021-11-03 10:05:37','2021-11-03 10:05:37',0,1,10000.00),
(5,'dd','BB','666',1,'13512341234',0,'1990-07-18',0,'2021-11-03 10:15:00','2021-11-03 10:15:00',0,1,10000.00),
(6,'ee','CC','666',1,'13512341234',2,'1990-07-18',0,'2021-11-03 10:40:01','2021-11-03 10:40:01',0,1,10000.00),
(7,'ff','mark123','666',0,'13512341234',0,'1990-07-18',0,'2021-11-05 11:23:45','2021-11-05 11:23:45',0,1,10000.00);
  • 导入依赖


​
    
        mysql
        mysql-connector-java
        5.1.6
    
​
    
        org.mybatis
        mybatis
        3.5.2
    
​
    
        junit
        junit
        4.12
    
​
    
    
        org.projectlombok
        lombok
        1.18.22
        provided
    
  • 实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
​
    private Long userId;
    private String username;   //成员变量
    private String password;
    private String nickName;
    private Integer isAdmin;
    private String phone;
    private Integer gender;
    private Date birth;
    private Integer userStatus;
    private Date userCreateTime;
    private Date userUpdateTime;
    private Integer isDelete;
    private Integer isMember;
    private BigDecimal balance;
​
}
  • Mapper接口

public interface IUserMapper {
​
    /**
     * 查询所有
     * @return
     */
    List findAll();
​
}
  • SQL【Mapper】映射文件





    
  • 全局配置文件




    
        
            
            
                
                
                
                
            
        
    
    
        
    
  • 测试代码

/**
 * mapper接口代理测试,掌握
 **/
@Test
public void test02() {
    SqlSession session = null;
    try {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
​
        session = sqlSessionFactory.openSession();
        //获取mapper代理
        IUserMapper userMapper = session.getMapper(IUserMapper.class);
        List users = userMapper.findAll();
        //mybatis能够帮我们自动完成查询结果集和实体间的映射,前提结果集列名和实体属性名相同
        for (User user : users) {
            System.out.println(user);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(null != session) {
            session.close();
        }
    }
}

四、CURD操作

  • 增删改操作涉及事务,务必要提交,查询不需要进行事务提交

1、增加

  • Mapper接口

/**
 * 增加
 * @param user
 */
void save(User user);
Mapper映射文件



    INSERT INTO t_user (
      username,password,nick_name,is_admin,phone,gender,birth,user_status,
      user_create_time,user_update_time,is_delete,is_member,balance)
    VALUES
      (#{username}, #{password}, #{nickName}, #{isAdmin}, #{phone}, #{gender}, #{birth}, #{userStatus},
      #{userCreateTime}, #{userUpdateTime}, #{isDelete}, #{isMember}, #{balance})
  • 测试类

/**
 * 增加
 **/
@Test
public void test03() throws Exception {
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
    SqlSession sqlSession = factory.openSession();
    IUserMapper userMapper = sqlSession.getMapper(IUserMapper.class);
    User user = new User();
    user.setUsername("lucy");
    user.setPassword("lucy123");
    user.setNickName("露西");
    user.setIsAdmin(0);
    user.setPhone("13566778899");
    user.setGender(0);
    user.setBirth(java.sql.Date.valueOf("2005-10-30"));
    user.setUserStatus(1);
    user.setUserCreateTime(new Date());
    user.setUserUpdateTime(new Date());
    user.setIsDelete(0);
    user.setIsMember(1);
    user.setBalance(new BigDecimal(2000));
    userMapper.save(user);
​
    //提交事务
    sqlSession.commit();
​
    if(null != sqlSession) {
        sqlSession.close();
    }
​
}

1.1 主键回填

  • 主键自增

    • 方式一【Mapper映射文件】

    
    
    ​
        
        
            SELECT LAST_INSERT_ID()
        
        INSERT INTO t_user 
          (username,password,nick_name,is_admin,phone,gender,birth,user_status,
          user_create_time,user_update_time,is_delete,is_member,balance)
        VALUES
            (#{username}, #{password}, #{nickName}, #{isAdmin}, #{phone}, #{gender}, #{birth}, 
        #{userStatus},#{userCreateTime}, #{userUpdateTime}, #{isDelete}, #{isMember}, #{balance})
    
    • 方式二

    
    
    
        INSERT INTO t_user
            (username,password,nick_name,is_admin,phone,gender,birth,user_status,
            user_create_time,user_update_time,is_delete,is_member,balance)
        VALUES
        (#{username}, #{password}, #{nickName}, #{isAdmin}, #{phone}, #{gender}, #{birth}, #{userStatus},
        #{userCreateTime}, #{userUpdateTime}, #{isDelete}, #{isMember}, #{balance})
    

2、修改



    UPDATE t_user
    SET PASSWORD = #{password}, nick_name = #{nickName}, user_update_time = #{userUpdateTime}
    WHERE user_id = #{userId}

3、删除



    DELETE FROM t_user WHERE user_id = #{userId}

4、主键是字符串实现主键回填

【MyBatis】基础全网最全,看这篇就够了_第1张图片

  • 实体类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * @author ghy
 * @version 1.0
 * @date
 **/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Stu {
​
    private String stuId;
    private String stuName;
​
}

  • Mapper接口和映射文件




​
    
    
        
            SELECT REPLACE(UUID(), '-', '')
        
        insert into t_stu (stu_id, stu_name) values (#{stuId},#{stuName})
    

五、日志

1、日志体系

  • Slf4j:接口【门面】

    • Log4j

    • Logback

    • commons-logging

2、日志作用

  • 没日志:很难判断问题来源

  • 有日志:一般都可以判断问题来源。

    • 可以通过日志把问题错误信息给记录下来

3、使用

  • 引入依赖



    org.slf4j
    slf4j-log4j12
    1.7.7


    log4j
    log4j
    1.2.17
  • 日志配置文件【log4j.properties】

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=ssm.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.rootLogger=debug, stdout, file
  • 日志级别

级别 描述
ALL LEVEL 打开所有日志记录开关;是最低等级的,用于打开所有日志记录。
DEBUG 输出调试信息;指出细粒度信息事件对调试应用程序是非常有帮助的。【开发使用】
INFO 输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。【线上使用】
WARN 输出警告信息;表明会出现潜在错误的情形。
ERROR 输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。
FATAL 输出致命错误;指出每个严重的错误事件将会导致应用程序的退出。
OFF LEVEL 关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录。
  • 直接使用API即可

package com.qf.java2107.test;
​
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
​
/**
 * @author ghy
 * @version 1.0
 * @date 2021-12-23
 **/
public class LoggerTest {
​
    Logger logger = LoggerFactory.getLogger(LoggerTest.class);
​
    /**
     *
     **/
    @Test
    public void logTest() throws Exception {
        logger.debug("debug---->{}", "debug level");
        logger.info("info--->{}---{}", "aa", "bb");
        logger.warn("warn----->");
        logger.error("error---->");
    }
}

4、常用插件

【MyBatis】基础全网最全,看这篇就够了_第2张图片

【MyBatis】基础全网最全,看这篇就够了_第3张图片

六、Mapper接口参数绑定

1、单个简单类型参数

基本数据类型及其包装类,String

Mapper映射文件获取参数值时,名称可以随意。但是强烈建议参数名跟形参名一致

【MyBatis】基础全网最全,看这篇就够了_第4张图片

2、实体类型参数

JavaBean

Mapper映射文件获取参数值时,使用#{},{}中写JavaBean的属性名

【MyBatis】基础全网最全,看这篇就够了_第5张图片

3、Map入参

Mapper映射文件获取参数值时,使用#{},{}中写Map中key的名称
【MyBatis】基础全网最全,看这篇就够了_第6张图片

4、多个参数

mybatis会把参数封装成一个Map,第一个参数的key为arg0param1】,第二参数的key为arg1param2】,以此类推。

以上方式不推荐使用,可读性太差。

Mapper映射文件获取参数值时,使用@Param来为Mapper接口方法指定入参的参数名,使用#{}取值

【MyBatis】基础全网最全,看这篇就够了_第7张图片

七、ORM映射

把查询结果集跟JavaBean进行映射绑定

1、映射规则

  • 当查询结果集的列名跟JavaBean属性名相同时,自动映射

  • 当查询结果集的列名跟JavaBean属性名不相同时,需要手动映射

2、不满足映射规则

  • 如果满足驼峰

    • 使用别名进行映射【但是SQL太长】

    
    
    • 全局配置文件【mybatis-config.xml】开启驼峰映射

    
        
        
    
    
    

3、ResultMap

  • 自定义结果集映射

    • mybatis内部已经集成了结果集映射,就是Mapper映射文件的resultType属性。resultType底层使用其实也是ResultMap

    • resultType和resultMap最好只使用一个

      • 如果满足驼峰并且不使用别名的情况下,可以使用resultType

      • resultMap可以在查询结果集封装中自定义使用。连表查询只能使用resultMap



    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

resultMap的使用
【MyBatis】基础全网最全,看这篇就够了_第8张图片

八、全局配置文件【了解】

  • 现在:会

  • 以后:配置文件不见了,放到Spring中

全局配置文件支持的标签
【MyBatis】基础全网最全,看这篇就够了_第9张图片

1、properties


2、settings



    
    

3、typeAliases

内置别名【推荐使用】
【MyBatis】基础全网最全,看这篇就够了_第10张图片
【MyBatis】基础全网最全,看这篇就够了_第11张图片

  • 自定义别名



    
    
    
    

4、plugins

4.1 分页插件概述

  • 可以屏蔽底层数据库的差异,实现同一API实现不同数据库的分页功能

  • Mybatis_PageHelper: Mybatis分页插件

4.2 使用步骤

  • 导入依赖


    com.github.pagehelper
    pagehelper
    5.1.10
  • 全局配置文件


    
    
        
        
        
        
  
  • 使用

    • Mapper映射文件

    
    
    • 测试代码

    /**
     * 分页
     **/
    @Test
    public void Test() throws Exception {
        IUserMapper userMapper = sqlSession.getMapper(IUserMapper.class);
    ​
        //分页设置跟查询之间不要出现其他操作
        //参数一:页码
        //参数二:显示条数
        PageHelper.startPage(3, 5);
        List list = userMapper.findAll();
    ​
        PageInfo pageInfo = new PageInfo<>(list);
        System.out.println("当前页:" + pageInfo.getPageNum());
        System.out.println("当前页集合:" + pageInfo.getList());
        System.out.println("总页数:" + pageInfo.getPages());
        System.out.println("总条数:" + pageInfo.getTotal());
        System.out.println("显示条数:" + pageInfo.getPageSize());  //显示条数
        System.out.println("实际显示条数:" + pageInfo.getSize());  //实际显示条数
    ​
    }

5、environments

  • 环境配置




    
    
        
        
        
        
            
            
            
            
        
    
    
    

6、mappers

  • 用来加载Mapper映射文件



    
    
    
    

九、连表查询

1、表与表之间的关系

  • 数据库层面

    • 一对多:部门对员工、公司对部门

    • 多对多:项目跟程序员、学生跟老师

    • 多对一:学生对班级、员工对部门

    • 一对一:人跟身份证、旅客跟护照

  • mybatis层面

    • 一对多【多对多】

    • 一对一【多对一】

2、部门表跟员工表为例

  • 一对多:查询部门关联查询员工

  • 一对一:查询员工关联查询部门

2.1 准备工作

  • 数据库表

CREATE TABLE `t_department` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `dept_name` VARCHAR(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
​
INSERT  INTO `t_department`(`id`,`dept_name`) VALUES 
(1,'研发部'),
(2,'市场部'),
(3,'财务部'),
(4,'测试部');
​
​
CREATE TABLE `t_employee` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `emp_name` VARCHAR(20) NOT NULL,
  `gender` INT(1) DEFAULT NULL COMMENT '1:男 0:女',
  `birthday` DATE DEFAULT NULL,
  `hire_date` DATETIME DEFAULT NULL,
  `salary` INT(11) DEFAULT NULL,
  `address` VARCHAR(200) DEFAULT NULL,
  `dept_id` INT(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8;
​
​
INSERT  INTO `t_employee`(`id`,`emp_name`,`gender`,`birthday`,`hire_date`,`salary`,`address`,`dept_id`) VALUES 
(1,'宝宝',0,'2000-10-31','2021-10-01 09:00:00',8000,'杭州江干',2),
(2,'李四',1,'1985-11-10','2006-12-12 18:10:10',9000,'李家村',1),
(3,'王五',0,'1991-10-02','2009-12-02 00:00:00',1500,'王家村',2),
(5,'测试更新2',1,'2021-10-04','2021-11-11 00:00:00',15000,'杭州',2),
(7,'大CC',1,'2019-05-12','2021-11-07 00:00:00',6000,'杭州',2),
(8,'DD',1,'2021-11-05','2021-11-05 00:00:00',6000,'杭州',1),
(9,'九妹',0,'2021-11-11','2005-12-06 00:00:00',11111,'远古时期',NULL),
(10,'萧炎',1,'1999-12-12','2021-02-03 00:00:00',5000,'牛田村',1),
(11,'萧媚',1,'1985-11-11','2006-12-12 00:00:00',9000,'李家村',1),
(12,'小医仙',0,'1991-10-02','2009-12-02 00:00:00',1500,'王家村',2),
(13,'林动',1,'2021-11-05','2021-11-05 00:00:00',6000,'杭州',1),
(14,'凌青竹',1,'2021-11-05','2021-11-05 00:00:00',6000,'杭州',1),
(15,'纪宁',1,'2021-11-05','2021-11-05 00:00:00',6000,'杭州',2),
(16,'北冥',1,'2021-11-05','2021-11-05 00:00:00',6000,'杭州',1),
(17,'叶伏天',0,'2021-11-11','2005-12-06 00:00:00',11111,'远古时期',NULL),
(18,'余生',0,'2021-11-11','2005-12-06 00:00:00',11111,'远古时期',2),
(19,'花解语',0,'2021-11-11','2005-12-06 00:00:00',11111,'远古时期',1),
(100,'张三',1,'1999-12-12','2021-02-03 00:00:00',5000,'牛田村',1);
部门表跟员工表
【MyBatis】基础全网最全,看这篇就够了_第12张图片

  • 实体

部门
【MyBatis】基础全网最全,看这篇就够了_第13张图片

员工
【MyBatis】基础全网最全,看这篇就够了_第14张图片

2.2 一对多

  • 根据ID查询部门信息且关联员工信息

2.2.1 修改部门实体

修改部门实体
【MyBatis】基础全网最全,看这篇就够了_第15张图片

2.2.2 Mapper接口和Mapper映射文件

Mapper接口和Mapper映射文件
【MyBatis】基础全网最全,看这篇就够了_第16张图片

  • Mapper接口

public interface IDepartmentMapper {
​
    Department findByIdAndEmps(Integer id);
​
}
  • Mapper映射文件

    
​
        
        
        
            
            
            
            
            
            
            
            
            
        
    
​
    
        
        
    
​
    
    

2.3 一对一

  • 根据ID查询员工信息且关联部门信息

2.3.1 修改员工实体

修改员工实体
【MyBatis】基础全网最全,看这篇就够了_第17张图片

2.3.2 Mapper接口和Mapper映射文件

Mapper接口和Mapper映射文件
【MyBatis】基础全网最全,看这篇就够了_第18张图片

  • Mapper接口

public interface IEmployeeMapper {
​
    Employee findByIdAndDept(Integer id);
​
}
  • Mapper映射文件


​
    
        
        
        
        
        
        
        
        
    
​
    
        
        
            
            
        
    
​
    
    

十、分步查询、延迟加载

1、分步查询

  • 是连表查询的另一种方式

    • 把连表查询的SQL进行拆分出多个单表查询的SQL

2、一对多

  • 查询部门信息关联查询员工信息

2.1 IDeparmentMapper映射文件



    
    
    
    
    



2.2 IEmployeeMapper映射文件



2.3 执行流程

执行流程
【MyBatis】基础全网最全,看这篇就够了_第19张图片

3、延迟加载【面试题】

也叫懒加载,也叫按需加载

当需要使用到关联的数据时,才去执行查询操作

实现原理:Cglib动态代理【基于继承】

延迟加载只会出现在分步查询中。一般延迟加载的数据都是大数据【如集合】

  • 即时加载:先执行完所有的SQL,再打印数据

  • 延迟加载,先执行需要获取数据的SQL,再打印数据。后面如果还需要获取数据,再执行SQL,再打印数据

3.1 具体实现

  • 全局配置文件开启延迟加载的开头

全局配置文件开启延迟加载的开头
【MyBatis】基础全网最全,看这篇就够了_第20张图片

  • DepartmentMapper接口和映射文件

DepartmentMapper接口和映射文件
【MyBatis】基础全网最全,看这篇就够了_第21张图片

  • 测试

即时加载
【MyBatis】基础全网最全,看这篇就够了_第22张图片

延迟加载
【MyBatis】基础全网最全,看这篇就够了_第23张图片

十一、动态SQL

  • if

  • choose (when, otherwise)

  • trim (where, set)

  • foreach

1、if

  • 条件判断

    • 成立,就拼接SQL

2、where

  • 功能相当于数据库的where关键字


3、trim【理解】

  • 可以自定义的拼接或去掉SQL片段前后缀

    
    

4、forEach


5、choose...when...otherwise【了解】


6、sql...include

    
    
    
        SELECT
         
         FROM t_employee
    

    
        id,
        emp_name,
        gender,
        birthday,
        hire_date,
        salary,
        address,
        dept_id
    

7、set

  • 仅用于更新,跟if配套使用



    update t_employee
    
        
            emp_name = #{empName},
        
        
            salary = #{salary},
        
        
            gender = #{gender},
        
    
    where id = #{id}

十二、缓存机制【理解】

  • 作用

    • 提高查询效率

    • 减轻数据库访问压力

1、分类

  • 一级缓存

  • 二级缓存

2、区别

  • 一级缓存

    • 级别:SqlSession

    • 存储介质:内存

    • 存储类型:对象副本

    • 失效情况

  • 二级缓存

    • 级别:NameSpace

    • 存储介质:磁盘

    • 存储类型:散装数据

    • 触发条件

3、Mybatis执行查询流程

Mybatis执行查询流程
【MyBatis】基础全网最全,看这篇就够了_第24张图片

4、一级缓存

  • 一级缓存默认开启,我们无法关闭他

  • 在执行两次相同的查询时,第一次会向数据库发送SQL,并且写入一份到一级缓存中,那么后面的查询操作就直接从缓存中获取数据。不会向数据库发送SQL

  • 一级缓存失效的情况

    • 不是同一个SqlSession

    • 两次相同的查询中间执行增删改

    • 两次相同的查询中间手动清空缓存

    • 两次相同的查询中间手动提交事务

@Test
public void firstLevelCacheTest() throws Exception {
   IEmployeeMapper employeeMapper = sqlSession.getMapper(IEmployeeMapper.class);
   Employee employee1 = employeeMapper.findById(10);
   System.out.println(employee1);


   //1.两个SqlSession
   //sqlSession = factory.openSession();
   //employeeMapper = sqlSession.getMapper(IEmployeeMapper.class);

   //2.执行增删改
   //employeeMapper.deleteById(111111);

   //3.手动清空缓存
   //sqlSession.clearCache();

   //4.手动提交事务
   sqlSession.commit();

   Employee employee2 = employeeMapper.findById(10);
   System.out.println(employee2);

   System.out.println(employee1 == employee2);

}

5、二级缓存

  • 二级缓存默认开启,我们可以通过配置文件对其关闭

  • 二级缓存

    • 必须在SqlSession关闭之后,数据才会被写入到二级缓存中。

    • 存储介质是磁盘,所以写入的字节数据,那么要被写入的对象所在的类必须实现Serializable接口

  • 实现步骤

    • 全局配置文件开启

    • 在要使用二级缓存的namespace中配置一个标签

    • 关闭SqlSession

/**
 * 二级缓存
 **/
@Test
public void secondLevelCacheTest() throws Exception {
    IEmployeeMapper employeeMapper = sqlSession.getMapper(IEmployeeMapper.class);
    Employee employee1 = employeeMapper.findById(10);
    System.out.println(employee1);

    sqlSession.close();
    sqlSession = factory.openSession();
    employeeMapper = sqlSession.getMapper(IEmployeeMapper.class);
    Employee employee2 = employeeMapper.findById(10);
    System.out.println(employee2);

    System.out.println(employee1 == employee2);

}

十三、#{}和${}的区别

  • #{}

    • 在填充参数时,是通过?占位符的方式,能够避免SQL注入

    • 只是获取跟数据库表列相关的值

  • ${}

    • 在填充参数时,使用的是直接进行字符串拼接,会有SQL注入的风险

    • 使用其可以操作非数据库表列的取值

优先选择#{}取值,如果不行,则使用${}

十四、注解开发【会用】

  • 注解开发跟配置文件开发,选择一种

package com.qf.java2107.mapper;

import com.qf.java2107.pojo.Department;
import org.apache.ibatis.annotations.*;

import java.util.List;

/**
 * @author ghy
 * @version 1.0
 * @date 2021-12-27
 **/
public interface IDepartmentMapper {

    @Insert("insert into t_department (dept_name) values (#{deptName})")
    @SelectKey(statement = "select last_insert_id()",
            keyProperty = "id",
            keyColumn = "dept_id",
            before = false,
            resultType = int.class)
    int save(Department department);

    /**
     * @Results 等同于配置文件的resultMap标签
     *          id 等同于配置文件的resultMap标签中id属性
     *
     *  @Result : 映射单个属性,用boolean来区分是否是主键映射
     */
    @Results(
            id = "BaseResultMap",
            value = {
                @Result(id = true, column = "id", property = "id"),
                @Result(id = false, column = "dept_name", property = "deptName")
            }
    )
    @Select("select id, dept_name from t_department")
    List findAll();

    @ResultMap("BaseResultMap")  //引用其他已经定义好的ResultMap
    @Select("select id, dept_name from t_department where id = #{id}")
    Department findById(Integer id);

}
  • 分步查询【IEmployeeAnnoMapper.class】

package com.qf.java2107.mapper;

import com.qf.java2107.pojo.Employee;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;

/**
 * @author ghy
 * @version 1.0
 * @date 2021-12-27
 **/
public interface IEmployeeAnnoMapper {

    /**
     * 根据ID查询员工关联部门
     * @param id
     * @return
     */
    @Results({
            @Result(id = true, column = "id", property = "id"),
            @Result(id = false, column = "emp_name", property = "empName"),
            @Result(id = false, column = "gender", property = "gender"),
            @Result(id = false, column = "birthday", property = "birthday"),
            @Result(id = false, column = "hire_date", property = "hireDate"),
            @Result(id = false, column = "salary", property = "salary"),
            @Result(id = false, column = "address", property = "address"),
            @Result(id = false, column = "dept_id", property = "deptId"),
            @Result(id = false, property = "dept", column = "dept_id",
                    //one 一对一映射
                    one = @One(select = "com.qf.java2107.mapper.IDepartmentMapper.findById",
                            fetchType = FetchType.LAZY))
    })
    @Select("select * from t_employee where id = #{id}")    //dept_id
    Employee findByIdUseStep(Integer id);

}

十五、源码分析【听一遍】

1、查询

  • 查询单个

    • DefaultSqlSession的selectOne方法

      • DefaultSqlSession的selectList方法,得到结果后,进行集合数量判断,如果是1,直接返回。>1,否则就报错。0返回null

        • CachingExecutor的query方法

          • BaseExecutor的query方法

            • BaseExecutor的queryFromDatabase方法

              • BaseExecutor的doQuery方法

                • 通过SimpleExecutor中去调用JDBC的execute()

  • 查询集合

    • DefaultSqlSession的selectList方法,得到结果后,直接返回

      • CachingExecutor的query方法

        • BaseExecutor的query方法

          • BaseExecutor的queryFromDatabase方法

            • BaseExecutor的doQuery方法

              • 通过SimpleExecutor中去调用JDBC的execute()

2、增删改方法

  • 修改

    • DefaultSqlSession的update方法

      • CachingExecutor的update方法:会清空缓存操作

        • BaseExecutor的doUpdate方法

          • 通过SimpleExecutor中去调用JDBC的execute()

  • 增加、删除

    • 先调用DefaultSqlSession的insert方法或者delete方法

      • 接下来就会执行DefaultSqlSession的update方法

十六、面试题

1、Mybatis支持延迟加载吗?

2、#{}和${}的区别

3、Mybatis应用到的设计模式

  • 构建者模式

  • 工厂模式

  • 代理模式

4、Mybatis的Mapper接口是否支持方法重载

  • 不支持

    • mapper接口的方法名就是mapper映射文件的statementId,是用来获取执行SQL的唯一标识

5、Mybatis的Mapper映射文件的标签,你知道哪些?

  • 除之前讲的之外

    • sql:抽取的SQL片段

    • include:引用抽取的SQL片段

    
    
    
        SELECT
         
         FROM t_employee
    

    
        id,
        emp_name,
        gender,
        birthday,
        hire_date,
        salary,
        address,
        dept_id
    

你可能感兴趣的:(数据库,java,sql,架构,mybatis)