MyBatis3.5.11-从入门到高阶
ztj100 2025-01-07 17:23 11 浏览 0 评论
一.课程介绍
- MyBatis概述
- MyBatis基础应用
- MyBatis高级
- MyBatis进阶
二.MyBatis概述
1、为什么需要MyBatis
- 在我们程序中,运行时期产生的数据都是存放在内存中的,那么在内存中的数据就不是持久化的数据,因为他有一个特性那就是断电既失,那么我们如何保证我们的数据不丢失呢?答案就是存储在数据库中,将数据进行持久化,那么此时我们就需要思考一个问题,在Java程序中,如何将数据存储到数据库?
- 在Java中操作数据库的技术有很多,其中MyBatis就是持久层框架的佼佼者,也是本次我们要学习的框架
2、何为MyBatis
- MyBatis是一个半ORM的数据库持久化框架
- 框架:框架指的就是一些类和接口的集合,通过这些类和接口协调可以完成一系列的程序实现,框架又叫做开发中的半成品,框架不能提供整个应用程序的所有东西,但是框架有技术实现以及辅助业务逻辑的作用
- 持久化:就是将对象数据,保存到数据库当中,从而达到数据断电也存在的目的,叫做持久化
- ORM:对象关系映射(Object Relational Mapping,简称ORM):是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术
- 半ORM(半映射):不是通过完整的映射方式,需要自己写sql语句
- 全ORM框架(全映射):JPA、Hibernate
- Java数据库类表属性字段/列对象记录/行
3、MyBatis起源
- MyBatis的前身是Apache的一个开源项目一一iBatis,2010 iBatis 项目由 Apache 基金会迁移到了 Google Code ,并正式更名为 MyBatis,2013 11 月, MyBatis 迁移到 Github
- 因为MyBatis的前身是iBatis,所以在后续的学习当中,我们可以发现引入的依赖包是iBatis的
4、持久层技术对比
1.JDBC
- SQL语句与代码写在一起,耦合度高
- 不易维护,在实际开发中如果要修改SQL语句还需要修改代码
- 代码冗长,开发效率低
- 代码量多,重复代码过多
- 开发麻烦,需要手动设置参数,如果有查询结果还需要手动转换结果
- 性能高,Java连接数据库的唯一技术
2.Hibernate&JPA
- 全映射ORM框架,大量字段时映射部分字段比较困难
- 自动生成的SQL语句不易优化
- 内部反射操作多,性能较低
- 开发简单,效率高
3.MyBatis
- SQL语句与代码分开,职责分离
- 轻量级框架,易上手
- 开发效率虽然不如Hibernate,也可以接受
5、MyBatis下载
- MyBatis下载地址:github.com/mybatis/myb…
三.MyBatis基础应用
1、MyBatis入门准备
1.开发环境说明
- 开发工具:IDEA2021.3.3
- MySql版本:8.0
- Maven版本:3.8.6
- MyBatis版本:3.5.11
2.学习来源
- 学习MyBatis推荐参考MyBatis官网进行学习
- 官方网站: mybatis.org/mybatis-3/
3.MyBatis使用步骤
- 项目工程准备
- 导入依赖
- 编写核心配置文件
- 编写mapper接口
- 编写SQL映射文件
- 测试,调用方法,执行SQL
2、MyBatis入门实现
1.创建Maven工程
2.导入依赖
<dependencies>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<!--Junit依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
</dependencies>
复制代码
3.编写核心配置文件
- 基本上框架都有自己的核心配置文件,MyBatis也不例外,配置文件名称推荐为mybatis-config.xml,业内基本上都是如此命名,但是不是强制要求,也可以叫其他名称
- MyBatis核心配置文件中的配置可以分为两类数据库连接配置MyBatis全局配置
- 配置文件没有必要手写,可以从官网复制过来修改即可
<?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:核心配置根标签-->
<configuration>
<!--连接数据库环境配置根标签,可以配置多套环境,以default参数作为选择哪个环境标识-->
<environments default="development">
<!--environment:连接数据库环境配置标签,可以配置数据库连接相关参数
id是此环境的唯一标识,default属性选择哪个id那么哪个环境就生效
-->
<environment id="development">
<!--transactionManager:事务管理器,默认就使用JDBC事务管理器即可-->
<transactionManager type="JDBC"/>
<!--dataSource:数据库连接配置
type="POOLED":表示使用传统的javax.sql.DataSource规范中的连接池
-->
<dataSource type="POOLED">
<!--驱动配置-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!--mysql连接地址-->
<property name="url" value="jdbc:mysql://localhost:3306/itsource_mybatis"/>
<!--mysql账号-->
<property name="username" value="root"/>
<!--mysql密码-->
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--引入SQL映射文件,等写了映射文件再回来修改路径-->
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
复制代码
4.数据库准备
4.1.创建数据库表
CREATE TABLE `t_employee` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`age` int DEFAULT NULL,
`sex` char(1) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
复制代码
4.2.编写domain
- 在Java中我们需要有一个类来接收从数据库中查询出来的数据,一般而言我们都使用对象的方式来进行接收,这种对象我们叫做实体类,实体类的类名与表名保持一致,字段名称需要和数据库中表字段名称保持一致
package cn.itsource.mybatis.domain;
/**
* @BelongsProject: mybatis_01
* @BelongsPackage: cn.itsource.mybatis.domain
* @Author: shezhan
* @CreateTime: 2022-11-12 20:10
* @Description: employee对应domain实体
* @Version: 1.0
*/
public class Employee {
private Long id;
private String name;
private Integer age;
private String sex;
private String email;
public Employee() {
}
public Employee(Long id, String name, Integer age, String sex, String email) {
this.id = id;
this.name = name;
this.age = age;
this.sex = sex;
this.email = email;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", email='" + email + '\'' +
'}';
}
}
复制代码
5.编写mapper接口
5.1.概述
- 通过官网我们可以发现,MyBatis提供了mapper接口映射SQL文件的方式,让我们可以调用接口方法时,调用SQL映射语句
- MyBatis底层会使用动态代理的方式,帮助我们创建mapper接口的对应实现类,所以当我们把mapper接口字节码交给MyBatis时,返回的就是他帮我们创建的实现类,那么我们直接可以调用实现类中的方法,MyBatis会根据mapper接口的全类名+方法名称定位到SQL映射文件中的namespace+id,从而实现SQL语句的调用
5.2.实现
- mapper接口命名规则:实体类名称+Mapper
package cn.itsource.mybatis.mapper;
import cn.itsource.mybatis.domain.Employee;
import java.util.List;
/**
* @BelongsProject: mybatis_01
* @BelongsPackage: cn.itsource.mybatis.mapper
* @Author: shezhan
* @CreateTime: 2022-11-12 20:34
* @Description: employee表对应mapper接口
* @Version: 1.0
*/
public interface EmployeeMapper {
/*
* @Description: 查询所有
* @Author: shezhan
* @Date: 2022/11/12 20:30
* @return: java.util.List<cn.itsource.mybatis.domain.Employee>
**/
List<Employee> selectAll();
}
复制代码
6.编写Sql映射文件
6.1.概述
- 映射文件:MyBatis有自己专门写SQL语句的文件,也就是我们的映射文件,也称之为mapper文件
- 映射文件的名称一般叫做 XxxMapper.xml (Xxx代表的是实体类名称)
- 例如实体类有: cn.itsource.mybatis.domain.Employee映射文件名为: resources/mappers/EmployeeMapper.xml
- 注意namespace的名称通常是接口的完全限定名除了MyBatis支持的类型,其它的类型都通通使用全限定
- 我们可以从官网找到映射文件示例
6.2.实现
<?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">
<!--
namespace:命名空间,用来标识此SQL映射文件对应是那个mapper接口
参数值是mapper接口的全类名
-->
<mapper namespace="cn.itsource.mybatis.mapper.EmployeeMapper">
<!--
select:查询SQL标签,在SQL映射文件中有mysql增删改查的对应标签,在标签中就写我们需要执行的sql语句
id:此sql语句的唯一标识,在同一个命名空间下id不能重复
id值就是此sql语句对应的mapper接口方法名称
resultType:自动映射,标识我们要将查询结果封装到那个实体类对象中
参数值就是要封装的实体类的全类名,必须是数据库字段与实体类字段名一直时才能自动映射
resultMap:自定义映射,当实体类字段名称与查询结果字段名称不一致时,我们需要使用自定义映射
注意:查询时必须设置resultType或者resultMap
-->
<select id="selectAll" resultType="cn.itsource.mybatis.domain.Employee">
select * from t_employee
</select>
</mapper>
复制代码
- 注意:之前我们在核心配置文件中的SQL映射文件路径还未指定,此时我们编辑好了映射文件需要回去进行指定
7.测试
package cn.itsource.mybatis.test;
import cn.itsource.mybatis.domain.Employee;
import cn.itsource.mybatis.mapper.EmployeeMapper;
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 org.junit.Test;
import java.io.InputStream;
import java.util.List;
/**
* @BelongsProject: mybatis_01
* @BelongsPackage: cn.itsource.mybatis.test
* @Author: shezhan
* @CreateTime: 2022-11-12 20:50
* @Description: mybatis测试
* @Version: 1.0
*/
public class MyBatisTest {
@Test
public void testSelectAll(){
try {
// 1.通过MyBatis提供的读取核心配置文件工具类,读取核心配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 2.根据核心配置文件,得到连接构建工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3.从连接构建工厂中获取数据库连接
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4.指定需要得到的mapper接口字节码,得到实现类
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
// 5.调用查询所有方法,相当于调用映射文件sql语句,执行查询
// 原理是根据接口的全类名+调用方法名,去匹配SQL映射文件的namespace+id,找到就执行SQL语句
List<Employee> employees = employeeMapper.selectAll();
employees.forEach(System.out::println);
}catch (Exception e){
e.printStackTrace();
}
}
}
复制代码
3、MyBatis基础CRUD
1.查询
- 第一步:编写查询接口方法
package cn.itsource.mybatis.mapper;
import cn.itsource.mybatis.domain.Employee;
import java.util.List;
/**
* @BelongsProject: mybatis_01
* @BelongsPackage: cn.itsource.mybatis.mapper
* @Author: shezhan
* @CreateTime: 2022-11-12 20:34
* @Description: employee表对应mapper接口
* @Version: 1.0
*/
public interface EmployeeMapper {
/*
* @Description: 根据主键值查询数据
* @Author: shezhan
* @Date: 2022/11/19 15:17
* @param id: 条件数据
* @return: cn.itsource.mybatis.domain.Employee
**/
Employee selectOne(Long id);
}
复制代码
- 第二步:编写映射文件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">
<!--
namespace:命名空间,用来标识此SQL映射文件对应是那个mapper接口
参数值是mapper接口的全类名
-->
<mapper namespace="cn.itsource.mybatis.mapper.EmployeeMapper">
<!--根据主键Id查询数据
1、获取mapper接口传递过来的值有两种方式,此处我们先有个概念,知道他做什么的,后续我们专门会进行讲解
1.#{}:可以有效防止SQL注入问题
2.${}:也可以获取mapper接口,但是有SQL注入风险
2、parameterType:标识传递进来的参数的类型,可以不写,mybatis会自动识别,具体对照表参照官网,此处我们传递的是Long类型的Id,那么对应mybatis中的类型就是小long
-->
<select id="selectOne" parameterType="long" resultType="cn.itsource.mybatis.domain.Employee">
select * from t_employee where id = #{id}
</select>
</mapper>
复制代码
- 第三步:测试
/*
* @Description: 测试根据Id查询
* @Author: shezhan
* @Date: 2022/11/19 15:24
* @return: void
**/
@Test
public void testSelectOne(){
try {
// 1.通过MyBatis提供的读取核心配置文件工具类,读取核心配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 2.根据核心配置文件,得到连接构建工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3.从连接构建工厂中获取数据库连接
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4.指定需要得到的mapper接口字节码,得到实现类
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
// 5.调用根据Id查询员工方法
Employee employee = employeeMapper.selectOne(1L);
System.out.println(employee);
}catch (Exception e){
e.printStackTrace();
}
}
复制代码
2.新增
- 第一步:编写新增接口方法
package cn.itsource.mybatis.mapper;
import cn.itsource.mybatis.domain.Employee;
import java.util.List;
/**
* @BelongsProject: mybatis_01
* @BelongsPackage: cn.itsource.mybatis.mapper
* @Author: shezhan
* @CreateTime: 2022-11-12 20:34
* @Description: employee表对应mapper接口
* @Version: 1.0
*/
public interface EmployeeMapper {
/*
* @Description: 新增员工
* @Author: shezhan
* @Date: 2022/11/19 15:52
* @param employee: 员工对象
* @return: int 代表受影响的行数,真实开发中返回值类型我们一般设置为void
**/
int insert(Employee employee);
}
复制代码
- 第二步:编写映射文件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">
<!--
namespace:命名空间,用来标识此SQL映射文件对应是那个mapper接口
参数值是mapper接口的全类名
-->
<mapper namespace="cn.itsource.mybatis.mapper.EmployeeMapper">
<!--新增员工数据
1.parameterType:代表传递进来的参数类型,如果是我们的实体类对象,那么需要写实体类的全类名
新增不需要写返回值类型,mybatis默认增删改都是返回受影响的行数,也就是一个int类型的数字
2.在映射文件中,如果我们想要获取对象中的字段值,那么我们可以有两种方式
#{}和${},直接在{}中写上传递进来的对象的字段名称即可调用对象字段的getter方法进行参数获取
此处我们使用#{},原因我们后续讲解
-->
<insert id="insert" parameterType="cn.itsource.mybatis.domain.Employee">
insert into t_employee(name, age, sex, email) values (#{name}, #{age}, #{sex}, #{email})
</insert>
</mapper>
复制代码
- 第三步:测试
/*
* @Description: 测试新增
* @Author: shezhan
* @Date: 2022/11/19 15:24
* @return: void
**/
@Test
public void testInsert(){
try {
// 1.通过MyBatis提供的读取核心配置文件工具类,读取核心配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 2.根据核心配置文件,得到连接构建工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3.从连接构建工厂中获取数据库连接
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4.指定需要得到的mapper接口字节码,得到实现类
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
// 5.创建需要新增的员工对象,并设置数据
Employee employee = new Employee();
employee.setName("赵六");
employee.setAge(22);
employee.setSex("男");
employee.setEmail("ww@itsource.cn");
// 6.执行新增
int insert = employeeMapper.insert(employee);
// 7.默认是需要我们手动提交事务的,也可以在获取连接时sqlSessionFactory.openSession(true);代表自动提交事务
sqlSession.commit();
System.out.println(insert);
}catch (Exception e){
e.printStackTrace();
}
}
复制代码
3.修改
- 第一步:编写修改接口方法
package cn.itsource.mybatis.mapper;
import cn.itsource.mybatis.domain.Employee;
import java.util.List;
/**
* @BelongsProject: mybatis_01
* @BelongsPackage: cn.itsource.mybatis.mapper
* @Author: shezhan
* @CreateTime: 2022-11-12 20:34
* @Description: employee表对应mapper接口
* @Version: 1.0
*/
public interface EmployeeMapper {
/*
* @Description: 根据Id修改员工信息
* @Author: shezhan
* @Date: 2022/11/19 16:02
* @param employee: 要修改的员工信息对象
* @return: void,不接收受影响的行数
**/
void update(Employee employee);
}
复制代码
- 第二步:编写映射文件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">
<!--
namespace:命名空间,用来标识此SQL映射文件对应是那个mapper接口
参数值是mapper接口的全类名
-->
<mapper namespace="cn.itsource.mybatis.mapper.EmployeeMapper">
<!--修改员工数据-->
<update id="update" parameterType="cn.itsource.mybatis.domain.Employee">
update t_employee set name = #{name} where id = #{id}
</update>
</mapper>
复制代码
- 第三步:测试
/*
* @Description: 测试修改
* @Author: shezhan
* @Date: 2022/11/19 16:05
* @return: void
**/
@Test
public void testUpdate(){
try {
// 1.通过MyBatis提供的读取核心配置文件工具类,读取核心配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 2.根据核心配置文件,得到连接构建工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3.从连接构建工厂中获取数据库连接,我们打开自动提交事务,就无需手动提交了
SqlSession sqlSession = sqlSessionFactory.openSession(true);
// 4.指定需要得到的mapper接口字节码,得到实现类
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
// 5.创建需要新增的员工对象,并设置数据
Employee employee = new Employee();
employee.setName("司马懿");
employee.setId(4L);
// 6.执行修改
employeeMapper.update(employee);
}catch (Exception e){
e.printStackTrace();
}
}
复制代码
4.删除
- 第一步:编写新增接口方法
package cn.itsource.mybatis.mapper;
import cn.itsource.mybatis.domain.Employee;
import java.util.List;
/**
* @BelongsProject: mybatis_01
* @BelongsPackage: cn.itsource.mybatis.mapper
* @Author: shezhan
* @CreateTime: 2022-11-12 20:34
* @Description: employee表对应mapper接口
* @Version: 1.0
*/
public interface EmployeeMapper {
/*
* @Description: 查询所有
* @Author: shezhan
* @Date: 2022/11/12 20:30
* @return: java.util.List<cn.itsource.mybatis.domain.Employee>
**/
List<Employee> selectAll();
/*
* @Description: 根据主键值查询数据
* @Author: shezhan
* @Date: 2022/11/19 15:17
* @param id: 条件数据
* @return: cn.itsource.mybatis.domain.Employee
**/
Employee selectOne(Long id);
/*
* @Description: 新增员工
* @Author: shezhan
* @Date: 2022/11/19 15:52
* @param employee: 员工对象
* @return: int 代表受影响的行数,真实开发中返回值类型我们一般设置为void
**/
int insert(Employee employee);
/*
* @Description: 根据Id修改员工信息
* @Author: shezhan
* @Date: 2022/11/19 16:02
* @param employee: 要修改的员工信息对象
* @return: void,受影响的行数
**/
void update(Employee employee);
/*
* @Description: 根据Id删除员工信息
* @Author: shezhan
* @Date: 2022/11/19 16:11
* @param id: 员工对应主键Id数据
* @return: void
**/
void delete(Long id);
}
复制代码
- 第二步:编写映射文件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">
<!--
namespace:命名空间,用来标识此SQL映射文件对应是那个mapper接口
参数值是mapper接口的全类名
-->
<mapper namespace="cn.itsource.mybatis.mapper.EmployeeMapper">
<!--根据Id删除员工信息-->
<delete id="delete" parameterType="long">
delete from t_employee where id = #{id}
</delete>
</mapper>
复制代码
- 第三步:测试
/*
* @Description: 测试根据Id删除员工信息
* @Author: shezhan
* @Date: 2022/11/19 16:13
* @return: void
**/
@Test
public void testDelete(){
try {
// 1.通过MyBatis提供的读取核心配置文件工具类,读取核心配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 2.根据核心配置文件,得到连接构建工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3.从连接构建工厂中获取数据库连接,我们打开自动提交事务,就无需手动提交了
SqlSession sqlSession = sqlSessionFactory.openSession(true);
// 4.指定需要得到的mapper接口字节码,得到实现类
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
// 6.执行删除
employeeMapper.delete(4L);
}catch (Exception e){
e.printStackTrace();
}
}
复制代码
4、MyBatis工具类封装
1.概述
- 前面我们已经实现了使用MyBatis进行增删改查我们的数据库数据了,那么各位也可以发现,在五个方法中,大部分代码都是重复的
- 以查询所有为例
2.封装工具类
package cn.itsource.mybatis.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.IOException;
import java.io.InputStream;
/**
* @BelongsProject: mybatis_01
* @BelongsPackage: cn.itsource.mybatis.utils
* @Author: shezhan
* @CreateTime: 2022-11-19 16:27
* @Description: MyBatis工具类
* @Version: 1.0
*/
public class MyBatisUtil {
// 1.定义一个私有的全局连接工厂对象
private static SqlSessionFactory sqlSessionFactory = null;
// 2.在类加载时加载一次连接工厂对象
static {
try {
// 1.获取MyBatis核心配置文件
InputStream resource = Resources.getResourceAsStream("mybatis-config.xml");
// 2.根据核心配置文件创建连接工厂对象
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);
} catch (IOException e) {
e.printStackTrace();
}
}
// 3.提供获取连接的方法
public static SqlSession getSqlSession(){
if (sqlSessionFactory != null){
return sqlSessionFactory.openSession(true);
}
return null;
}
}
复制代码
3.工具类测试
/*
* @Description: MyBatis工具类测试
* @Author: shezhan
* @Date: 2022/11/19 16:32
* @return: void
**/
@Test
public void testMyBatisUtil(){
// 1.获取连接
SqlSession sqlSession = MyBatisUtil.getSqlSession();
// 2.得到mapper接口实现类
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
// 3.执行查询所有
List<Employee> employees = mapper.selectAll();
// 4.打印输出
employees.forEach(System.out::println);
}
复制代码
5、MyBatis使用细节
1.新增返回自增Id
- 在真实开发当中,某些情况下我们需要知道当前新增数据的主键Id是多少,那么此时我们可以通过配置开启主键返回得到我们的自增长主键Id
- 第一步:映射文件中开启主键返回
<?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">
<!--
namespace:命名空间,用来标识此SQL映射文件对应是那个mapper接口
参数值是mapper接口的全类名
-->
<mapper namespace="cn.itsource.mybatis.mapper.EmployeeMapper">
<!--新增员工数据
1.parameterType:代表传递进来的参数类型,如果是我们的实体类对象,那么需要写实体类的全类名
新增不需要写返回值类型,mybatis默认增删改都是返回受影响的行数,也就是一个int类型的数字
2.在映射文件中,如果我们想要获取对象中的字段值,那么我们可以有两种方式
#{}和${},直接在{}中写上传递进来的对象的字段名称即可调用对象字段的getter方法进行参数获取
此处我们使用#{},原因我们后续讲解
3.自增主键返回属性
useGeneratedKeys:开启自增主键返回
true:开启返回自增主键
false:关闭返回自增主键,默认值
keyProperty:此属性表示将Id返回到传递进来的对象的那个字段,一般我们都填写id字段
keyColumn:此属性表示数据库中的那一列是主键,如果主键列在数据库表中是第一列,那么默认可以不写,
通常情况下我们在设计表时主键都是第一列,所以此属性可以不写
-->
<insert id="insert" parameterType="cn.itsource.mybatis.domain.Employee"
useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into t_employee(name, age, sex, email) values (#{name}, #{age}, #{sex}, #{email})
</insert>
</mapper>
复制代码
- 第二步:测试获取自增主键
/*
* @Description: 测试新增获取自增长Id
* @Author: shezhan
* @Date: 2022/11/19 17:15
* @return: void
**/
@Test
public void testInsertGetId(){
try {
// 1.获取连接
SqlSession sqlSession = MyBatisUtil.getSqlSession();
// 2.得到mapper接口实现类
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
// 4.指定需要得到的mapper接口字节码,得到实现类
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
// 5.创建需要新增的员工对象,并设置数据
Employee employee = new Employee();
employee.setName("仁宗帝");
employee.setAge(34);
employee.setSex("男");
employee.setEmail("rzd@itsource.cn");
// 6.执行新增
employeeMapper.insert(employee);
// 7.获取返回的自增Id,因为我们的keyProperty属性设置的是id字段,所以主键会返回到我们传递进去对象的id字段中
System.out.println(employee.getId());
}catch (Exception e){
e.printStackTrace();
}
}
复制代码
2.MyBatis运行日志
- 通过前面对于MyBatis的运用,我们发现一个问题,我们并不知道我们的SQL语句在运行时期是什么样子,因为我们都是通过占位符进行SQL的编写,这样不便于我们在真实开发当中进行排错,所以我们需要开启log4j的支持
- 日志级别:ALL < TRACE< DEBUG < INFO < WARN < ERROR < FATAL < OFFOFF | 关闭:最高级别,不打印日志FATAL | 致命:指明非常严重的可能会导致应用终止执行错误事件ERROR | 错误:指明错误事件,但应用可能还能继续运行WARN | 警告:指明可能潜在的危险状况INFO | 信息:指明描述信息,从粗粒度上描述了应用运行过程DEBUG | 调试:指明细致的事件信息,对调试应用最有用TRACE | 跟踪:指明程序运行轨迹,比DEBUG级别的粒度更细ALL | 所有:所有日志级别,包括定制级别
- 第一步:导入依赖
<!--log4j日志,用于打印mybatis运行日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
复制代码
- 第二步:复制配置文件到src->main->resource下,日志文件名称为log4j.properties,名称是固定的
#日志器logger #輸出器appender #布局器layout
#1.控制台输出
#指定日志器的输出级别和日志器的名称
#log4j.rootLogger=info,myconsole
#指定输出器
#log4j.appender.myconsole=org.apache.log4j.ConsoleAppender
#指定布局器
#log4j.appender.myconsole.layout=org.apache.log4j.SimpleLayout
#2.文件输出.txt
#指定日志器的输出级别和日志器的名称
#log4j.rootLogger=error,myfile
#指定输出器
#log4j.appender.myfile=org.apache.log4j.FileAppender
#log4j.appender.myfile.File=E:\\log4j.txt
#指定布局器(普通布局表示文本输出)
#log4j.appender.myfile.layout=org.apache.log4j.SimpleLayout
#3.文件输出.html
#指定日志器的输出级别和日志器的名称
#log4j.rootLogger=error,myhtml
#指定输出器
#log4j.appender.myhtml=org.apache.log4j.FileAppender
#log4j.appender.myhtml.File=D:\\log4j.html
#指定布局器(网页布局)
#log4j.appender.myhtml.layout=org.apache.log4j.HTMLLayout
#4.控制台输出+文件输出.txt
#指定日志器的输出级别和日志器的名称
#log4j.rootLogger=error,con,file
#指定输出器
#log4j.appender.con=org.apache.log4j.ConsoleAppender
#log4j.appender.file=org.apache.log4j.FileAppender
#log4j.appender.file.File=D\:\\log4j.txt
#指定布局器(网页布局)
#log4j.appender.con.layout=org.apache.log4j.SimpleLayout
#log4j.appender.file.layout=org.apache.log4j.SimpleLayout
#5.控制台输出+自定义布局
#指定日志器的输出级别和日志器的名称
log4j.rootLogger=DEBUG,custom
#指定输出器,输出到控制台
log4j.appender.custom=org.apache.log4j.ConsoleAppender
#指定布局为自定义布局,可以灵活的配置布局
log4j.appender.custom.layout=org.apache.log4j.PatternLayout
#指定在自定义布局的格式,%d -- 表示当前系统时间,%t -- 执行该业务的线程名称,%p -- 日记器的级别,-5 -- 5表示输出字符的个数,符号表示右对齐
#%c -- 表示指定业务所在的类的完全限定名(包名.类名),%m -- 输出额外信息,%n -- 表示换行
log4j.appender.custom.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
#设置要输出日志的包package(可以是自定义的包也可以是api的包)输出级别
log4j.logger.org.apache.ibatis=info
log4j.logger.cn.itsource=debug
复制代码
- 第三步:执行新增查看控制台
6、MyBatis核心配置文件详解
1.environments
- environments:配置多个连接数据库的环境,在MyBatis核心配置文件中,我们可以配置多套连接数据库的环境,并且以default属性来选择默认的环境
- default:此属性用于选择默认使用哪套环境,对应的值就是environment标签的id值
- environment:配置一个连接数据库的环境
- id:标识此环境的唯一标识,不能重复,用于让外部environments的default属性进行选择
- transactionManager:设置事务的管理方式
- type:设计事务管理的类型,有两个值JDBC:表示当前环境中,执行SQL时使用JDBC原生的事务管理方式,也就是需要手动提交事务或者手动回滚事务MANAGED:表示当前环境中事务被管理,比如以后我们使用spring框架,那么事务可以被spring框架管理
- dataSource:配置数据源,也就是设置连接数据库的四大属性
- type:设置连接数据源的类型POOLED:表示使用数据库连接池来缓存连接,那么下次需要获取连接时从缓存中获取即可UNPOOLED:表示不使用数据库连接池JNDI:是SUN公司推出的一套规范,可以在服务器中注册数据源
- property:设置连接数据库的四大属性
2.properties
- 在MyBatis核心配置文件中,数据源的四大连接属性官方推荐是写在配置文件中,通过properties属性引入到核心配置文件中再进行读取,我们之前是直接写死的,接下来我们按照官方的方式进行配置
- 第一步:在resources下创建jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/itsource_mybatis
jdbc.username=root
jdbc.password=root
复制代码
- 第二步:改造核心配置文件
<?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:核心配置根标签-->
<configuration>
<!--引入jdbc.properties,动态读取数据源四大属性值-->
<properties resource="jdbc.properties"></properties>
<!--连接数据库环境配置根标签,可以配置多套环境,以default参数作为选择哪个环境标识-->
<environments default="development">
<!--environment:连接数据库环境配置标签,可以配置数据库连接相关参数
id是此环境的唯一标识,default属性选择哪个id那么哪个环境就生效
-->
<environment id="development">
<!--transactionManager:事务管理器,默认就使用JDBC事务管理器即可-->
<transactionManager type="JDBC"/>
<!--dataSource:数据库连接配置
type="POOLED":表示使用传统的javax.sql.DataSource规范中的连接池
-->
<dataSource type="POOLED">
<!--驱动配置-->
<property name="driver" value="${jdbc.driver}"/>
<!--mysql连接地址-->
<property name="url" value="${jdbc.url}"/>
<!--mysql账号-->
<property name="username" value="${jdbc.username}"/>
<!--mysql密码-->
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--引入SQL映射文件,等写了映射文件再回来修改路径-->
<mappers>
<mapper resource="mappers/EmployeeMapper.xml"/>
</mappers>
</configuration>
复制代码
3.typeAliases
- 在MyBatis中,有一些内置的别名,我们在映射文件中使用时就可以根据已经内置好的别名进行使用,除此之外我们也可以将我们的实体类进行别名映射,这样以后在映射文件中写返回值类型时,我们就无需再写类的全类名了
别名 | 映射的类型 |
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
- 第一种:单个类取别名
<!--类型别名映射标签,注意按照MyBatis核心配置文件编写顺序,此标签需要在properties下-->
<typeAliases>
<!--typeAlias:给单个实体类对象映射别名
默认别名:如果不写alias属性,那么别名的名称就是类名或者是类名首字母小写
alias:设置类型别名名称,如果设置了,那么以设置的为准,一般我们不进行设置,即便设置也是类名
-->
<typeAlias type="cn.itsource.mybatis.domain.Employee" alias="Employee"></typeAlias>
</typeAliases>
复制代码
- 第二种:给包下所有类取别名,推荐使用
<!--类型别名映射标签,注意按照MyBatis核心配置文件编写顺序,此标签需要在properties下-->
<typeAliases>
<!--package:指定要取别名的包路径,那么这个包下面所有类都会取别名,别名默认就是类名或者类型小写-->
<package name="cn.itsource.mybatis.domain"/>
</typeAliases>
复制代码
- 别名使用
<!-- resultType:取了别名以后返回值类型直接写别名,无需再写全类名 -->
<select id="selectAll" resultType="Employee">
select * from t_employee
</select>
复制代码
4.mappers
- 此标签是用来引入我们的映射文件的,之前我们是使用的mapper标签对映射文件进行单个引入,这种方式在真实开发当中不常用,因为我们以后的映射文件肯定是很多的,一个数据库表对应一个mapper接口,一个mapper接口对应一个映射文件,所以我们会采用对映射文件以包的方式进行引入
- 映射文件包创建规范在resources下必须以 / 的方式进行创建,这样才能创建出多级包路径包的路径必须跟mapper接口路径保持一致
7、MyBatis获取参数值的两种方式
1.概述
- 如果有学过JDBC的同学应该知道,在JDBC中我们可以使用两种方式进行SQL语句参数的赋值,一种是SQL语句拼接参数,一种是使用占位符,那么其实在MyBatis中${}和#{}就是对应这两种方式
- 之前在映射文件中,我们讲过可以通过两种方式获取到mapper接口的参数值
- ${}:本质上是字符串拼接,如果参数类型是字符串或者是时间类型那么需要手动添加单引号
- #{}:本质上是占位符赋值,也被称为预编译,如果参数类型是字符串或者是时间类型那么会自动加单引号
String name = "'123' OR 1=1";
select * from t_employee where name = '123' OR 1=1
2.区别
- #{}
- #{} 解析为一个预编译语句的参数标记符,一个 #{} 被解析为一个参数占位符SQL语句:"SELECT * FROM t_employee where name = #{name}"预编译语句为:"SELECT * FROM t_employee where name = ?"
- #{} 解析之后会将String类型或者时间类型的数据自动加上引号,其他数据类型不会
- #{} 很大程度上可以防止SQL注入
- ${}
- ${}仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换SQL语句:"SELECT * FROM t_employee where name = '"+name+"'"
- ${} 解析之后是什么就是什么,他不会当做字符串处理,所以如果是字符串或者时间类型需要我们手动加上单引号
- ${} 主要用于SQL拼接的时候,有很大的SQL注入隐患
- 在某些特殊场合下只能用${},不能用#{}
- 例如:在使用排序时ORDER BY ${id},如果使用#{id},则会被解析成ORDER BY “id”,这显然是一种错误的写法
- SQL注入
- 如果使用拼接的方式,那么我们就是在条件后面拼接参数值,那么如果是下面这种情况,就会出现SQL注入问题了,不管我是否知道具体的条件,我都可以通过拼接OR 1= 1得到数据,这就是SQL注入name值为:123 OR 1 = 1SQL语句为:"SELECT * FROM t_employee where name = '"+username+"'"最终运行时:"SELECT * FROM t_employee where name = 123 OR 1 = 1"
3.实战运用
3.1.参数为单个字面量
- #{}方式获取参数
- ${}获取参数
3.2.参数为多个字面量
- 定义接口
- #{}方式获取参数
- ${}方式获取参数
3.3.@Param注解方式
- 当参数为多个时,通常我们不会用MyBatis给我们准备的参数名称,而是自己使用@Param注解定义参数别名,当然一个参数时也可以使用别名的方式定义参数名称
- #{}方式获取参数
- ${}方式获取参数
3.4.参数为Map集合
- 当参数为Map时,我们在xml中根据map的key获取参数值即可
- 定义接口
- #{}方式获取参数
- ${}方式获取参数
3.5.参数为实体类对象
- 定义接口
- #{}方式获取参数
- ${}方式获取参数
3.6.参数为List集合或者数组
- 此处我们不做演示,在批量新增和删除时我们再进行研究
四.MyBatis高级
1、MyBatis多种结果封装
- 在MyBatis中,对于一条查询结果和多条查询结果我们需要使用不同的处理方式
- 搭建新工程
1.一条结果封装
1.1.对象封装结果
- 创建接口
- 编写映射XML
- 测试
1.2.List封装结果
- 编写接口
- 编写映射XML
- 测试
1.3.Map封装结果
- 编写接口
- 编写映射XML
- 测试
1.4.使用@MapKey注解
- 我们可以将数据作为Map的Key、Value,那么也可以将数据作为Value我们指定一个字段作为Key
- 编写接口
- 编写映射XML
- 测试
2.多条结果封装
2.1.List封装结果
- 编写接口
- 编写映射XML
- 测试
2.2.Map封装结果
- 编写接口
- 编写映射XML
- 测试
2、MyBatis模糊查询
- 模糊查询:通过一个字符或者多个字符去匹配某个字段值,叫做模糊查询
1.${}实现模糊查询
- 编写接口
- 编写映射XML
- 测试
2.concat函数实现模糊查询
- 编写映射XML
- 测试
4.SQL拼接实现模糊查询
- 编写映射XML
- 测试
3、MyBatis原生分页查询
1.概述
- 在MySql数据库中,limit是分页关键字,后面需要填写两个分页参数以逗号分隔第一个参数:分页数据起始位置第二个参数:分页每页数据展示条数
2.实战
- 定义分页条件对象
package cn.itsource.mybatis.query;
import lombok.Data;
/**
* @BelongsProject: mybatis_02
* @BelongsPackage: cn.itsource.mybatis.query
* @Author: shezhan
* @CreateTime: 2022-12-14 15:38
* @Description: 数据查询公共条件类
* @Version: 1.0
*/
@Data
public class BaseQuery {
// 分页页码
private Integer currentPage;
// 分页每页展示条数
private Integer pageSize;
/*
* @Description: 前端传递的参数不是数据起始位置,而是页码,所以我们需要根据页码进行计算
* @Author: shezhan
* @Date: 2022/12/14 15:40
* @return: java.lang.Integer
**/
public Integer getStart(){
// 分页起始位置计算公式:(页码-1) * 每页展示条数
return (this.currentPage - 1) * this.pageSize;
}
}
复制代码
- 编写接口
- 编写映射XML
- 测试
4、MyBatis动态表名
1.概述
- 在以后的实际开发中,一张表的数据过多时,会影响操作性能,此时就会使用水平切分,将一张表的数据,切分到多张表共同存储一张表的数据
- 当数据在多张表时,我们查询数据表名就不固定了,那么就需要将表名通过参数的形式传递到SQL中,此时我们可以使用${}的方式获取参数作为表名
- 注意此时不能使用#{},因为#{}会帮助我们添加单引号,表名是不能添加单引号的
2.实战
- 编写接口
- 编写映射XML
- 测试
5、MyBatis动态SQL
- Mybatis 动态Sql可以让我们在 Xml 映射文件内,以标签的形式编写动态Sql,完成逻辑判断和动态拼接Sql 的功能
1.if标签
1.1.概述
- if标签可以解决SQL语句条件不确定是否执行的问题,通过test属性,我们可以判断某个参数是否存在,如果表达式为true,那么标签中的语句会被执行,否则不执行
- 注意
- 如果不需要过滤空字符串那么只需要判断null值
- 如果参数需要判定是否为空字符串,那么需要判断null和空字符串并且需要去空白
1.2.实战
- 编写条件对象
- 改造查询接口,使用条件类
- 编写映射XML
- 测试
2.where标签
2.1.概述
- where标签可以解决不确定if标签中表达式是否成立所引发的问题,它有两个功能如果where标签中的条件成立,那么会自动加上where关键字会自动去掉标签中条件语句前面多余的and或者是or关键字
2.2.实战
- 编写映射XML
- 测试
3.trim标签
3.1.概述
- 标签的功能是可以实现代替其他标签的功能,但同时又比其他标签更加灵活,他有四个属性prefix:指定要动态添加的前缀suffix属性:指定要动态添加的后缀prefixOverrides:去除sql语句前面的关键字或者字符,假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND",使用“|”分隔有可能的多个值suffixOverrides属性:指定要动态去掉的后缀,使用“|”分隔有可能的多个值
3.2.实战
- 编写映射XML
- 测试
4.foreach标签
4.1.概述
- foreach标签的使用场景是对集合进行遍历,并可以自定义的设置
- foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量,它也允许你指定开头与结尾的字符串以及各个元素之间的分隔符,这个元素也不会错误地添加多余的分隔符
- 属性详解collection: 表示要遍历的对象,如果要遍历的参数使用@Param注解取名了就使用该名字,如果没有取名List,或者collectionitem: 表示遍历出来的元素,我们到时候要拼接SQL语句就得使用这个元素,如果遍历出来的元素是POJO对象, 那么我们就通过 #{遍历出来的元素.POJO的属性} 获取数据,如果遍历出来的元素是简单类型的数据,那么我们就使用 #{遍历出来的元素} 获取这个简单类型数据separator: 遍历出来的元素之间的分隔符open: 在遍历出来的第一个元素之前添加前缀close: 在遍历出来的最后一个元素之后添加后缀
4.2.实战
- 实战我们就放在批量删除和批量新增时进行使用
5.choose&when&otherwise标签
5.1.概述
- choose标签实现多路选择,当choose下的when标签条件满足时,就将when中的sql拼进外面的sql,反之若不满足,则将下面的otherwise标签中的sql拼进总sql
- 这三个关键字是一套组合标签,类似于Java中的 if(){}else if(){}else{} 的功能
- choose是父标签,when和otherwise标签需要写在此标签下
- when相当于 if(){}else if(){}
- otherwise相当于最后的 else{}
5.2.实战
- 编写映射XML
- 测试
6.特殊符号处理
6.1.概述
- 在sql语句中,我们会使用到一些比较符号,在xml中需要特殊处理
- 注意:在xml中特殊字符不处理会报错
- 在xml的转义字符
- 符号:<、<=、>、>=、&、' 、"
- gt :大于
- lt:小于
- get:大于等于
- let:小于等于
- 或者可以用CDATA代码段
- 大于等于 <![CDATA[ >= ]]>
- 小于等于 <![CDATA[ <= ]]>
6.2.实战
- 编写映射XML
- 测试
7.sql标签
7.1.概述
- 在java代码中,经常使用到的代码,我们会将他抽取为一个方法,在需要使用的地方引入,那么在SQL中也可以将重复的SQL代码块进行抽取,然后引入使用
7.2.实战
- 编写映射XML
- 测试
8.set标签
8.1.概述
- set标签是在我们进行update操作时使用,可以解决修改条件不确定时引发的问题,通常和if标签搭配使用,它有两个功能如果set标签中的条件成立,那么会自动加上set关键字会自动去掉标签中条件语句中多余的逗号
8.2.实战
- 编写接口
- 编写映射XML
- 测试
6、Mybatis批量新增
1.sql中批量添加写法
INSERT INTO t_employee (name,age,sex,email) VALUES ("小明",18,'男',"xm@itsource.com"),("小李",19,'女',"xl@itsource.com")
复制代码
2.实战
- 编写接口
- 编写映射XML
- 测试
7、MyBatis批量删除
1.sql中批量删除写法
DELETE FROM t_employee where id in (4,5,6); -- 推荐使用
DELETE FROM t_employee where id=4 or id=5 or id=6; -- 不推荐
复制代码
2.实战
- 编写接口
- 编写映射XML
- 测试
8、MyBatis解决字段映射名称不一致方案
1.概述
- 在实际开发中,数据库中的字段名规范是以下划线分隔,而Java的实体类字段规范是小驼峰,所以这就会导致字段自动映射失败,无法将数据库数据封装到实体类对象中
- 问题展示
2.Sql别名方案
- 在Sql语句中,我们可以给返回值起别名,那么我们可以让我们的字段别名与实体类字段名称保持一致,这样就可以自动映射成功
- 这种方式过于麻烦,不推荐使用
3.MyBatis全局配置方案
- MyBatis提供了全局属性配置来解决数据库与实体类字段名称不一致问题,我们只需要配置开启,那么就会自动将数据库下划线字段映射成实体类驼峰字段
- 实际开发中使用较多,推荐使用
4.MyBatis自定义映射方案
- 我们之前映射XML中SQL语句的返回值属性一直使用的是resultType,此属性是自动映射的意思,会根据数据库名称和实体类名称进行自动映射,当我们的数据库字段与实体类不一致时,那么还可以使用resultMap进行自定义映射,手动指定数据库字段与实体类字段的映射关系
- 注意:自定义映射只要需要查询的字段都需要映射完整,不管字段名称是否一致
属性:
id:此resultMap映射的名称
type:自定义映射到那个实体类中
property:指定实体类字段是谁
column:指定数据库字段是谁
标签:
id:指定数据库主键字段与实体类字段映射关系的标签
result:指定数据库普通字段与实体类字段映射关系的标签
复制代码
9、关联关系概述
1.关联关系的分类
1.1.一对一
- 一个人对应一个身份证ID
- 一个QQ对应一个QQ空间
- 一个手机号对应一个微信号
- 真实开发使用的地方较少
1.2.一对多
- 站在部门的角度看问题
- 例子:一个部门【dept】对应多个员工【emp】
- 真实开发使用较多
1.3.多对一
- 站在员工的角度去看问题
- 例子:多个员工【emp】对应一个部门【dept】
- 真实开发使用较多
1.4.多对多
- 老师【teacher】对学生【student】
- 学生【student】对老师【teacher】
- 多个用户【user】对应多个角色【role】
- 真实开发使用较多
2.关联关系表的设计
2.1.一对一
- 共享主键一对一:耦合度太高,不方便维护
- 唯一外键一对一,在任意一方设计外键
2.2.一对多&多对一
- 表设计都一样
- 只要有外键就是多方
- 在多方设计外键
2.3.多对多
- 需要一个中间表
3.关联关系domain设计
3.1.一对一
- 在任意一方添加对方的对象字段
3.2.一对多
- 在一方添加多方的List集合对象字段
3.3.多对一
- 在多方添加一个一方的对象字段
3.4.多对多
- 双方都保存对方的List集合对象字段
10、MyBatis实现关联关系查询
- MyBatis提供两种方式处理我们关联对象,嵌套查询和嵌套结果
- 嵌套结果查询: 发送1条SQL,查询所有的信息(本身+关联对象)
- 嵌套语句查询:发送1+N条sql
- 接下来,分别使用两种方式对多对一、一对多进行处理
1.一对多代码实现
1.1.嵌套结果查询
- 一对多与多对一的表设计是一样的,只是看待表的角度不同
- 使用较多
1.创建domain模型
2.Mapper接口的准备
3.Mapper的XML准备
- 当我们要把数据封装到集合中时需要使用collection标签property:表示要把集合封装到对象的那个字段中ofType:表示集合泛型是什么
4.功能测试
1.2.嵌套语句查询
- 使用较少
1.Mapper接口的准备
2.Mapper的XML准备
- collection标签表示封装集合
- property:封装对象的那个字段
- column:调用SQL语句时传递的参数
- select:集合的数据通过那条SQL语句获得
- EmployeeMapper.xml
- DepartmentMapper.xml
3.功能测试
2.多对一代码实现
- 一对一与多对一代码编写上是一样的,所以一对一就不单独讲解
2.1.嵌套结果查询
1.创建domain模型
2.Mapper接口准备
3.Mapper的XML准备
- association:此标签用于封装对象property:对象封装到实体类的那个字段javaType:封装的对象类型
4.功能测试
2.2.嵌套语句查询
- 使用较少
1.Mapper接口的准备
2.Mapper的XML准备
- association:此标签用于封装对象
- property:标识对象封装到实体类的那个字段
- column:调用SQL语句时传递的参数
- select:对象的数据来自哪条SQL语句
- DepartmentMapper.xml
- EmployeeMapper.xml
3.功能测试
3.多对多
- 多对多其实就是双向一对多,我们已经学习了一对多的查询,那么多对多就是SQL查询语句不一样,封装结果操作都是一样的,各位同学可以自己进行练习
11、延迟加载
1.概述
- MyBatis中的延迟加载,也称为懒加载,是指在进行关联查询时,按照设置延迟规则推迟对关联对象的select查询,延迟加载可以有效的减少数据库压力
- MyBatis的延迟加载只是对关联对象的查询有延迟设置,对于主加载对象都是直接执行查询语句的
2.开启延迟加载
- mybatis-config.xmllazyLoadingEnabled:延迟加载全局开关,设置为true代表开启,当开启时所有对象都会进行延迟加载aggressiveLazyLoading:此属性表示是否需要按需加载true:即便全局延迟加载打开,对象还是会加载所有属性false:默认值,所有对象的属性都按需加载
<settings>
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--关闭积极加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
复制代码
3.局部立即加载
- 当我们开启了全局延迟加载之后,如果有些SQL我们需要立即加载,那么可以在collection或者是association标签中指定fetchType属性
- fetchType:局部指定SQL语句是延迟加载还是立即加载,一般当我们开启了全局延迟加载之后,部分SQL需要立即加载时配置此属性eager:立即加载lazy:延迟加载(默认)
五.MyBatis进阶
1、MyBatis缓存
1.什么是缓存
- 缓存就是内存中的一个对象,用于对数据库查询结果的保存,用于减少与数据库的交互次数从而降低数据库的压力,进而提高响应速度
2.什么是MyBatis缓存
- MyBatis 中的缓存就是说 MyBatis 在执行一次SQL查询之后,这条SQL语句的查询结果并不会消失,而是被MyBatis缓存起来,当再次执行相同SQL语句的时候,就会直接从缓存中进行提取,而不是再次执行SQL命令
- MyBatis中的缓存分为一级缓存和二级缓存
3.MyBatis一级缓存
3.1.概述
- 一级缓存又被称为 SqlSession 级别的缓存,是MyBatis默认开启的缓存
- SqlSession是什么:SqlSession 是SqlSessionFactory会话工厂创建出来的一个会话的对象,这个SqlSession对象用于执行具体的SQL语句并返回给用户请求的结果
- SqlSession级别的缓存是什么意思?SqlSession级别的缓存表示的就是每当执行一条SQL语句后,默认就会把该SQL语句缓存起来,也被称为会话缓存
3.2.四种失效情况
- 使用不同的SqlSession查询数据
- 同一个SqlSession但是查询条件不同
- 同一个SqlSession两次查询期间执行了任意一次增删改操作
- 同一个SqlSession两次查询期间手动清除了缓存
4.MyBatis二级缓存
4.1.概述
- 二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
4.2.二级缓存开启条件
- 在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置
- 在映射文件中设置标签
- 二级缓存必须在SqlSession关闭或提交之后有效
- 查询的数据所转换的实体类类型必须实现序列化的接口
4.3.失效情况
- 两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
4.4.二级缓存配置
- 在mapper配置文件中添加的cache标签可以设置一些属性
- eviction:缓存回收策略
- LRU:最近最少使用的:移除最长时间不被使用的对象,默认的是 LRU
- FIFO:先进先出:按对象进入缓存的顺序来移除它们
- SOFT:移除基于垃圾回收器状态和软引用规则的对象
- WEAK:更积极地移除基于垃圾收集器状态和弱引用规则的对象
- flushInterval:刷新间隔,单位毫秒,默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新,也就是执行增删改才刷新缓存
- size属性:(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024
- readOnly属性:是否只读
- true:只读缓存,会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改,性能较高
- false:读写缓存,会返回缓存对象的拷贝,性能较低,但是安全,因此默认是 false
4.5.缓存查询执行顺序
- 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用
- 如果二级缓存没有命中,再查询一级缓存
- 如果一级缓存也没有命中,则查询数据库
- SqlSession关闭之后,一级缓存中的数据会写入二级缓存
2、PageHelper分页插件
1.概述
- 在前面我们通过手动实现了MyBatis的分页,这种原生的方式虽然性能最高,但是实现上稍微有点复杂,所以在真实开发中,也会使用分页插件
- PageHelper是一个开源的基于MyBatis拦截器开发的通用分页插件工具,该插件是国人开发的,所以文档是中文的
- PageHelper官网地址:pagehelper.github.io/PS:可以通过阅读官网文档进行学习
2.使用
- 导入依赖
- 配置PageHelper一般只设置一个reasonable(是否合理分页)为true,其它参数请参考具体文档
- 执行分页PageHelper的分页使用非常简单,只需要在执行mapper方法之前,调用静态方法传入分页参数即可
3.对象说明
- Page对象和PageInfo对象中的属性是不一样的,以后真实开发中我们需要根据前端所需要的参数要决定我们需要返回那个对象
- Page对象
private int pageNum; //当前页码
private int pageSize; //每页数据的数量
private int startRow; //始页首行行号
private int endRow; //尾页尾行行号
private long total; //总记录数
private int pages; //总页数
private Boolean reasonable; //分页合理化
private Boolean pageSizeZero; //当设置为true的时候,如果pagesize设置为0(或RowBounds的limit=0),就不执行分页,返回全部结果
复制代码
- PageInfo对象
private int pageNum; //当前页
private int pageSize; //每页显示数据条数
private int size; //当前页的数量
private int startRow; //始页首行行号
private int endRow; //尾页尾行行号
private long total; //总记录数
private int pages; //总页数
private List<T> list; //查询结果的数据
private int firstPage; //首页
private int prePage; //上一页
private int nextPage; // 下一页
private int lastPage; //最后一页
private boolean isFirstPage; //是不是第一页
private boolean isLastPage; //是不是最后一页
private boolean hasPreviousPage;//有没有上一页
private boolean hasNextPage; //有没有下一页
private int navigatePages; //所有导航页号
private int[] navigatepageNums; //导航页码数
复制代码
3、MyBatis逆向工程
1.概述
- 在实际开发中,为了提高开发效率,节省开发时间,那么通常一些重复的代码我们会使用工具进行生成,比如像domain、mapper、SQL语句等等有固定模板的都是可以通过工具进行基础代码的生成,一般有两种玩法
- 正向工程:先创建Java实体类,由框架根据实体类生成数据库表,Hibernate支持正向工程
- 逆向工程:先创建数据库表,由框架来负责根据数据库表,反向生成下面的资源Java实体类Mapper接口Mapper映射文件
- 官方网址:mybatis.org/generator/
2.使用
- 导入依赖
<!-- 依赖MyBatis核心包 -->
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<!--junit驱动-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--log4j驱动-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
</dependencies>
<!-- 控制Maven在构建过程中相关配置 -->
<build>
<!-- 构建过程中用到的插件 -->
<plugins>
<!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.1</version>
<!-- 插件的依赖 -->
<dependencies>
<!-- 逆向工程的核心依赖 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.4.1</version>
</dependency>
<!--此处添加一个mysql-connector-java依赖可以防止找不到jdbc Driver-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
复制代码
- 配置文件准备
- mybatis-config.xml
- jdbc.properties
- log4j.properties
- generatorConfig.xml
- generatorConfig.xml:逆向工程核心配置文件,必须叫这个名称
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--
targetRuntime: 执行生成的逆向工程的版本
MyBatis3Simple: 生成基本的CRUD
MyBatis3: 生成带条件的CRUD
defaultModelType :flat 该模型为每一张表只生成一个实体类(解决一表生成多个实体)
-->
<context id="MySqlTables" targetRuntime="MyBatis3Simple" defaultModelType="flat">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- 数据库的连接信息 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/itsource_mybatis"
userId="root"
password="root">
<!--解决映射文件出现重复多余字段-->
<property name="nullCatalogMeansCurrent" value="true"/>
</jdbcConnection>
<!-- 把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,
true时把JDBC DECIMAL NUMERIC 类型解析为java.math.BigDecimal
默认为false-->
<javaTypeResolver>
<property name="forceBigDecimals" value="true" />
</javaTypeResolver>
<!-- domain的生成策略-->
<javaModelGenerator targetPackage="cn.itsource.mybatis.domain" targetProject=".\src\main\java">
<!--是否允许使用子包:表示targetPackage属性的值是多级目录还是一级,true为多级目录,false为一级-->
<property name="enableSubPackages" value="true"/>
<!--是否去除数据库表字段前后空格,true为去除,false不去除-->
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- SQL映射文件的生成策略 -->
<sqlMapGenerator targetPackage="cn.itsource.mybatis.mapper" targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!-- Mapper接口的生成策略 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="cn.itsource.mybatis.mapper"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- 逆向分析的表 -->
<!-- tableName:设置要生成的表名 -->
<!-- domainObjectName:指定生成出来的实体类的类名 -->
<table tableName="t_department" domainObjectName="Department"/>
</context>
</generatorConfiguration>
复制代码
- 执行插件
- 查看效果
3.逆向工程版本区别
- 在配置文件中targetRuntime属性可以设置为两个值MyBatis3Simple: 生成基本的CRUDMyBatis3: 生成带条件的CRUD,方法众多,功能强大,推荐使用
- 如果属性为MyBatis3那么逆向工程中会生成实例及实例对应的example,example用于添加条件,相当where后面的部分
- mapper接口中的方法解析
方法 | 功能说明 |
int countByExample(UserExample example) thorws SQLException | 按条件计数 |
int deleteByPrimaryKey(Integer id) thorws SQLException | 按主键删除 |
int deleteByExample(UserExample example) thorws SQLException | 按条件删除 |
String/Integer insert(User record) thorws SQLException | 插入数据(返回值为ID) |
User selectByPrimaryKey(Integer id) thorws SQLException | 按主键查询 |
ListselectByExample(UserExample example) thorws SQLException | 按条件查询 |
ListselectByExampleWithBLOGs(UserExample example) thorws SQLException | 按条件查询(包括BLOB字段)。只有当数据表中的字段类型有为二进制的才会产生 |
int updateByPrimaryKey(User record) thorws SQLException | 按主键更新 |
int updateByPrimaryKeySelective(User record) thorws SQLException | 按主键更新值不为null的字段 |
int updateByExample(User record, UserExample example) thorws SQLException | 按条件更新 |
int updateByExampleSelective(User record, UserExample example) thorws SQLException | 按条件更新值不为null的字段 |
- Example类解析
方法 | 功能说明 |
example.setOrderByClause(“字段名 ASC”); | 添加升序排列条件,DESC为降序 |
example.setDistinct(false) | 去除重复,boolean型,true为选择不重复的记录 |
criteria.andXxxIsNull | 添加字段xxx为null的条件 |
criteria.andXxxIsNotNull | 添加字段xxx不为null的条件 |
criteria.andXxxEqualTo(value) | 添加xxx字段等于value条件 |
criteria.andXxxNotEqualTo(value) | 添加xxx字段不等于value条件 |
criteria.andXxxGreaterThan(value) | 添加xxx字段大于value条件 |
criteria.andXxxGreaterThanOrEqualTo(value) | 添加xxx字段大于等于value条件 |
criteria.andXxxLessThan(value) | 添加xxx字段小于value条件 |
criteria.andXxxLessThanOrEqualTo(value) | 添加xxx字段小于等于value条件 |
criteria.andXxxIn(List<?>) | 添加xxx字段值在List<?>条件 |
criteria.andXxxNotIn(List<?>) | 添加xxx字段值不在List<?>条件 |
criteria.andXxxLike(“%”+value+”%”) | 添加xxx字段值为value的模糊查询条件 |
criteria.andXxxNotLike(“%”+value+”%”) | 添加xxx字段值不为value的模糊查询条件 |
criteria.andXxxBetween(value1,value2) | 添加xxx字段值在value1和value2之间条件 |
criteria.andXxxNotBetween(value1,value2) | 添加xxx字段值不在value1和value2之间条件 |
相关推荐
- Vue 技术栈(全家桶)(vue technology)
-
Vue技术栈(全家桶)尚硅谷前端研究院第1章:Vue核心Vue简介官网英文官网:https://vuejs.org/中文官网:https://cn.vuejs.org/...
- vue 基础- nextTick 的使用场景(vue的nexttick这个方法有什么用)
-
前言《vue基础》系列是再次回炉vue记的笔记,除了官网那部分知识点外,还会加入自己的一些理解。(里面会有部分和官网相同的文案,有经验的同学择感兴趣的阅读)在开发时,是不是遇到过这样的场景,响应...
- vue3 组件初始化流程(vue组件初始化顺序)
-
学习完成响应式系统后,咋们来看看vue3组件的初始化流程既然是看vue组件的初始化流程,咋们先来创建基本的代码,跑跑流程(在app.vue中写入以下内容,来跑流程)...
- vue3优雅的设置element-plus的table自动滚动到底部
-
场景我是需要在table最后添加一行数据,然后把滚动条滚动到最后。查网上的解决方案都是读取html结构,暴力的去获取,虽能解决问题,但是不喜欢这种打补丁的解决方案,我想着官方应该有相关的定义,于是就去...
- Vue3为什么推荐使用ref而不是reactive
-
为什么推荐使用ref而不是reactivereactive本身具有很大局限性导致使用过程需要额外注意,如果忽视这些问题将对开发造成不小的麻烦;ref更像是vue2时代optionapi的data的替...
- 9、echarts 在 vue 中怎么引用?(必会)
-
首先我们初始化一个vue项目,执行vueinitwebpackechart,接着我们进入初始化的项目下。安装echarts,npminstallecharts-S//或...
- 无所不能,将 Vue 渲染到嵌入式液晶屏
-
该文章转载自公众号@前端时刻,https://mp.weixin.qq.com/s/WDHW36zhfNFVFVv4jO2vrA前言...
- vue-element-admin 增删改查(五)(vue-element-admin怎么用)
-
此篇幅比较长,涉及到的小知识点也比较多,一定要耐心看完,记住学东西没有耐心可不行!!!一、添加和修改注:添加和编辑用到了同一个组件,也就是此篇文章你能学会如何封装组件及引用组件;第二能学会async和...
- 最全的 Vue 面试题+详解答案(vue面试题知识点大全)
-
前言本文整理了...
- 基于 vue3.0 桌面端朋友圈/登录验证+60s倒计时
-
今天给大家分享的是Vue3聊天实例中的朋友圈的实现及登录验证和倒计时操作。先上效果图这个是最新开发的vue3.x网页端聊天项目中的朋友圈模块。用到了ElementPlus...
- 不来看看这些 VUE 的生命周期钩子函数?| 原力计划
-
作者|huangfuyk责编|王晓曼出品|CSDN博客VUE的生命周期钩子函数:就是指在一个组件从创建到销毁的过程自动执行的函数,包含组件的变化。可以分为:创建、挂载、更新、销毁四个模块...
- Vue3.5正式上线,父传子props用法更丝滑简洁
-
前言Vue3.5在2024-09-03正式上线,目前在Vue官网显最新版本已经是Vue3.5,其中主要包含了几个小改动,我留意到日常最常用的改动就是props了,肯定是用Vue3的人必用的,所以针对性...
- Vue 3 生命周期完整指南(vue生命周期及使用)
-
Vue2和Vue3中的生命周期钩子的工作方式非常相似,我们仍然可以访问相同的钩子,也希望将它们能用于相同的场景。...
- 救命!这 10 个 Vue3 技巧藏太深了!性能翻倍 + 摸鱼神器全揭秘
-
前端打工人集合!是不是经常遇到这些崩溃瞬间:Vue3项目越写越卡,组件通信像走迷宫,复杂逻辑写得脑壳疼?别慌!作为在一线摸爬滚打多年的老前端,今天直接甩出10个超实用的Vue3实战技巧,手把...
- 怎么在 vue 中使用 form 清除校验状态?
-
在Vue中使用表单验证时,经常需要清除表单的校验状态。下面我将介绍一些方法来清除表单的校验状态。1.使用this.$refs...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- Vue 技术栈(全家桶)(vue technology)
- vue 基础- nextTick 的使用场景(vue的nexttick这个方法有什么用)
- vue3 组件初始化流程(vue组件初始化顺序)
- vue3优雅的设置element-plus的table自动滚动到底部
- Vue3为什么推荐使用ref而不是reactive
- 9、echarts 在 vue 中怎么引用?(必会)
- 无所不能,将 Vue 渲染到嵌入式液晶屏
- vue-element-admin 增删改查(五)(vue-element-admin怎么用)
- 最全的 Vue 面试题+详解答案(vue面试题知识点大全)
- 基于 vue3.0 桌面端朋友圈/登录验证+60s倒计时
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- node卸载 (33)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- exceptionininitializererror (33)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)