文章目录
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] 导航分页的页码
}