文章目录
1 Mybatis 介绍
什么是MyBatis?MyBatis是一款持久层框架,用于简化JDBC开发。提升开发效率、降低资源浪费
- 持久层:指的是数据访问层(dao),用来操作数据库。
1.1 快速入门
1.1.1 SQL数据准备:
-- 用户表
create table user(
id int unsigned primary key auto_increment comment 'ID',
name varchar(100) comment '姓名',
age tinyint unsigned comment '年龄',
gender tinyint unsigned comment '性别, 1:男, 2:女',
phone varchar(11) comment '手机号'
) comment '用户表';
-- 测试数据
insert into user(id, name, age, gender, phone) VALUES (null,'白眉鹰王',55,'1','18800000000');
insert into user(id, name, age, gender, phone) VALUES (null,'金毛狮王',45,'1','18800000001');
insert into user(id, name, age, gender, phone) VALUES (null,'青翼蝠王',38,'1','18800000002');
insert into user(id, name, age, gender, phone) VALUES (null,'紫衫龙王',42,'2','18800000003');
insert into user(id, name, age, gender, phone) VALUES (null,'光明左使',37,'1','18800000004');
insert into user(id, name, age, gender, phone) VALUES (null,'光明右使',48,'1','18800000005');
1.1.2 pojo实体类:
public class User {
private Integer id; //id(主键)
private String name; //姓名
private Short age; //年龄
private Short gender; //性别
private String phone; //手机号
//省略GET, SET方法
}
1.1.3 application.properties配置文件:
# 应用名称
spring.application.name=springboot_mybatis-crud
# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据源名称
spring.datasource.name=defaultDataSource
# 数据库连接地址
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC
# 数据库用户名&密码:
spring.datasource.username=root
spring.datasource.password=123456
#下面这些内容是为了让MyBatis映射
#指定Mybatis的Mapper文件
mybatis.mapper-locations=classpath:mappers/*xml
#指定Mybatis的实体目录
mybatis.type-aliases-package=com.sesameseed.mybatis.entity
# 配置mybatis的sql语句输出到控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
1.1.4 mapper数据库操作类:
import com.itheima.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper//生成该接口的实现类,并放到spring容器中
public interface UserMapper {
//查询所有用户数据
@Select("select id, name, age, gender, phone from user")
public List<User> list();
}
注解 | 解释 |
---|---|
@Mapper | 1、表示是mybatis中的Mapper接口;2、注意 :程序运行时:框架会自动生成接口的实现类对象(代理对象),并交给Spring的IOC容器管理 |
@Select | 代表的就是select查询,用于书写select查询语句 |
1.1.5 单元测试类:
@SpringBootTest
public class MybatisQuickstartApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
public void testList(){
List<User> userList = userMapper.list();
for (User user : userList) {
System.out.println(user);
}
}
}
2 JDBC
2.1 JDBC介绍
JDBC: ( Java DataBase Connectivity ),使用Java语言操作关系型数据库的一套API。
注意:使用这套接口(JDBC)编程,真正执行是驱动jar包中的实现类
2.3 JDBC问题分析
原始JDBC程序,存在以下问题:
- 数据库链接的四要素(驱动、链接、用户名、密码)全部硬编码在java代码中
- 查询结果的解析及封装非常繁琐
- 每次查询数据库都需获取连接,操作完毕释放连接, 资源浪费, 性能降低
2.4 Mybatis与JDBC技术对比
Mybatis中:
- 数据库连接四要素(驱动、链接、用户名、密码),都配置在springboot默认配置文件 application.properties中
- 查询结果解析及封装,由mybatis自动完成映射封装,我们无需关注
- 在mybatis中使用了数据库连接池技术,从而避免频繁创建连接、销毁连接而带来的资源浪费。
- Mybatis提升开发效率、降低资源浪费
2.5 mybatis和jdbc的区别
JDBC(Java database connectivity) | mybatis |
---|---|
① jdbc是java提供用于访问数据库的API,其允许Java程序通过标准SQL语句喻户数据库进行交互,jdbc提供连接数据库、执行SQL语句、处理事务的功能。 | ① mybatis是开源持久层框架,其简化使用jdbc进行数据库访问的流程,通过更方便的方式来执行SQL语句、处理结果集。 |
② 支持对象与数据库表间表映射 | |
③ mybatis可以看做是jdbc基础上一个数据访问层扩展,其提供了很多其他功能,如:缓存、动态SQL | |
④使用mybatis可以将SQL与Java代码进行解耦,通过配置文件将SQL与Java方法进行映射,从而简化数据访问的代码编写过程,并通过更好可读性与维护下性 |
总结:jdbc是Java提供用于访问数据库底层的API,而mybatis则是在jdbc基础上提供更高级、更方便、更易用的数据库访问框架,并提供诸如映射、缓存等新功能。
3 数据库连接池
3.1 数据库连接池介绍
没有使用数据库连接池:
客户端执行SQL语句:要先创建新连接对象,然后执行SQL语句,执行后又需关闭连接对象、释放资源,每次执行SQL都需要创建连接、销毁链接,频繁重复创建销毁的过程是浪费计算机性能。
数据库连接池是个容器,负责分配、管理数据库连接(Connection)
- 程序在启动时,会在数据库连接池(容器)中,创建一定数量的Connection对象
允许应用程序重复使用一个现有数据库连接,而不重新建立
- 客户端在执行SQL时,先从连接池中获取一个Connection对象,然后再执行SQL语句,SQL执行完后,释放Connection时就会把Connection对象归还给连接池(Connection对象可以复用)
- 客户端获取到Connection对象,但是Connection对象并没有去访问数据库(处于空闲),数据库连接池发现Connection对象的空闲时间 > 连接池中预设的最大空闲时间,此时数据库连接池会自动释放掉这个连接对象。
数据库连接池的好处:
1. 资源重用
2. 提升系统响应速度
3. 避免数据库连接遗漏
3.2 数据库连接池产品产品
常见的数据库连接池:C3P0、DBCP、Druid、Hikari (springboot默认),现在使用更多的是:Hikari、Druid (性能更优越)
- Hikari(追光者) [默认的连接池]
Druid(德鲁伊)
- Druid连接池是阿里巴巴开源的数据库连接池项目
- 功能强大,性能优秀,是Java语言最好的数据库连接池之一
Druid参考官方地址:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
3.3 Druid引入项目
3.3.1 在pom.xml文件中引入依赖
<dependency>
<!-- Druid连接池依赖 -->
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
3.3.2 在application.properties中引入数据库连接配置
# 应用名称
spring.application.name=springboot_mybatis-crud
# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据源名称
spring.datasource.name=defaultDataSource
# 数据库连接地址
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC
# 数据库用户名&密码:
spring.datasource.username=root
spring.datasource.password=123456
#下面这些内容是为了让MyBatis映射
#指定Mybatis的Mapper文件
mybatis.mapper-locations=classpath:mappers/*xml
#指定Mybatis的实体目录
mybatis.type-aliases-package=com.sesameseed.mybatis.entity
# 配置mybatis的sql语句输出到控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
4 lombok
4.1 lombok介绍
Lombok是一个实用的Java类库,可以通过简单注解来简化、消除一些必须有但臃肿的Java代码。
注解 | 作用 |
---|---|
@Getter/@Setter | 为所有的属性提供get/set方法 |
@ToString | 会给类自动生成易阅读的 toString 方法 |
@EqualsAndHashCode | 根据类所拥有的非静态字段自动重写 equals 方法和 hashCode 方法 |
@Data | (@Getter + @Setter + @ToString + @EqualsAndHashCode) |
@NoArgsConstructor | 为实体类生成无参的构造器方法 |
@AllArgsConstructor | 为实体类生成除了static修饰的字段之外带有各参数的构造器方法。 |
4.2 lombok使用
4.2.1 在pom.xml文件中引入依赖
<!-- 在springboot的父工程中,已经集成了lombok并指定了版本号,故当前引入依赖时不需要指定version -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
4.2.2 pojo类代码引入
package com.sesameseed.pojo;
import lombok.*;
//@Getter
//@Setter
//@ToString
//@EqualsAndHashCode
@Data //替代上边4个,不包含下方四个
@NoArgsConstructor //无参构造
@AllArgsConstructor //全参构造
public class User {
private Integer id;
private String name;
private Short age;
private Short gender;
private String phone;
}
5 Mybatis基础操作
5.1 准备
实施前的准备工作:
- 准备数据库表
- 创建一个新的springboot工程,选择引入对应的起步依赖(mybatis、mysql驱动、lombok)
- application.properties中引入数据库连接信息
- 创建对应的实体类 Emp(实体类属性采用驼峰命名)
- 准备Mapper接口 EmpMapper
5.1.1 创建一个新的springboot工程,选择引入对应的起步依赖(mybatis、mysql驱动、lombok)
5.1.2 application.properties中引入数据库连接信息
# 应用名称
spring.application.name=tlias-demo
# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据库连接地址
spring.datasource.url=jdbc:mysql://localhost:3306/tlias?serverTimezone=UTC
# 数据库用户名&密码:
spring.datasource.username=root
spring.datasource.password=123456
# 应用服务 WEB 访问端口
server.port=8080
#开启mybatis的日志输出
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#开启数据库表字段 到 实体类属性的驼峰映射
mybatis.configuration.map-underscore-to-camel-case=true
5.1.3 创建对应的实体类Emp(实体类属性采用驼峰命名
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {
private Integer id;
private String username;
private String password;
private String name;
private Short gender;
private String image;
private Short job;
private LocalDate entrydate; //LocalDate类型对应数据表中的date类型
private Integer deptId;
private LocalDateTime createTime;//LocalDateTime类型对应数据表中的datetime类型
private LocalDateTime updateTime;
}
5.1.4 准备Mapper接口:EmpMapper
注解 | 解释 |
---|---|
@Mappe | 当前接口为mybatis中Mapper接口,程序运行时自动创建接口的实现类对象代理对象,并交给Spring的IOC容器管理 |
@Mapper
public interface EmpMapper {
}
最终工程结构目录:
5.2 删除
5.2.1 删除功能实现
页面原型:
5.2.1.1 功能:根据主键删除数据
-- 需求:删除id=17的数据
-- SQL语句
delete from emp where id = 17;
5.2.1.2 Mapper(Dao)数据访问层(CRUD)方法
注解 | 解释 |
---|---|
@DELETE | 该注解标记的方法,表明此方法执行的是删除资源的操作,其返回值可以是void(没有返回值) |
@Mapper
public interface EmpMapper {
//@Delete("delete from emp where id = 17")
//public void delete();
//以上delete操作的SQL语句中的id值写成固定的17,就表示只能删除id=17的用户数据
//SQL语句中的id值不能写成固定数值,需要变为动态的数值
//解决方案:在delete方法中添加一个参数(用户id),将方法中的参数,传给SQL语句
/**
* 根据id删除数据
* @param id 用户id
*/
@Delete("delete from emp where id = #{id}")//使用#{key}方式获取方法中的参数值
public void delete(Integer id);
}
5.2.1.3 测试类方法
注解 | 解释 |
---|---|
@Autowired | 从Spring的IOC容器中,获取到类型是EmpMapper的对象,并注入到声明此注解的类中 |
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired //从Spring的IOC容器中,获取类型是EmpMapper的对象并注入
private EmpMapper empMapper;
@Test
public void testDel(){
//调用删除方法
empMapper.delete(16);
}
}
5.2.2 日志输入
application.properties配置类中开启日志:
#指定mybatis输出日志的位置, 输出控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
控制台日志打印出,并且打印出预编译SQL
5.2.3 预编译SQL
预编译优势:
- 性能更高(编译一次后将编译后SQL缓存起来,再次执行语句时,不会再次编译。(只是输入的参数不同)
- 更安全(
防止SQL注入
,将敏感字进行转义,保障SQL安全性。)
5.2.4 SQL注入
关键代码块
' or '1'='1
SQL编译后:
由于1=1是永远成立,因而可以完成注入,SQL客户端也提示always true
5.2.5 参数占位符
Mybatis中提供的参数占位符有两种:
${...}
、#{...}
#{…}说明
- 执行SQL时,将#{…}替换为?,生成预编译SQL,自动设置参数值
- 使用时机:参数传递,都使用#{…}.优先使用。
${…}说明
- 拼接SQL。
直接将参数拼接
在SQL中,存在SQL注入问题。
5.3 新增
功能:新增员工信息
5.3.1 新增功能实现
SQL语句:
insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values ('songyuanqiao','宋远桥',1,'1.jpg',2,'2012-10-09',2,'2022-10-01 10:00:00','2022-10-01 10:00:00');
5.3.1.1 Mapper(Dao)数据访问层(CRUD)方法
@Mapper
public interface EmpMapper {
//说明:#{...} 里面写的名称是对象的属性名
@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")
public void insert(Emp emp);
}
5.3.1.2 测试类方法
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testInsert(){
//创建员工对象
Emp emp = new Emp();
emp.setUsername("tom");
emp.setName("汤姆");
emp.setImage("1.jpg");
emp.setGender((short)1);
emp.setJob((short)1);
emp.setEntrydate(LocalDate.of(2000,1,1));
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
emp.setDeptId(1);
//调用添加方法
empMapper.insert(emp);
}
}
5.3.1.3 日志输出
5.3.2 主键返回
概念:在数据添加成功后,需要获取插入数据库数据的主键。
5.3.2.1 Mapper(Dao)数据访问层(CRUD)方法
@Mapper
public interface EmpMapper {
//会自动将生成的主键值,赋值给emp对象的id属性
@Options(useGeneratedKeys = true,keyProperty = "id")
@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")
public void insert(Emp emp);
}
5.3.2.2 测试类
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testInsert(){
//创建员工对象
Emp emp = new Emp();
emp.setUsername("jack");
emp.setName("杰克");
emp.setImage("1.jpg");
emp.setGender((short)1);
emp.setJob((short)1);
emp.setEntrydate(LocalDate.of(2000,1,1));
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
emp.setDeptId(1);
//调用添加方法
empMapper.insert(emp);
System.out.println(emp.getDeptId());
}
}
5.4 更新
功能:修改员工信息
5.4.1 更新功能实现
SQL语句:
update emp set username = 'linghushaoxia', name = '令狐少侠', gender = 1 , image = '1.jpg' , job = 2, entrydate = '2012-01-01', dept_id = 2, update_time = '2022-10-01 12:12:12' where id = 18;
5.4.1.1 Mapper(Dao)数据访问层(CRUD)方法
@Mapper
public interface EmpMapper {
/**
* 根据id修改员工信息
* @param emp
*/
@Update("update emp set username=#{username}, name=#{name}, gender=#{gender}, image=#{image}, job=#{job}, entrydate=#{entrydate}, dept_id=#{deptId}, update_time=#{updateTime} where id=#{id}")
public void update(Emp emp);
}
5.4.1.2 测试类方法
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testUpdate(){
//要修改的员工信息
Emp emp = new Emp();
emp.setId(23);
emp.setUsername("songdaxia");
emp.setPassword(null);
emp.setName("老宋");
emp.setImage("2.jpg");
emp.setGender((short)1);
emp.setJob((short)2);
emp.setEntrydate(LocalDate.of(2012,1,1));
emp.setCreateTime(null);
emp.setUpdateTime(LocalDateTime.now());
emp.setDeptId(2);
//调用方法,修改员工数据
empMapper.update(emp);
}
}
5.5 问:为什么不把pojo中的实体类对象,利用IoC思想注入到IoC容器中?
答:IOC容器是单例的 把pojo放到IOC容器中,会发生数据共享,线程安全问题。
单例模式:1、这种模式涉及到一个单一的类;
2、该类负责创建自己的对象,同时确保只有单个对象被创建;
3、这个类提供一种访问其唯一对象的方式,可以直接访问,不需要实例化该类的对象
注意:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
线程安全问题:多个对象操作同一资源,联想此处pojo类想法,pojo实体类要经常被创建,而IoC是单例设计模式的,会发生线程安全问题。
5.6 查询
功能:根据ID查询
5.6.1 查询功能实现
SQL语句:
select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp;
5.6.1.1 Mapper(Dao)数据访问层(CRUD)方法
@Mapper
public interface EmpMapper {
@Select("select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp where id=#{id}")
public Emp getById(Integer id);
}
5.6.1.2 测试类方法
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testGetById(){
Emp emp = empMapper.getById(1);
System.out.println(emp);
}
}
5.6.1.3 执行结果
5.6.2 数据封装
引入:5.6.1.3 执行结果中deptId,createTime,updateTime这几个字段没有值
原因:
- 实体类属性名和数据库表查询返回字段名一致,mybatis会自动封装。
- 如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装。
解决方案:
- 起别名
- 结果映射
- 开启驼峰命名
开启驼峰命名(推荐):如果字段名与属性名符合驼峰命名规则,mybatis会自动通过驼峰命名规则映射
- 驼峰命名规则: abc_xyz => abcXyz
- 表中字段名:abc_xyz
- 类中属性名:abcXyz
# 在application.properties配置类中添加:
mybatis.configuration.map-underscore-to-camel-case=true
注意:
使用驼峰命名前提: 实体类的属性与数据库表中字段名严格遵守驼峰命名。
5.7 条件查询
在员工管理列表页面,需要根据条件查询员工信息,查询条件包括:姓名、性别、入职时间。
5.7.1 条件查询功能实现
SQL语句:
select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp where name like '%张%' and gender = 1 and entrydate between '2010-01-01' and >'2020-01-01 ' order by update_time desc;
5.7.1.1 Mapper(Dao)数据访问层(CRUD)方法:
此方法相比较与
where name like '%${name}%'
解决SQL注入风险
- 使用MySQL提供的字符串拼接函数:
concat('%' , '关键字' , '%')
@Mapper
public interface EmpMapper {
@Select("select * from emp " +
"where name like concat('%',#{name},'%') " +
"and gender = #{gender} " +
"and entrydate between #{begin} and #{end} " +
"order by update_time desc")
public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end);
}
5.7.1.2 执行结果:生成的SQL是预编译的SQL语句(性能高、安全)
5.7.2 参数名说明
在上述所编写条件查询功能中,我们需保证接口中方法形参名和SQL语句中参数占位符名相同。
当方法中形参名和SQL语句中占位符参数名不相同时,报以下错误:
参数名在不同的SpringBoot版本中,处理方案:
- 在springBoot2.x版本(保证参数名一致)
- 在springBoot1.x版本中,单独使用mybatis的@Param注解来指定SQL语句中的参数名
6 Mybatis的XML配置文件
Mybatis的开发有两种方式:
注解方式
:来完成一些简单的增删改查功能XML方式
:实现复杂的SQL功能
6.1 XML配置文件规范
- XML映射文件名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)
- XML的namespace属性和Mapper接口全限定名一致
- XML中sql语句的id与Mapper接口中方法名一致,并保持返回类型一致。
标签 | resultType属性 |
---|---|
就是用于编写select查询语句 | 查询返回的单条记录所封装的类型 |
6.2 XML配置文件实现
6.2.1 编写XML映射文件
xml映射文件中dtd约束,直接从mybatis官网复制
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="">
</mapper>
6.2.2 配置:XML映射文件namespace属性为Mapper接口全限定名
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--此处写代码-->
</mapper>
6.2.3 配置:XML映射文件中sql语句id与Mapper接口中的方法名一致,并保持返回类型一致
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--查询操作-->
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp
where name like concat('%',#{name},'%')
and gender = #{gender}
and entrydate between #{begin} and #{end}
order by update_time desc
</select>
</mapper>
6.2.4 执行结果
6.3 MybatisX的使用
7 Mybatis动态SQL
标签 | 作用 |
---|---|
<if> | 判断条件是否成立。用test属性进行条件判断,如果条件为true,则拼接SQL。 |
<where> | 会在子元素有内容的情况下才插入where子句,自动去除子句的开头的AND或OR |
<set> | 动态的在SQL语句中插入set关键字,并会删掉额外的逗号。(用于update语句中) |
<foreach> | 遍历deleteByIds方法中传递的参数ids集合) |
<sql> | 定义可重用的SQL片段 |
<include> | 通过属性refid,指定包含的SQL片段 |
SQL语句随着用户输入或外部条件变化而变化,称为:动态SQL。
如:在页面原型中,列表上方条件是动态的,可以不传递的,也可以只传递其中1个或2个或全部。
7.1 动态SQL-if
标签 | 作用 |
---|---|
<if> | 判断条件是否成立。用test属性进行条件判断,如果条件为true,则拼接SQL。 |
<where> | 会在子元素有内容的情况下才插入where子句,自动去除子句的开头的AND或OR |
<set> | 动态的在SQL语句中插入set关键字,并会删掉额外的逗号。(用于update语句中) |
<foreach> | 遍历deleteByIds方法中传递的参数ids集合) |
<sql> | 定义可重用的SQL片段 |
<include> | 通过属性refid,指定包含的SQL片段 |
<if>
:用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL。
<if test="条件表达式">
要拼接的sql语句
</if>
7.1.1 动态条件查询功能实现
7.1.1.1 原有的SQL语句:
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp
where name like concat('%',#{name},'%')
and gender = #{gender}
and entrydate between #{begin} and #{end}
order by update_time desc
</select>
7.1.1.2 改装成动态SQL语句
标签 | 功能 |
---|---|
<where> | 会在子元素有内容的情况下才插入where子句,自动去除子句的开头的AND或OR |
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp
<where>
<!-- where直接作为关键字出现此,会出现下图7.1.1.2.error错误 -->
<!-- if做为where标签的子元素 -->
<if test="name != null">
and name like concat('%',#{name},'%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="begin != null and end != null">
and entrydate between #{begin} and #{end}
</if>
</where>
order by update_time desc
</select>
where直接作为关键字出现
测试类方法,打印结果error@Test public void testList(){ //姓名为null List<Emp> list = empMapper.list(null, (short)1, null, >null); for(Emp emp : list){ System.out.println(emp); } }
图7.1.1.2.error
7.1.1.3 测试方法
@Test
public void testList(){
//性别数据为null、开始时间和结束时间也为null
List<Emp> list = empMapper.list("张", null, null, null);
for(Emp emp : list){
System.out.println(emp);
}
}
7.1.1.4 执行的SQL语句
7.1.2 动态更新员工功能实现
动态更新员工信息,如果更新时传递有值,则更新;如果更新时没有传递值,则不更新
7.1.2.1 Mapper(Dao)数据访问层(CRUD)方法
修改Mapper接口
@Mapper
public interface EmpMapper {
//删除@Update注解编写的SQL语句
//update操作的SQL语句编写在Mapper映射文件中
public void update(Emp emp);
}
7.1.2.2 修改Mapper映射的XML文件
标签 | 功能 |
---|---|
<set> | 动态的在SQL语句中插入set关键字,并会删掉额外的逗号。(用于update语句中) |
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--更新操作-->
<update id="update">
update emp
<!-- 使用set标签,代替update语句中的set关键字 -->
<!-- 此处直接使用set关键字,会发生下图7.1.2.2.error错误-->
<set>
<if test="username != null">
username=#{username},
</if>
<if test="name != null">
name=#{name},
</if>
<if test="gender != null">
gender=#{gender},
</if>
<if test="image != null">
image=#{image},
</if>
<if test="job != null">
job=#{job},
</if>
<if test="entrydate != null">
entrydate=#{entrydate},
</if>
<if test="deptId != null">
dept_id=#{deptId},
</if>
<if test="updateTime != null">
update_time=#{updateTime}
</if>
</set>
where id=#{id}
</update>
</mapper>
图7.1.2.2.error:
测试类方法
@Test public void testUpdate2(){ //要修改的员工信息 Emp emp = new Emp(); emp.setId(20); emp.setUsername("Tom222"); //调用方法,修改员工数据 empMapper.update(emp); }
7.1.2.3 测试方法返回结果
@Test
public void testUpdate2(){
//要修改的员工信息
Emp emp = new Emp();
emp.setId(20);
emp.setUsername("Tom111");
emp.setName("汤姆111");
emp.setUpdateTime(LocalDateTime.now());
//调用方法,修改员工数据
empMapper.update(emp);
}
7.2 动态SQL-foreach
案例:员工删除功能(既支持删除单条记录,又支持批量删除)
功能所对应的示例SQL语句:
delete from emp where id in (1,2,3);
7.2.1 动态SQL-foreach功能实现
7.2.1.1 Mapper(Dao)数据访问层(CRUD)方法
@Mapper
public interface EmpMapper {
//批量删除
public void deleteByIds(List<Integer> ids);
}
7.2.1.1 Mapper(Dao)数据访问层对应的XML映射文件
标签 | 作用 |
---|---|
<foreach> | 遍历deleteByIds方法中传递的参数ids集合) |
方法示例:
<foreach collection="集合名称" item="集合遍历出来的元素/项" separator="每一次遍历使用的分隔符"
open="遍历开始前拼接的片段" close="遍历结束后拼接的片段">
</foreach>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--删除操作-->
<delete id="deleteByIds">
delete from emp where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
</mapper>
注意:
#{id}必须与item中id名字保持一致==
执行的SQL结果
7.3 动态SQL-sql&include
标签 | 作用 |
---|---|
<sql> | 定义可重用的SQL片段 |
<include> | 通过属性refid,指定包含的SQL片段 |
问题:xml映射文件中配置的SQL,有时存在很多重复片段,存在很多冗余代码
- 因此对重复代码片段进行抽取,通过
<sql>
标签封装到SQL片段,再通过<include>
标签进行引用。
SQL片段: 抽取重复的代码<sql id="commonSelect"> select id, username, password, name, gender, >image, job, entrydate, dept_id, create_time, >update_time from emp </sql>
通过
<include>
标签在原来抽取的地方进行引用<select id="list" resultType="com.itheima.pojo.Emp"> <include refid="commonSelect"/> <where> <if test="name != null"> name like concat('%',#{name},'%') </if> <if test="gender != null"> and gender = #{gender} </if> <if test="begin != null and end != null"> and entrydate between #{begin} and #{end} </if> </where> order by update_time desc </select>
8 mybatis一对多查询
8.1 一对多查询、嵌套查询概念
一对多查询:指在关系型数据库中,两个表间存在一对多关系(例如
部门表
、员工表
,一个部门可以包含多个用户,多个用户可以属于一个部门)。
嵌套查询:在我们进行一对多查询同时,需要使用多个SQL语句来查询多个表,并通过特定关键字或条件将多个结果集关联起来,例如:
select id,name,sex,hiredate,sys_dept,deptno,dno from dept,emp where emp.deptno = dept.deptno
8.2 < resultMap >标签
< resultMap >标签主要用来定义映射规则、级联的更新以及定义类型转换器等。
<resultMap id = "" type="">
<constructor><!--类型实例化时用来注入结果到构造方法-->
</constructor>
<id></id><!--用来标识哪个是主键-->
<result></result><!--注入到字段或JavaBean属性的普通结果-->
<association property=""></association ><!--用于一对一关联-->
<collection property=""></collection ><!--用于一对多、多对多关联-->
</resultMap>
8.3 为什么使用< resultMap >
- ① 当字段与pojo属性不一致时,使用resultMap自定义映射规则。
- ② 当需要完成复杂映射时,例如(1对1,1对多)时,必须使用resultMap。
8.4 mybatis一对多案例
8.4.1 定义两个表
① 表一:emp(员工表:id、name、sex、depno),表二:dept(部门表:deptno、dname),其:一个部门可以包含多员工、多个员工属于一个部门。
定义两个实体类:
- Emp(id,name,dept,hiredate)
- Dept(deptno、dname、List< Emp >)
8.4.2 Mapper的XML映射书写
<select id = "select" resultMap="empResultMap">
select id,name,sex,hiredate,sys_dept,deptno,dno from dept,emp where emp.deptno = dept.deptno
</select><!--此条SQL由于跟实体类是不能一一对应的,因此并不能成功映射-->
<!--自定义映射规则-->
<resultMap id = "empResultMap" type="com.jiao.entity.Emp">
<id property="id" column="id"></id>
<result property="sex" column="sex"></result>
<result property="name" column="name"></result >
<result property="hiredate" column="hiredate"></result >
<association property="dept" javaType= "com.jiao.entity.Dept">
<id property="depno" column="depno"></id>
<result property="dname" column="dname"></result >
</association>
</resultMap>