MyBatis框架

文章目录

MyBatis

本内容配套代码Gitee仓库链接:https://gitee.com/allureyu/mybatis_hx.git

第一章 MyBatis简介

1.1 MyBatis历史

MyBatis最初是Apache的一个开源项目iBatis。之后迁移到Goole Code,
iBatis3.x正式更名为MyBatis,代码之后迁移到Github。
iBatis是一个基于Java的持久层框架,iBatis提供的持久层框架包括:
SQL、Maps和Data Access Objects(DAO)

1.2 MyBatis特性

1 MyBatis是支持定制化SQL。存储过程以及高级映射的优秀的持久层框架
2 MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
3 MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和java的POJO(普通的java对象 Plain Old Java Objects)映射成数据库中的记录。
4 MyBatis是一个半自动的ORM框架(Object Relation Mapping)

1.3 MyBatis下载

MyBatis的下载地址:https://github.com/mybatis/mybatis-3
在这里插入图片描述
在这里插入图片描述

1.4 Mybatis与其他持久层技术对比

1 与JDBC对比:
SQL夹杂在java代码中,耦合度高,导致硬编码内伤
维护不易且实际开发中SQL有变化,频繁修改的情况多见。代码冗长,开发效率低

2 与Hibernate 和 JPA对比
操作简便,开发效率高,内部自动产生的SQL,不容易做特殊优化
基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难
反射操作太多,导致数据库性能下降

3 MyBatis
轻量级,性能出色 . SQL和java代码分开,功能边界清晰,java代码专注业务,SQL语句专注数据.开发效率稍逊于Hibernate,但是完全能够接受

第二章 搭建MyBatis环境

2.1 开发环境准备

IDEA 2023.1
Maven:3.6.3
MariaDB:10.3.
MyBatis:5.3.7

2.2 创建maven工程

<!--打包方式jar-->
	<packaging>jar</packaging>
<!--引入依赖-->
    <dependencies>
        <!--引入mybatis的核心依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>

        <!--MySql数据库依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>

        <!--导入单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!--lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.28</version>
            <scope>provided</scope>
        </dependency>

        <!--log4j日志依赖-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>

2.3 创建表

创建表emp和dept

CREATE DATABASE /*!32312 IF NOT EXISTS*/`mybase` /*!40100 DEFAULT CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci */;

USE `mybase`;

/*Table structure for table `emp` */

DROP TABLE IF EXISTS `emp`;

CREATE TABLE `emp` (
  `empno` int(11) NOT NULL AUTO_INCREMENT,
  `ename` varchar(20) DEFAULT NULL,
  `job` varchar(20) DEFAULT NULL,
  `mgr` int(11) DEFAULT NULL,
  `hiredate` date DEFAULT NULL,
  `sal` double(7,2) DEFAULT NULL,
  `commit` double(5,2) DEFAULT NULL,
  `deptno` int(11) DEFAULT NULL,
  PRIMARY KEY (`empno`),
  KEY `deptno` (`deptno`),
  CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`deptno`) REFERENCES `dept` (`deptno`)
) ENGINE=InnoDB AUTO_INCREMENT=3031 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;

/*Data for the table `emp` */

insert  into `emp`(`empno`,`ename`,`job`,`mgr`,`hiredate`,`sal`,`commit`,`deptno`) values 
(1002,'白展堂','clerk',1001,'1983-05-09',7000.00,200.00,10),(1003,'李大嘴','clerk',1002,'1980-07-08',8000.00,100.00,10),
(1004,'吕秀才','clerk',1002,'1985-11-12',4000.00,NULL,10),
(1005,'郭芙蓉','clerk',1002,'1985-03-04',4000.00,NULL,10),
(2001,'胡一菲','leader',NULL,'1994-03-04',15000.00,NULL,20),
(2002,'陈美嘉','manger',2001,'1993-05-24',10000.00,300.00,20),(2003,'吕子乔','clerk',2002,'1995-05-19',7300.00,100.00,20),
(2004,'张伟','clerk',2002,'1994-10-12',8000.00,500.00,20),
(2005,'曾小贤','clerk',2002,'1993-05-10',9000.00,700.00,20),
(3001,'刘梅','leader',NULL,'1968-08-08',13000.00,NULL,30),
(3002,'夏冬梅','manger',3001,'1968-09-21',10000.00,600.00,30),(3003,'夏雪','clerk',3002,'1989-09-21',8000.00,300.00,30),
(3004,'张一山','clerk',3002,'1991-06-16',88000.00,200.00,30);
DROP TABLE IF EXISTS `dept`;

CREATE TABLE `dept` (
  `deptno` int(11) NOT NULL,
  `dname` varchar(20) DEFAULT NULL,
  `loc` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`deptno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;

insert  into `dept`(`deptno`,`dname`,`loc`) values 
(10,'餐饮部','上海'),
(20,'销售部','浙江'),
(30,'财务部','北京'),
(40,'技术部','深圳');

表关系:
dept表的主键是emp表的外键
在这里插入图片描述

2.4 创建实体类

public class Dept {
    private int deptno;
    private String dname;
    private String loc;
}
public class Emp {
    //定义属性
    private Integer empno;//编号
    private String ename;//姓名
    private String job;//工作
    private int mgr;//上级编号
    private Date hiredate;//入职日期
    private double sal;//薪资
    private double commit;//奖金
    private int deptno;//部门号
}

2.5 创建数据库配置文件jdbc.properties

习惯命名为:jdbc.properties
配置文件存放的位置:src/main/resources目录下

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=UTF-8
jdbc.username=root
jdbc.password=root

2.6 创建MyBatis的核心配置文件

习惯上命名为mybatis-config.xml,这个文件名仅仅是建议,并非强制要求,
将来整合spring之后,这个配置文件可以省略,所以大家操作时可以直接复制粘贴

核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息
核心配置文件存放的位置是:src/main/resources目录下

<?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 resource="jdbc.properties"></properties>
    <!--配置环境-->
    <environments default="development">
        <environment id="development">
            <!--设置事物的管理器-->
            <transactionManager type="JDBC"/>
            <!--配置数据源-->
            <dataSource type="POOLED">
                <!--设置数据库的驱动-->
                <property name="driver" value="${jdbc.driver}"/>
                <!--设置数据库连接地址-->
                <property name="url" value="${jdbc.url}"/>
                <!--设置数据库的用户名-->
                <property name="username" value="${jdbc.username}"/>
                <!--设置数据库的密码-->
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--引入mapper的映射文件-->
    <mappers>
        <!--指定映射文件的具体路径-->
        <!-- <mapper resource="com/hx/mapper/EmpMapper.xml"></mapper>-->
        <!--指定映射文件坐在包位置,前提是接口的包路径和映射文件的包路径保持一致-->
        <package name="com.hx.mapper"/>
    </mappers>
</configuration>

2.7 创建mapper接口

MyBatis中的mapper接口相当于以前的Dao。区别在于mapper仅仅是接口,不需要提供实现类

public interface EmpMapper {
    //1、查询所有数据
    List<Emp> findAll();
}

2.8 创建Mapper接口的映射文件

1 相关概念:
ORM(Object Relationship mapping)对象关系映射
对象:Java的实体类对象
关系:关系型数据库
映射:二者之间的对应关系
```

2 映射文件的命名规则
表对应的实体类的类名+Mapper.xml
一个映射文件对应一个实体类,对应一张表的操作
MyBatis的映射文件用于编写SQL,访问以及操作表中的数据
MyBatis的映射文件存放的目录是:src/resources/mappers目录下

3 面向接口操作数据保证两个一致
mapper接口的全类名和映射文件的命名空间(namespace)保持一致
mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性一致

4 注意事项
1)mapper接口的名称与mapper.xml映射文件的名称保持一致
2)mapper接口的包类路径(src/main/java下)与mapper.xml映射文件的路径(src/main/resources下)保持一致
3) mapper接口的层级包是以点分隔的
4) mapper.xml文件的层级包是以正斜杠分隔的

5 EmpMapper.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="com.hx.mapper.EmpMapper">
    <!--1、查询所有数据-->
    <select id="findAll" resultType="Emp">
        select *
        from emp;
    </select>
</mapper>

在这里插入图片描述

2.9 junit测试功能

1 SqlSession的创建步骤:

  • 1 读取MyBatis的核心配置文件
  • 2 创建SqlSessionFactoryBuilder对象
  • 3 通过核心配置文件对应的输入流,创建工厂类SqlSessionFactory
  • 4 创建sqlSession对象
  • 5 通过代理模式创建自定义mapper接口的代理实现类对象
  • 6 调用自定义mapper接口的中的方法,
    就会根据自定义mapper的全类名匹配映射文件,
    通过调用的方法名匹配映射文件中SQL标签中的id属性值,
    并执行标签中的SQL语句
  • 7 sqlSession提交事务
  • 8 关闭sqlSession会话

2 名词解释:

  • SqlSession:代表Java程序和数据库之间的会话
  • HttpSession:Java程序和浏览器之间的会话
  • SqlSessionFactory:是生产SqlSession对象的工厂
  • 工厂模式:
    如果创建一个对象,使用的过程都是基本固定的,
    那么我们就可以把创建这个对象的相关代码封装到一个工厂类中,
    以后使用这个工厂类来生产我们想要的对象

3 关于sqlSession提交事务的问题:
MyBatis对于增删改的业务需要操作事务, 如果未设置事务,则测试运行不报错,但数据库数据未发生变化。

sqlSession提交事务的两种方式:

  • 方式一:手动提交。通过方法提交事务
	sqlSession.commit()
  • 方式二:自动提交。在创建SqlSession对象时,参数设置为true,进行事务提交
	SqlSession sqlSession = sessionFactory.openSession(true);

4 编写测试类

    @Test
    public void testMyBatis1() throws IOException {
        //1.加载核心配置文件
        InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
        //2.获取会化工厂的构建对象SqlSessionFactoryBuilder
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder =
                new SqlSessionFactoryBuilder();
        //3.获取会话工厂对象SqlSessionFactory
        SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(stream);
        //4.获取SqlSession--代表java程序和数据库之间的会话
        SqlSession sqlSession = sessionFactory.openSession(true);
        //5.获取mapper接口对象
        EmpMapper mapper = sqlsession.getMapper(EmpMapper.class);
        //6、对象名调用方法
        List<Emp> list = mapper.findAll();
        //7、打印集合
        list.forEach(emp -> System.out.println(emp));
    }

2.10 加入log4j日志功能

1 添加依赖

		<dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

2 加入log4j.xml配置文件。位置:src/main/resources 目录下

日志的级别::从左到右打印的越来越详细
FATAL(致命) > ERROR(错误 > WARN(警告)> INFO(信息)> DEBUG(调试)

<?xml version="1.0" encoding="GB2312" ?>
<!DOCTYPE log4j:configuration SYSTEM "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">

<log4j:configuration debug="true">
    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Encoding" value="UTF-8"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{HH:mm:ss,SS} %-5p (%C{1}:%M) - %m%n"/>
        </layout>
    </appender>
    <logger name="java.sql">
        <level value="debug"/>
    </logger>
    <logger name="org.apache.ibatis">
        <level value="info"/>
    </logger>
    <root>
        <level value="debug"/>
        <appender-ref ref="STDOUT"/>
    </root>
</log4j:configuration>

2.11 MyBatis 的执行流程图

在这里插入图片描述

  • 1、读取MyBatis的配置文件。mybatis-config.xml为MyBatis的全局配置文件,用于配置数据库连接信息。
  • 2、加载映射文件。映射文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,需要在MyBatis配置文件mybatis-config.xml中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
  • 3、构造会话工厂。通过MyBatis的环境配置信息构建会话工厂SqlSessionFactory。
  • 4、创建会话对象。由会话工厂创建SqlSession对象,该对象中包含了执行SQL语句的所有方法。
  • 5、Executor执行器。MyBatis底层定义了一个Executor接口来操作数据库,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。
  • 6、MappedStatement对象。在Executor接口的执行方法中有一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等信息。
  • 7、输入参数映射。输入参数类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输入参数映射过程类似于JDBC对preparedStatement对象设置参数的过程。
  • 8、输出结果映射。输出结果类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输出结果映射过程类似于JDBC对结果集的解析过程。

第三章 基础增删改

3.1 添加功能

	//2、插入一条数据
	int addEmp(@Param("emp") Emp emp);
    <!--2、插入数据-->
    <insert id="addEmp">
        insert into emp
        values (#{emp.empno}, #{emp.ename}, #{emp.job}, #{emp.mgr}, #{emp.hiredate}, #{emp.sal}, #{emp.commit},
                #{emp.deptno});
    </insert>

3.2 优化功能

创建工具类SqlSessionUtils ,封装SqlSession的创建过程

package com.hx.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

/**
 * 封装创建sqlsession对象一个工具类
 * 获取sqlsession:SqlSessionUtils.getSqlsession()
 */
public class SqlSessionUtils {
    //定义一个静态方法,进行封装操作
    public static SqlSession getSqlsession() throws Exception {
        //声明变量在方法里,try的外
        SqlSession sqlSession = null;
        try {
            //1、加载核心配置文件mybatis-config.xml
            InputStream inputStream =
                    Resources.getResourceAsStream("mybatis-config.xml");
            //2、创建工厂构造对象
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder =
                    new SqlSessionFactoryBuilder();
            //3、创建会话工厂
            SqlSessionFactory sqlSessionFactory =
                    sqlSessionFactoryBuilder.build(inputStream);
            //4、生产会话,设置自动提交事务--true
            sqlSession = sqlSessionFactory.openSession(true);
        } catch (Exception e) {
            //2、当逻辑代码发生异常时输出异常
            e.printStackTrace();
        }
        //5、返回sqlsession对象
        return sqlSession;
    }
}

3.3 修改和删除功能

    //3、修改一条数据根据id
    int updateById(Emp emp);

    //4、删除id数据
    int deleteById(int id);
    <!--3、根据id修改数据 updateById-->
    <update id="updateById">
        update emp
        set empno   = #{empno},
            ename=#{ename},
            job=#{job},
            mgr=#{mgr},
            hiredate=#{hiredate},
            sal=#{sal},
            commit  =#{commit},
            deptno=#{deptno}
        where empno = #{empno}
    </update>

    <!--4、根据id删除数据-->
    <delete id="deleteById">
        delete
        from emp
        where empno = #{empno};
    </delete>

第四章 核心配置文件详解

Mybatis核心配置文件中标签的固定顺序:

	properties
	settings
	typeAliases
	objectFactory
	objectWrapperFactory
	reflectorFactory
	plugins
	environments
	databaseIdProvider
	mappers
<?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>

    <!--
        mybatis核心配置问津中,标签的顺序
        "(properties?,settings?,typeAliases?,typeHandlers?,
        objectFactory?,objectWrapperFactory?,reflectorFactory?,
        plugins?,environments?,databaseIdProvider?,mappers?)".
    -->


    <!--引入jdbc.properties文件,此时就可以${属性名}的方式访问属性值-->
    <properties resource="jdbc.properties"></properties>
    
    <settings>
        <!--将表中字段的下划线自动转换为驼峰-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--开启延迟加载
            引入延迟加载的目的:为了处理N+1问题(级联加载的性能问题)
            设置 lazyLoadingEnabled = true,默认情况下mybatis 是按层级延时加载的
            会级联加载所有关联对象的数据
         -->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>

    <!--设置类型别名
       typeAlias 设置某个类型的别名
       属性:
           type="设置需要设置别名的类型"
           alias=“设置某个类型的别名,若不设置该属性,
                    那么该类型拥有默认的别名,即类名,不区分大小写 ”
     -->
    <typeAliases>
        <!--
        <typeAlias type="com.zzy.pojo.User" alias="User"></typeAlias>
        -->

        <!--
            以包为单位,将包下所有的类型设置默认的类型别名,即类的名称,不区分大小写
        -->
        <package name="com.zzy.pojo"/>
    </typeAliases>

    <!--配置连接数据库的环境-->
    <environments default="development">
        <environment id="development">
            <!--
             transactionManager:设置事务管理方式
             属性:
                type=“JDBC/MANAGED”
                JDBC表示当前环境中,执行SQL时,使用的是JDBC中原生的事务管理方式,事务的提交和回滚需要手动处理
                MANAGED:被管理,例如spring
            -->
            <transactionManager type="JDBC"/>
            <!--
               dataSource 用于配置数据源
               属性:
                    type:设置数据源的类型
                    type=“POOLED/UNPOOLED/JNDI”
                        POOLED:表示使用数据库连接池缓存数据库连接
                        UNPOOLED:表示不使用数据库连接池
                        JNDI:表示使用上下文中的数据源
            -->
            <dataSource type="POOLED">
                <!--设置数据库的连接驱动-->
                <property name="driver" value="${jdbc.driver}"/>
                <!--设置数据库的连接地址-->
                <property name="url" value="${jdbc.url}"/>
                <!--设置数据库的连接用户名-->
                <property name="username" value="${jdbc.username}"/>
                <!--设置数据库的连接密码-->
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--引入映射文件-->
    <mappers>
        <!--资源路径在java目录下可以使用点
            在resources目录下不能使用点,需要使用全名称
        -->
        <!--
            <mapper resource="com/zzy/mapper/UserMapper.xml"></mapper>
        -->
        <!--
            以包未单位引入映射文件
             要求:
                1、mapper接口所在的包和映射文件所在的包路径一致,且目录之间使用/分隔,不使用点
                2、mapper接口要和映射文件的名称一致
        -->
        <package name="com.hx.mapper"/>
    </mappers>
</configuration>

4.1 核心配置文件— properties

用于引入外部的后缀名为properties的文件
数据部原配置中,使用${key}来获取其对应的value值

 <properties resource="jdbc.properties"></properties>

4.2 核心配置文件— settings

settings标签中一般配置两个内容,分别是:
1、表中字段的下划线自动转换为驼峰
如将u_id 转换为uId,u_name转为uName
2、开启延迟加载
开启延迟加载的目的:为了处理N+1问题(级联加载的性能问题),设置 lazyLoadingEnabled = true,默认情况下mybatis 是按层级延时加载,开启之后会级联加载所有关联对象的数据

<settings>
	<!--将表中字段的下划线自动转换为驼峰-->
	<setting name="mapUnderscoreToCamelCase" value="true"/>
	<!--开启延迟加载-->
	<setting name="lazyLoadingEnabled" value="true"/>
</settings>

4.3 核心配置文件— typeAliases

typeAlias 设置某个类型的别名,包含两个属性:
type=“设置需要设置别名的类型”
alias=“设置某个类型的别名,若不设置该属性,那么该类型拥有默认的别名,即类名,不区分大小写

方式一:
设置具体的包类全路径,以及设置别名,默认设置类名或类名小写,也可以自定义

<typeAliases>
		<typeAlias type="com.zzy.pojo.User" alias="User"></typeAlias>
</typeAliases>

方式二:
以包为单位,将包下所有的类型设置默认的类型别名,即类的名称,不区分大小写

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

4.4 核心配置文件— environments

  • environments:
    作用:配置多个连接数据库的环境
    属性:default=“development”:用来设置默认使用环境的id,为开发环境

  • environment:
    作用:配置某个具体的环境
    属性:id=“development”:表示连接数据库环境的唯一标识,不能重复

  • transactionManager:
    作用:设置事务管理方式
    属性:type=“JDBC/MANAGED”
    JDBC:表示当前环境中,执行SQL时,使用的是JDBC中原生的事务管理方式,事务的提交和回滚需要手动处理。
    MANAGED:被管理,例如spring

  • dataSource:
    作用:配置数据源
    属性:type:设置数据源的类型
    type=“POOLED/UNPOOLED/JNDI”
    POOLED:表示使用数据库连接池缓存数据库连接
    UNPOOLED:表示不使用数据库连接池
    JNDI:表示使用上下文中的数据源

 <!--配置环境-->
    <environments default="development">
        <environment id="development">
            <!--设置事物的管理器-->
            <transactionManager type="JDBC"/>
            <!--配置数据源-->
            <dataSource type="POOLED">
                <!--设置数据库的驱动-->
                <property name="driver" value="${jdbc.driver}"/>
                <!--设置数据库连接地址-->
                <property name="url" value="${jdbc.url}"/>
                <!--设置数据库的用户名-->
                <property name="username" value="${jdbc.username}"/>
                <!--设置数据库的密码-->
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

4.5 核心配置文件— mappers

方式一:设置映射文件资源全路径全名
要求:在resources目录下不能使用点,使用斜杠分隔:

<!--设置映射文件资源全路径全名-->
<mappers>
    <mapper resource="com/hx/mapper/EmpMapper.xml"></mapper>
</mappers>

方式二:以包未单位引入映射文件
要求:
1、mapper接口所在的包和映射文件所在的包路径一致,目录之间使用/分隔,不使用点
2、mapper接口要和映射文件的名称一致,使用点分隔:

<!--以包未单位引入映射文件-->
<mappers>
    <package name="com.hx.mapper"/>
</mappers>

第五章 $ { } 和 # { }获取参数值

MyBatis获取参数值的两种方式:$ { } 和 # { }

  • ${ }:本质是字符串拼接,需要加引号
  • #{ }:本质是占位符赋值

5.1 获取单个字面量类型的参数

mapper接口方法的参数为单个字面量类型,可以通过 $ { }和 # { } ,
以任意的名称获取参数值, 但是要注意给 $ { } 添加引号

接口方法

//5、#{}、${}获取单个参数,查询emp信息
Emp findEmpByEname1(String ename);
Emp findEmpByEname2(String ename);

sql语句

<!--5、根据一个参数查询emp信息-->
<select id="findEmpByEname1" resultType="Emp">
	select * from emp where ename = #{ename};
</select>
<select id="findEmpByEname2" resultType="Emp">
	select * from emp where ename = "${ename}";
</select>

日志信息:
在这里插入图片描述

5.2 获取多个字面量类型的参数

当 mapper接口方法的参数为多个个字面量类型。此时mybatis会将这些参数存储在一个map集合中,以两种方式进行存储

  • 1、以 arg0,arg1…为键,以参数为值
  • 2、以 param1,param2…为键,以参数为值

程序传递过来的参数1和参数2会维护在map集合中,map的key为:arg0,arg1 或者param1,param2。需要使用map的key(arg0,arg1 或者 param1,param2,或者二者混合使用 arg0,param1)获取参数的值。
如果直接使用ename = #{ename} and job = #{job},则会出现binding异常,报错的原因是:传递进来的参数维护在map中,map的key有四个值,分别是:arg0,arg1,param1,param1,value值是:ename 和job的真实值

接口方法:

//6、根据两个参数查询emp的信息
//使用mybatis默认的map的key
Emp finEmpByEnameAndJob1(String ename,String job);
Emp finEmpByEnameAndJob2(String ename,String job);
Emp finEmpByEnameAndJob3(String ename,String job);

SQL语句:

    <!--6、根据两个参数,查询emp信息-->
    <select id="finEmpByEnameAndJob1" resultType="Emp">
        select *
        from emp
        where ename = #{arg0}
          and job = #{arg1};
    </select>
    <select id="finEmpByEnameAndJob2" resultType="Emp">
        select *
        from emp
        where ename = #{param1}
          and job = #{param2};
    </select>
    <select id="finEmpByEnameAndJob3" resultType="Emp">
        select *
        from emp
        where ename = "${arg0}"
          and job = "${param2}";
    </select>

日志信息:
在这里插入图片描述

5.3 获取Map集合类型的参数

当mapper接口方法的参数有多个时,可以手动将参数放在一个map中存储。此时map中的键值自行手动设置的,不是默认的。因此只需要通过#{}和 以键的方式访问,获得 k e y 对应的 v a l u e 值,但是需要注意 {}以键的方式访问,获得key对应的value值,但是需要注意 以键的方式访问,获得key对应的value值,但是需要注意{}的引号问题

接口方法:

    //7、自定义Map封装参数ename和job,查询emp的信息
    Emp findEmpByMap1(Map<String,String> map);
    Emp findEmpByMap2(Map<String,String> map);

SQL语句:

    <!--7、根据map形式的参数,进行查询操作-->
    <select id="findEmpByMap1" resultType="Emp">
        select *
        from emp
        where ename = #{ename}
          and job = #{job}
    </select>
    <select id="findEmpByMap2" resultType="Emp">
        select *
        from emp
        where ename = "${ename}"
          and job = "${job}"
    </select>

日志信息:
在这里插入图片描述

5.4 注解@Param标识参数

  • 1、@Param注解是为SQL语句中参数赋值而服务的。
  • 2、作用:@Param的作用就是给参数命名。如(@Param(“userId”) int id),想要取出传入的id值,只需要取它的参数名userId就可以了。将参数值传到SQL语句中,通过 #{userId} 进行取值给SQL的参数赋值。
  • 3、使用了@Param注解来声明参数的时候,SQL语句取值使用#{},${}取值都可以,推荐使用#{},且需要使用 注解值.属性名 获取属性
  • 4、不使用@Param注解声明参数的时候,必须使用的是#{}来取参数。使用${}方式取值会报错
  • 5、不使用@Param注解时,参数只能有一个,并且是Javabean。在SQL语句里可以引用JavaBean的属性,而且只能引用JavaBean的属性

接口方法

@Param注解基本类型的参数
@Param注解JavaBean对象

   //8、@Param注解基本类型的参数和Javabean对象
   //使用@Param注解的值为键,参数为值,注解的值的名称和字段名保持一致
   Emp finEmpByEnameAndJob4(@Param("ename") String ename, @Param("job") String job);
   int addEmp2(@Param("emp") Emp emp);

SQL语句:

 <!--8、@Param注解基础类型的参数和Javabean参数-->
    <select id="finEmpByEnameAndJob4" resultType="Emp">
        select *
        from emp
        where ename = #{ename}
          and job = #{job};
    </select>
    <insert id="addEmp2">
        insert into emp
        values (#{emp.empno}, #{emp.ename}, #{emp.job}, #{emp.mgr}, #{emp.hiredate}, #{emp.sal}, #{emp.commit},
                #{emp.deptno});
    </insert>

日志信息:

在这里插入图片描述

第六章 各种查询功能

6.1 查询得到实体类对象

MyBatis的各种查询功能查询返回一个实体类对象;若查询出的数据只有一条,可以通过实体类对象接收,或者集合接收

接口方法:

    //9、根据id查询得到一个实体类对象
    Emp queryEmpById(@Param("empno") int id);

SQL语句:

    <!--9、根据empno查询,得到实体类对象-->
    <select id="queryEmpById" resultType="Emp">
        select *
        from emp
        where empno = #{empno}
    </select>

6.2 查询得到List集合

MyBatis的各种查询功能–查询返回一个list集合。若查询出的数据由多条,可以通过list集合接收。一定不能通过实体类来接收,会报异常TooManyResulrsException

接口方法:

//10、查询所有emp信息,存放在List结合
List<Emp> queryAllEmp();

SQL语句:

<!--10、查询所有封装在List集合中-->
<select id="queryAllEmp" resultType="Emp">
	select *
	from emp;
</select>

6.3 计数查询

MyBatis的各种查询功能–查询单个数据
MyBatis设置了默认的类型别名
int ---- _int/_Integer
Map — map

接口方法:查询总记录数

//11、函数计数操作
int getCount();

SQL语句:

<!--11、函数计数-->
<select id="getCount" resultType="java.lang.Integer">
	select count(*)
	from emp;
</select>

6.4 查询一条数据为Map集合

MyBatis的各种查询功能 --查询一条数据为Map集合,设置返回值类型resultType="Map",以字段为键,以字段所对应的值为值

接口方法:

//12、查询一条emp记录,存放在一个map集合中
Map<Object,Object> getEmpByIdPutMap(@Param("empno") int empno);

SQL语句:

<!--12、根据编号查询emp信息,查询结果存放在map集合
			sql中的第一个empno要和数据库的表字段保持一致
			第二个empno要和接口中java方法的@Param的注解值保持一致
-->
<select id="getEmpByIdPutMap" resultType="Map">
	select *
	from emp
	where empno = #{empno};
</select>

6.5 查询每条数据转化为Map集合,封装在List中

查询每条数据转化为Map集合,并将其存储在List集合中

接口方法:

//13、查询所有,将每条数据转化成Map集合,再存放在List集合中
List<Map<Object,Object>> findAllEmpPutListMap1();

SQL语句:

<!--13、查询所有,将每条数据转化为Map集合,并将其存储在List集合中-->
<select id="findAllEmpPutListMap1" resultType="Map">
	select *
	from emp;
</select>

6.5 查询每条数据转化为Map集合,封装在Map中

查询每条数据转化为Map集合,并将表中的id字段作为外层Map的key,将查询的每条数据的Map形式作为value。最后存储到Map中,相当于Map嵌套Map。

  • @MapKey注解,将每条数据转换成Map集合作为值,以某个字段的值作为键,放在同一个Map集合中
  • 注解中的id表示Map的key,value是每条数据封装到Map中的数据

接口方法:

//14、查询所有,将每条数据转化成Map集合,再存放在Map集合中
@MapKey("empno")
Map<Object,Object> findAllEmpPutMap2();

SQL语句:

<!--14、查询所有,将每条数据转化成Map集合,再存放在Map集合中-->
<select id="findAllEmpPutMap2" resultType="Map">
	select *
	from emp;
</select>

第七章 特殊SQL的执行

7.1 模糊查询—使用${}

根据用户名模糊查询用户信息

  • 方式一:使用${}进行字符串拼接
  • 方式二:使用concat关键字拼接以及${)占位符
  • 方式三:使用#{}占位符
  • 方式四:使用concat关键字拼接以及#{)占位符

接口方法

    //15、特殊SQL执行:模糊查询使用${}和#{}
    List<Emp> findEmpByEnameLike1(@Param("ename") String ename);
    List<Emp> findEmpByEnameLike2(@Param("ename") String ename);
    List<Emp> findEmpByEnameLike3(@Param("ename") String ename);
    List<Emp> findEmpByEnameLike4(@Param("ename") String ename);

SQL语句:

    <!--15、特殊SQL执行:模糊查询使用${}和#{}-->
    <select id="findEmpByEnameLike1" resultType="Emp">
        select * from emp where ename like "%${ename}%"
    </select>
    <select id="findEmpByEnameLike3" resultType="Emp">
        select * from emp where ename like concat('%', '${ename}', '%');
    </select>
    <select id="findEmpByEnameLike2" resultType="Emp">
        select * from emp where ename like "%"#{ename}"%"
    </select>
    <select id="findEmpByEnameLike4" resultType="Emp">
        select * from emp where ename like concat("%", #{ename}, "%")
    </select>

日志信息:
在这里插入图片描述

7.2 批量删除—使用${}

批量删除只能使用${},以字符串形式拼接进行删除,不能使用#{},会自动添加单引号。只会删除第一条,其余不删除

接口方法:

    //16、特殊SQL执行使用${}:批量删除
    Integer deleleEmpByIds(@Param("empnos") String empnos);

SQL语句:

    <!--16、特殊SQL执行使用${}:批量删除-->
    <delete id="deleleEmpByIds">
        delete
        from emp
        where empno in (${empnos});
    </delete>

日志信息:
在这里插入图片描述

7.3 动态设置表名—使用${}

动态设置表名,查询表中所有数据,只能使用${},不能使用#{},使用#{}会给SQL语句中表名上加单引号,会报持久性异常:PersistenceException

接口方法:

    //17、动态设置表名,根据表名称查询所有数据
    List<Emp> getAllEmpByTableName(@Param("emp") String tabelName);

SQL语句:

    <!--17、动态设置表名,根据表名称查询所有数据-->
    <select id="getAllEmpByTableName" resultType="Emp">
        select *
        from ${emp};
    </select>

日志信息:
在这里插入图片描述

7.4 添加功能获取自增的主键

添加数据操作的同时获取自增的主键,有两个属性:

  • useGeneratedKeys=“true”:设置当前标签中的SQL使用了自增的id
  • keyProperty=“id”:将自增的主键的值,赋值给传输到映射文件中参数的某个属性

接口方法:

    //18、特殊SQL执行使用${}:新增数据返回表记录的主键
    int saveEmp(Emp emp);

SQL语句:

    <!--18、新增数据同时获取此记录的主键-->
    <insert id="saveEmp" useGeneratedKeys="true" keyProperty="empno">
        insert into emp
        values (null, #{ename}, #{job}, #{mgr}, #{hiredate}, #{sal}, #{commit}, #{deptno});
    </insert>

第八章 自定义映射resultMap

8.1 字段名和属性不一致映射处理(三种放肆)

解决字段名和属性名不一致

  • 1、方式一:为字段起别名,保持和属性名一致

  • 2、方式二:核心配置文件中配置全局驼峰命名,将_自动映射成驼峰命名
    < setting name=“mapUnderscoreToCamelCase” value=“true” />

  • 3、方式三:取消核心配置文件的驼峰命名,使用resultMap,
    设置自定义之间的关系,使用字段名映射属性名

  • 注意:如果属性名和字段名不一致,不对其进行以上三种方式操作
    别名设置,或开启核心配置文件全局驼峰,或者自定义关系映射
    那么查询结果中,此属性的值为null

1.1 方式一:为字段起别名
情境:数据库字段名:e_name,实体类属性名:eName
解决:为字段起别名,其别名必须和实体类的属性名一致

接口方法:

    //19、字段名和属性名不一致
    //字段名和属性名不一致,根据id查询--设置字段别名匹配实体类属性
    Emp4 findByEmp4no1(Integer empno);

SQL语句:

    <!--19、设置字段别名匹配实体类属性-->
    <select id="findByEmp4no1" resultType="Emp4">
        select eno,
               e_name eName,
               ejob,
               emgr,
               ehiredate,
               esal,
               ecommit,
               edeptno
        from emp4
        where eno = #{eno}
    </select>

1.2 方式二:MyBatis核心配置文件中配置全局驼峰命名,将下划线 _ 自动映射成驼峰命名

核心配置文件开启驼峰映射:

    <!--设置驼峰映射-->
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

接口方法:

    //字段名和属性名不一致,根据id查询--配置文件设置了驼峰映射
    Emp3 findByEmp3no1(Integer empno);

SQL语句:

    <!--19、配置文件设置了驼峰映射-->
    <select id="findByEmp3no1" resultType="Emp3">
        select *
        from emp2
        where e_no = #{empno}
    </select>

1.3 方式三:取消核心配置文件的驼峰命名,使用resultMap,设置自定义之间的关系,使用字段名映射属性名.

接口方法:

    //字段名和属性名不一致,根据id查询--自定义字段名和属性名的映射关系
    Emp2 findByEmp2no(Integer empno);

SQL语句:

    <!--19、字段名和属性名定义映射关系-->
    <resultMap id="map1" type="Emp2">
        <id column="e_no" property="empno"/>
        <result column="e_name" property="ename"/>
        <result column="e_job" property="job"/>
        <result column="e_mgr" property="mgr"/>
        <result column="e_hiredate" property="hiredate"/>
        <result column="e_sal" property="sal"/>
        <result column="e_commit" property="commit"/>
        <result column="e_deptno" property="deptno"/>
    </resultMap>
    <select id="findByEmp2no" resultMap="map1">
        select *
        from emp2
        where e_no = #{empno}
    </select>

属性含义:

属性名属性值含义
resultMap设置自定义映射关系
resultMap中的id设置唯一标识,不能重复
type设置映射关系中的实体类类型
id设置主键的映射关系
result设置其他普通属性的映射关系
property设置映射关系的属性名,必须是type属性所设置的实体类类型中的属性名
column设置数据映射关系的字段名,必须是SQL查询出来的表的字段名

8.2 多对一映射处理(三种方式)

实体类关系:
员工表和部门表的关系维护在多的一方,也就是员工表中的deptno字段。并在Emp实体类中注入Dept对象属性。

public class Emp {
    //定义属性
    private Integer empno;//编号
    private String ename;//姓名
    private String job;//工作
    private int mgr;//上级编号
    private Date hiredate;//入职日期
    private double sal;//薪资
    private double commit;//奖金
    private int deptno;//部门号
    //注入部门实体对象属性
    //多对一查询,多个员工对应一个部门
    private Dept dept;
}
public class Dept {
    private int deptno;//部门编号
    private String dname;//部门名称
    private String loc;//部门位置
}

8.2.1 级联方式处理多对一映射关系

需求1:根据empno查询员工信息以及对应的部门信息

接口方法:

//20、多对一 :根据empno查询员工信息以及对应的部门信息
Emp selectEmpFromDeptByEmpno(@Param("empno") int empno);

SQL语句:

    <!--20、多对一 :根据empno查询员工信息以及对应的部门信息-->
    <resultMap id="empdeptmap" type="Emp">
        <id column="empno" property="empno"/>
        <result column="ename" property="ename"/>
        <result column="job" property="job"/>
        <result column="mgr" property="mgr"/>
        <result column="hiredate" property="hiredate"/>
        <result column="sal" property="sal"/>
        <result column="commit" property="commit"/>
        <result column="deptno" property="deptno"/>
        <result column="deptno" property="dept.deptno"/>
        <result column="dname" property="dept.dname"/>
        <result column="loc" property="dept.loc"/>
    </resultMap>
    <select id="selectEmpFromDeptByEmpno" resultMap="empdeptmap">
        select *
        from emp e,
             dept d
        where e.deptno = d.deptno
          and e.empno = #{empno};
    </select>

需求2:查询所有员工信息以及对应的部门信息

接口方法:

    //21、多对一 :查询所有员工信息以及对应的部门信息
    List<Emp> selectEmpFromDept();

SQL语句:

    <!--21、多对一 :查询所有员工信息以及对应的部门信息-->
    <resultMap id="empdeptmap1" type="Emp">
        <id column="empno" property="empno"/>
        <result column="ename" property="ename"/>
        <result column="job" property="job"/>
        <result column="mgr" property="mgr"/>
        <result column="hiredate" property="hiredate"/>
        <result column="sal" property="sal"/>
        <result column="commit" property="commit"/>
        <result column="deptno" property="deptno"/>
        <result column="deptno" property="dept.deptno"/>
        <result column="dname" property="dept.dname"/>
        <result column="loc" property="dept.loc"/>
    </resultMap>
    <select id="selectEmpFromDept" resultMap="empdeptmap1">
        select *
        from emp e,
             dept d
        where e.deptno = d.deptno;
    </select>

8.2.2 association处理映射关系

需求:根据empno查询员工信息以及对应的部门信息

  • association:联合,即处理多对一的映射关系
  • property:需要处理的映射关系的属性名
  • javaType:该property属性的java类型

property="dept" javaType="Dept"
dept是Emp中的属性名称,Dept是Emp类中dept属性的类型
将查询到的字段deptno与java类型Dept中的deptno属性进行映射,
字段dname与java类型Dept中的dname属性进行映射,
字段loc和java类型Dept中的loc属性进行映射。
映射完毕之后得到java的Dept类型的dept对象
再将此dept对象赋值给java类型Emp中的dept属性

接口方法:

//22、多对一 :根据empno查询员工信息以及对应的部门信息
Emp selectEmpFromDeptByEmpno1(@Param("empno") int empno);

SQL语句:

    <!--22、多对一 :根据empno查询员工信息以及对应的部门信息-->
    <resultMap id="empdeptmap2" type="Emp">
        <id column="empno" property="empno"/>
        <result column="ename" property="ename"/>
        <result column="job" property="job"/>
        <result column="mgr" property="mgr"/>
        <result column="hiredate" property="hiredate"/>
        <result column="sal" property="sal"/>
        <result column="commit" property="commit"/>
        <result column="deptno" property="deptno"/>
        <association property="dept" javaType="Dept">
            <id column="deptno" property="deptno"/>
            <result column="dname" property="dname"/>
            <result column="loc" property="loc"/>
        </association>
    </resultMap>
    <select id="selectEmpFromDeptByEmpno1" resultMap="empdeptmap2">
        select *
        from emp e,
             dept d
        where e.deptno = d.deptno
          and e.empno = #{empno};
    </select>

8.2.3 分步查询处理映射关系

需求:根据empno查询员工信息以及对应的部门信息
思路:先查询员工信息以及对应的部门编号,基于关联关系,再根据部门编号查询对应的部门信息
步骤:
数据库emp表维护对多一的关系,即dept表的deptno主键作为emp表的外键存在

  • 步骤1:现根据empno查询得到员工信息,包括员工对应的depeno的值
  • 步骤2:通过depeno的值,映射到DeptMapper中,执行根据depeno查询部门此案系的操作
  • 步骤3:最后通过association标签进行联合

接口方法:
EmpMapper接口的方法:

    Emp queryEmpByEmpno(@Param("empno")int empno);

DeptMapper接口的方法:

    //多对一查询分步查询第二步
    //根据emp中deptno查询部门信息
    Dept queryDeptByEmpDeptno(@Param("deptno")int deptno);

SQL语句:
EmpMapper.xml中:

    <!--23、多对一 :分步第一步查询,根据empno查询员工信息以及对应的部门信息-->
    <resultMap id="empdeptmap4" type="Emp">
        <id column="empno" property="empno"/>
        <result column="ename" property="ename"/>
        <result column="job" property="job"/>
        <result column="mgr" property="mgr"/>
        <result column="hiredate" property="hiredate"/>
        <result column="sal" property="sal"/>
        <result column="commit" property="commit"/>
        <result column="deptno" property="deptno"/>
        <association property="dept"
                     select="com.hx.mapper.DeptMapper.queryDeptByEmpDeptno"
                     column="deptno"
                     fetchType="lazy">
        </association>
    </resultMap>
    <select id="queryEmpByEmpno" resultMap="empdeptmap4">
        select *
        from emp
        where empno = #{empno};
    </select>

DeptMapper.xml中:

    <!--多对一的分步第二步查询:根据emp中deptno查询部门信息-->
    <select id="queryDeptByEmpDeptno" resultType="Dept">
        select *
        from dept
        where deptno = #{deptno}
    </select>
属性含义
property=“dept”dept表示Emp中的属性
select=“com.hx.mapper.DeptMapper.queryDeptByEmpDeptno”设置分步查询的SQL的唯一标识。即:(namespace.sqlId,或者mapper接口的全类名.方法名)
column=“deptno”设置分步查询的条件,即deptno指的是员工表和部门的关系字段。即通过查询得到的员工信息Emp中的deptno的值,去dept表中查询对应部门信息。再将部门的信息赋值给属性dept
fetchType=“lazy/eager”当开启全局延迟加载之后,可通过此属性设置延迟加载的效果,或者在核心配置文件中设置< setting name=“lazyLoadingEnabled” value=“true” />。lazy 表示延迟加载 eager表示立即加载

延迟加载、紧急加载

FetchType.LAZY:懒加载,加载一个实体时,定义懒加载的属性不会马上从数据库中加载。
FetchType.EAGER:急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载

核心配置文件延迟加载的设置

    <settings>
        <!--开启延时加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>

8.3 resultType 一对多映射处理

实体类关系:
员工表和部门表的关系维护在多的一方,也就是员工表中的deptno字段。并在Dept实体类中注入List类型的list属性。

public class Dept {
    private int deptno;
    private String dname;
    private String loc;
    //一对多查询:一个部门有多个员工
    private List<Emp5> list;
}
public class Emp {
    //定义属性
    private int empno;//编号
    private String ename;//姓名
    private String job;//工作
    private int mgr;//上级编号
    private Date hiredate;//入职日期
    private double sal;//薪资
    private double commit;//奖金
    private int deptno;//部门号
}

8.3.1 collection方式一对多映射

需求:根据部门id查询部门信息以及部门所对应的员工信息

接口方法:

    //24、一对多:根据部门id查询部门信息以及部门所对应的员工信息:collection
    Dept queryDeptAndEmpByDeptno(@Param("deptno") int deptno);

SQL语句:

    <!--24、一对多 方式一:collection-->
    <resultMap id="deptempmap1" type="Dept">
        <id property="deptno" column="deptno"/>
        <result property="dname" column="dname"/>
        <result property="loc" column="loc"/>
        <collection property="list" ofType="Emp5">
            <id property="empno" column="empno"/>
            <result property="ename" column="ename"/>
            <result property="job" column="job"/>
            <result property="mgr" column="mgr"/>
            <result property="hiredate" column="hiredate"/>
            <result property="sal" column="sal"/>
            <result property="commit" column="commit"/>
            <result property="deptno" column="deptno"/>
        </collection>
    </resultMap>
    <select id="queryDeptAndEmpByDeptno" resultMap="deptempmap1">
        select * from emp e right join dept d on d.deptno = e.deptno where d.deptno = #{deptno}
    </select>

collection的属性

属性含义
collection用来处理一对多的关系
property=“list”表示Dept实体类中的list属性
ofType表示该属性所对应的集合中存储的数据的类型

8.3.2 分步查询处理映射关系

需求:根据部门id查询部门信息以及部门所对应的员工信息

接口方法:

DeptMapper方法:

    //25、一对多:分步查询第一步,根据id查询部门信息
    Dept queryDeptByDeptno(@Param("deptno") int deptno);

EmpMapper方法:

    //25、一对多查询:分步查询第二步,根据部门信息中部门id查询对应的emp信息
    Emp5 queryEmpByDeptno(@Param("deptno") int deptno);

SQL语句:
DeptMapper.xml

    <!--25、一对多查询,分步查询第一步-->
    <resultMap id="deptempmap2" type="Dept">
        <id property="deptno" column="deptno"/>
        <result property="dname" column="dname"/>
        <result property="loc" column="loc"/>
        <collection property="list"
                    select="com.hx.mapper.EmpMapper.queryEmpByDeptno"
                    column="deptno"
                    fetchType="lazy">
        </collection>
    </resultMap>
    <select id="queryDeptByDeptno" resultMap="deptempmap2">
        select * from dept where deptno = #{deptno};
    </select>

EmpMapper.xml

    <!--25、一对多查询,分步查询第二步-->
    <select id="queryEmpByDeptno" resultType="Emp">
        select *
        from emp
        where deptno = #{deptno};
    </select>

第九章 动态SQL

动态SQL是MyBatis的强大特性之一 基于功能强大的OGNL表达式。动态SQL主要是来解决查询条件不确定的情况,在程序运行期间,根据提交的条件动态的完成查询
常用的标签:

  • < if > : 进行条件的判断
  • < where >:在判断后的SQL语句前面添加WHERE关键字,并处理SQL语句开始位置的AND 或者OR的问题
  • < trim >:可以在SQL语句前后进行添加指定字符 或者去掉指定字符.
  • < set >: 主要用于修改操作时出现的逗号问题
  • < choose > < when > < otherwise >:类似于java中的switch语句.在所有的条件中选择其一
  • < foreach >:迭代操作
  • < sql >:提取sql片段

9.1 if 标签

if 标签含义:进行条件的判断

接口方法:

//26、动态SQL之if
List<Emp> queryEmpByMoreConditions(@Param("empno") Integer empno,@Param("ename") String ename,@Param("job") String job);

SQL语句:

    <select id="queryEmpByMoreConditions" resultType="Emp">
        select * from emp where 1=1
        <if test="empno != null and empno != ''">
            and empno = #{empno}
        </if>
        <if test="ename != null and ename != ''">
            and ename = #{ename}
        </if>
        <if test="job !=null and job !=''">
            and job = #{job}
        </if>
    </select>

日志信息:
在这里插入图片描述

注意事项:
当if标签中的条件都不满足时,SQL执行的过程是:select * from emp where 1=1。
当where后面没有1=1恒成立的条件时,并且if标签中的条件都不满足时,即where后面的查询条件都不成立,此时,SQL的执行过程是:select * from emp where,就会报异常java.sql.SQLSyntaxErrorException

9.2 where 标签

where标签含义:
1、当where标签中有内容时,且内容满足条件时,会自动生成关键字where,并且将内部多余的and或or去掉
2、当where标签中有内容时,且内容不满足条件时,不会生成where关键字,此时where标签没有任何效果
3、where标签不能将内容后的and去掉,只能将内容前面的and去掉

接口方法:

    //27、动态SQL之where
    List<Emp> queryEmpByMoreConditions2(@Param("emp") Emp emp);

SQL语句:

    <!--27、动态SQL之where标签-->
    <select id="queryEmpByMoreConditions2" resultType="Emp">
        select * from emp
        <where>
            <if test="emp.empno != null and emp.empno != ''">
                empno = #{emp.empno}
            </if>
            <if test="emp.ename != null and emp.ename != ''">
                and ename = #{emp.ename}
            </if>
            <if test="emp.job !=null and emp.job !=''">
                and job = #{emp.job}
            </if>
        </where>
    </select>

日志信息:

条件满足
在这里插入图片描述
条件不满足在这里插入图片描述

9.3 trim 标签

trim标签含义:可以在SQL语句前后进行添加指定字符 或者去掉指定字符.

属性含义
prefix在trim标签的前面添加指定的内容
suffix在trim标签的后面添加指定的内容
prefixOverrides在trim标签中的内容前面去掉指定的内容
suffixOverrides在trim标签中的内容后面去掉指定的内容

注意:如果标签中的内容无效,那么 trim标签也没有任何效果,解析后的SQL语句是:select * from t_emp1

三种用法:

  • 1、 SQL中的and或or在字段的前面
    prefixOverrides=“and|or”,去除字段前面的and或or。
    当条件都不满足,会去掉字段全面的所有and或or
  • 2、 SQL中的and或or在字段的后面
    suffixOverrides=“and|or”,去除字段后面的and或or
    第一个条件满足的情况下,其他条件不满足,去除字段后面的and 或者 or
  • 3、 动态在SQL字段后面添加and或or
    suffix=“and”,在条件后面添加and或者or
    中间某一个条件满足的情况下,其他条件不满足,满足条件的字段后面会有and,需要借助1=1。

接口方法:

    //28、动态SQL之where
    List<Emp> queryEmpByMoreConditions3(@Param("emp") Emp emp);
    List<Emp> queryEmpByMoreConditions4(@Param("emp") Emp emp);
    List<Emp> queryEmpByMoreConditions5(@Param("emp") Emp emp);

SQL语句:

	<!--
    1、SQL中的and或or在字段的前面
    	prefixOverrides="and|or",去除字段前面的and或or
    	当条件都不满足,会去掉字段全面的所有and或or
    -->
    <select id="queryEmpByMoreConditions3" resultType="Emp">
        select * from emp
        <trim prefix="where" prefixOverrides="and|or">
            <if test="emp.empno != null and emp.empno != ''">
                empno = #{emp.empno}
            </if>
            <if test="emp.ename != null and emp.ename != ''">
                and ename = #{emp.ename}
            </if>
            <if test="emp.job !=null and emp.job !=''">
                and job = #{emp.job}
            </if>
        </trim>
    </select>
    <!--
    2、SQL中的and或or在字段的后面
        suffixOverrides="and|or",去除字段后面的and或or
        第一个条件满足的情况下,其他条件不满足,去除字段后面的and 或者 or
    -->
    <select id="queryEmpByMoreConditions4" resultType="Emp">
        select * from emp
        <trim prefix="where" suffixOverrides="and|or">
            <if test="emp.empno != null and emp.empno != ''">
                empno = #{emp.empno} and
            </if>
            <if test="emp.ename != null and emp.ename != ''">
                ename = #{emp.ename} and
            </if>
            <if test="emp.job !=null and emp.job !=''">
                job = #{emp.job}
            </if>
        </trim>
    </select>
	<!--
    3、动态在SQL字段后面添加and或or
        suffix="and",在条件后面添加and或者or
        中间某一个条件满足的情况下,其他条件不满足,满足条件的字段后面会有and,需要借助1=1
    -->
    <select id="queryEmpByMoreConditions5" resultType="Emp">
        select * from emp
        <trim prefix="where" suffix="and">
            <if test="emp.empno != null and emp.empno != ''">
                empno = #{emp.empno}
            </if>
            <if test="emp.ename != null and emp.ename != ''">
                ename = #{emp.ename}
            </if>
            <if test="emp.job !=null and emp.job !=''">
                job = #{emp.job}
            </if>
        </trim>
        1=1;
    </select>

日志信息:
在这里插入图片描述在这里插入图片描述在这里插入图片描述

9.4 choose when otherwise 标签

标签含义:类似于java中的switch语句.在所有的条件中选择其一,情况不满足时,就执行默认语句

属性含义
choose选择其中一个条件去判断
when相当于cese,只会匹配一个条件,当匹配某个条件成功之后,其余条件将不会判断执行
otherwise相当于是default

接口方法:

    //29、动态SQL之choose when otherwise
    List<Emp> queryEmpByMoreConditions6(@Param("emp") Emp emp);

SQL语句:

<!--29、动态SQL之choose when otherwise -->
    <select id="queryEmpByMoreConditions6" resultType="Emp">
        select * from emp
        <where>
            <choose>
                <when test="emp.empno != null and emp.empno != ''">
                    empno = #{emp.empno}
                </when>
                <when test="emp.ename != null and emp.ename != ''">
                    ename = #{emp.ename}
                </when>
                <when test="emp.job !=null and emp.job !=''">
                    job = #{emp.job}
                </when>
                <otherwise>
                    empno = 1092
                </otherwise>
            </choose>
        </where>
    </select>

9.5 foreach 标签— 数组实现批量删除(in,or)

标签含义:迭代操作

属性含义
collection=“ids”集合数组的名称
item=“eid”数组中的元素
separator=", / or "元素之间以逗号隔开,或者使用or相连
open=“(”以左括号开始
close=“)”以右括号结束

接口方法:

//30、动态SQL之foreach 批量删除
int deleteMoreEmpByIds(@Param("empnos")Integer[] empnos);
int deleteMoreEmpByIds2(@Param("empnos")Integer[] empnos);

SQL语句:

    <!--30、动态SQL之foreach 批量删除 -->
    <!--in 情况:SQL执行过程:delete from emp where empno in ( ? , ? , ? , ? )
    -->
    <delete id="deleteMoreEmpByIds">
        delete
        from emp
        where empno in
        <foreach collection="empnos" item="empno" separator="," open="(" close=")">
            #{empno}
        </foreach>
    </delete>
    <!--
        or情况:SQL的执行流程: delete from emp where empno = ? or empno = ? or empno = ? or empno = ?
    -->
    <delete id="deleteMoreEmpByIds2">
        delete
        from emp
        where
        <foreach collection="empnos" item="empno" separator="or">
            empno = #{empno}
        </foreach>
    </delete>

日志信息:
in情况:
SQL执行过程:delete from emp where empno in ( ? , ? , ? , ? )
or情况:
delete from emp where empno = ? or empno = ? or empno = ? or empno = ?

9.6 foreach 标签—集合实现批量添加

foreach 通过集合实现批量添加

接口方法:

    //31、动态SQL之foreach 批量新增-->
    int insertMoreEmp(@Param("empList") List<Emp2> empList);

SQL语句:

    <!--31、动态SQL之foreach 批量新增-->
    <insert id="insertMoreEmp">
        insert into emp
        values
        <foreach collection="empList" item="emp2" separator=",">
            (null ,#{emp2.ename},#{emp2.job},#{emp2.mgr},#{emp2.hiredate},#{emp2.sal},#{emp2.commit},#{emp2.deptno})
        </foreach>
    </insert>

属性:

属性含义
collection设置需要循环的数组或者集合
item表示数组或者集合中的每一个数据
separator循环体之间的分隔符

9.7 sql片段标签

标签含义:用于提取公共部分,引入使用

设置SQL片段,提起公共部分代码:
		<sql id=“common”>公共代码</sql>
引用SQL片段,在SQL中引入公共代码:
    	<include refid="common"></include>

10 缓存

10.1 一级缓存

1、一级缓存介绍:
一级缓存是SqlSession级别的,一级缓存是默认开启的,
通过一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会重新访问数据库

2、一级缓存生效的情况:

  • 1 同个sqlSession下的,同一个mapper两次相同查询。SQL知执行一次,第一次查询数据库。第二次查询缓存
  • 2 同个sqlSession下的,不同mapper的相同查询。SQL知执行一次,第一次查询数据库。第二次查询缓存

3、一级缓存失效的四种情况:

  • 1 不同的SqlSession对应不同的缓存
  • 2 同一个SqlSession查询条件不同
  • 3 同一个SqlSession两次查询期间执行了任意一次增删改查的操作
  • 4 同一个SqlSession两次查询期间手动清空了缓存

10.2 二级缓存

1、二级缓存介绍:
二级缓存是SqlSessionFactory级别的,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存,此后若再次执行相同的查询操作,结果会从缓存中获取

2、二级缓存开启的条件

  • 1 在核心配置文件中,设置全局配置属性,cacheEnable=“true”,
    默认为true,不需要设置
  • 2 在映射文件中设置标签
  • 3 二级缓存必须在SqlSession关闭或者提交之后有效
  • 4 查询的数据所转换的实体类类型必须实现序列化接口

mapper映射文件中配置在这里插入图片描述

MyBatis核心配置文件中配置在这里插入图片描述

3、二级缓存生效的情况:
不同sqlSession下的mapper分别查询,执行相同方法,SQL只执行一次,第一次查询数据库,第二次查询二级缓存
4、二级缓存失效的情况
两次查询期间执行了任意的增删改,会使得一级缓存和二级缓同时失效




2 测试2:二级缓存失效

```xml
二级缓存生效
         *   不同sqlSession下的mapper分别查询
         *      第一次查询数据库
         *      随机执行新增操作,此时缓存会被清理
         *      第二次查询数据库
    @Test
    public void secondCache2(){
        try {
            InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder =
                    new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(stream);

            SqlSession sqlSession1 = sqlSessionFactory.openSession();
            SqlSession sqlSession2 = sqlSessionFactory.openSession();
            SqlSession sqlSession3 = sqlSessionFactory.openSession();

            CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
            Emp1 emp1 = mapper1.getEmpByEid(5);
            System.out.println(emp1);
            sqlSession2.commit();
            sqlSession1.close();

            CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
            int i = mapper2.insertEmp(new Emp1(null, "DDD", 22, "男", "111@qq.com", null));
            System.out.println(i);
            sqlSession2.commit();
            sqlSession1.close();

            CacheMapper mapper3 = sqlSession3.getMapper(CacheMapper.class);
            Emp1 emp3 = mapper3.getEmpByEid(5);
            System.out.println(emp3);
            sqlSession2.commit();
            sqlSession2.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

在这里插入图片描述

10.3 缓存查询的顺序

1 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,
可以拿来直接使用
2 如果二级缓存中没有命中,则查询一级缓存
3 如果一级缓存中也没有命中,则查询数据库
4 SqlSession关闭之前,从数据库查询的数据默认保存在一级缓存
5 SqlSession关闭之后,一级缓存的数据会写入二级缓存

11 分页插件

11.1 分页插件的使用步骤

1、添加分页插件依赖

<!--mybatis的分页插件依赖-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.3.0</version>
        </dependency>

2、MyBatis的核心配置文件中设置分页插件

 <!--配置分页插件-->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>

11.2 分页插件的使用

1、分页知识点:

limit (index,pageSize)
		index:当前页的起始索引
		pageSize:每页显示的条数
 		pageNumber:当前页的页码
	  公式:
		index = (pageNumber-1)*pageSize
		
表记录:是以下标为0开始的,即inde-0.表示第1条表记录
页码:是从1开始的

2、案例1:获取指定页码中展示指定记录数的数据

方法:
需要在查询功能之前开启分页
   PageHelper.startPage(1,4);
    参数一: pageNumber 当前页的页码
    参数二: pageSize  表示当前页展示的条数
代码:
    @Test
    public void testPage(){
        try {
            //1 加载mybatis的核心配置文件
            InputStream stream = 
                    Resources.getResourceAsStream("mybatis-config.xml");
            //2 创建会话工厂构造器
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder =
                    new SqlSessionFactoryBuilder();
            //3 工厂构造器加载输入流创建会话工厂
            SqlSessionFactory sessionFactory = 
                    sqlSessionFactoryBuilder.build(stream);
            //4 获取session对象,true表示开启事务提交
            SqlSession sqlSession = sessionFactory.openSession(true);
            //5 获取接口的实现类对象
            Emp1Mapper mapper = sqlSession.getMapper(Emp1Mapper.class);
            /*
                PageHelper.startPage(1, 4);
                    参数一:表示当前页的页码
                    参数二:表示当前页展示的条数
             */
            //6 开启分页查询操作
            PageHelper.startPage(4, 4);
            //7 通过条件进行查询
            List<Emp1> list = mapper.selectByExample(null);
            //8 遍历打印
            list.forEach(emp1 -> System.out.println(emp1));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

在这里插入图片描述

3、案例2:获取分页详情信息

方法:
查看分页的详细信息
      PageInfo<Emp1> pageInfo = new PageInfo<>(list, 5);
                        参数一:每页展示的list集合数据
                        参数二:导航目录的显示的页码个数
@Test
    public void testPag2(){
        try {
            //1 加载核心配置文件
            InputStream stream =
                    Resources.getResourceAsStream("mybatis-config.xml");
            //2 创建会话工厂构造器
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder =
                    new SqlSessionFactoryBuilder();
            //3 工厂构造器加载输入流创建会话工厂
            SqlSessionFactory sessionFactory =
                    sqlSessionFactoryBuilder.build(stream);
            //4 获取session对象,true表示开启事务提交
            SqlSession sqlSession = sessionFactory.openSession(true);
            //5 获取接口的实现类对象
            Emp1Mapper mapper = sqlSession.getMapper(Emp1Mapper.class);
            //6 条件查询所有
            List<Emp1> list = mapper.selectByExample(null);
            /*
                  查看分页的详细信息
                    PageInfo<Emp1> pageInfo = new PageInfo<>(list, 5);
                        参数一:每页展示的list集合数据
                        参数二:导航目录的显示的页码个数

             */
            // 7 获取分页信息并打印
            PageInfo<Emp1> pageInfo = new PageInfo<>(list, 5);
            System.out.println(pageInfo);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
分页信息详情:
PageInfo{
      pageNum=4,      当前页的页码
      pageSize=4,     每页显示的条数
      size=4,         当前页中的真实条数
      startRow=13,    开始的行数
      endRow=16,      结束的行数
      total=34,       总记录数
      pages=9,        总页数
      prePage=3,          上一页的页码
      nextPage=5,         下一页的页码
      isFirstPage=false,  是否是首页
      isLastPage=false,   是都是尾页
      hasPreviousPage=true,   是否有前一页
      hasNextPage=true,       是否有下一页
      navigatePages=5,        导航分页的页码数
      navigateFirstPage=2,    导航分页的首页字数
      navigateLastPage=6,     导航分页的尾页数
      navigatepageNums=[2, 3, 4, 5, 6]    导航分页的页码
  }
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值