Mybatis学习记录
- 1.快速开始示例
-
- [1.1 什么是MyMyBatis?](#1.1 什么是MyMyBatis?)
- [1.2 代码示例](#1.2 代码示例)
- 2.基本使用
-
- [2.1 #{}和{}](#{}和{})
-
- [2.2 参数传入](#2.2 参数传入)
-
- [2.2.1 概念说明](#2.2.1 概念说明)
- [2.2.2 单个简单类型](#2.2.2 单个简单类型)
- [2.2.3 实体类型](#2.2.3 实体类型)
- [2.2.4 零散简单数据类型](#2.2.4 零散简单数据类型)
- [2.2.5 Map类型](#2.2.5 Map类型)
- [2.3 返回值](#2.3 返回值)
-
- [2.3.1 单个简单类型](#2.3.1 单个简单类型)
- [2.3.2 实体类型](#2.3.2 实体类型)
- [2.3.3 Map类型](#2.3.3 Map类型)
- [2.3.4 List类型](#2.3.4 List类型)
- 3.多表映射
-
- [3.1 什么是多表映射](#3.1 什么是多表映射)
- [3.2 对一映射](#3.2 对一映射)
- [3.3 对多映射](#3.3 对多映射)
- [3.4 对一映射和对多映射的说明](#3.4 对一映射和对多映射的说明)
- 4.动态语句
-
- [4.1 为什么需要动态语句](#4.1 为什么需要动态语句)
- [4.2 if where标签](#4.2 if where标签)
- [4.3 set标签](#4.3 set标签)
- [4.4 choose when otherwise标签](#4.4 choose when otherwise标签)
- [4.5 foreach标签](#4.5 foreach标签)
- 5.MyBatis高级扩展
-
- [5.1 批量映射](#5.1 批量映射)
- [5.2 分页插件 pageHelper](#5.2 分页插件 pageHelper)
- [5.3 MybatisX插件](#5.3 MybatisX插件)
1.快速开始示例
1.1 什么是MyMyBatis?
MyBatis 是一个用于 Java 编程语言的持久层框架,它可以帮助开发者更轻松地管理数据库交互。它的核心思想是将 SQL 语句从 Java 代码中分离出来,通过 XML 或注解的方式来配置和映射 SQL 查询、更新和删除等操作,简化 Java 应用程序与数据库之间的交互过程。
1.2 代码示例
- 准备数据库
sql
CREATE DATABASE `mybatis-example`;
USE `mybatis-example`;
CREATE TABLE `t_emp`(
emp_id INT AUTO_INCREMENT,
emp_name CHAR(100),
emp_salary DOUBLE(10,5),
PRIMARY KEY(emp_id)
);
INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("tom",200.33);
INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("jerry",666.66);
INSERT INTO `t_emp`(emp_name,emp_salary) VALUES("andy",777.77);
- 准备实体类
java
public class Employee {
private Integer empId;
private String empName;
private Double empSalary;
public Integer getEmpId() {
return empId;
}
public String getEmpName() {
return empName;
}
public Double getEmpSalary() {
return empSalary;
}
public void setEmpId(Integer empId) {
this.empId = empId;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public void setEmpSalary(Double empSalary) {
this.empSalary = empSalary;
}
@Override
public String toString() {
return "Employee{" +
"empId=" + empId +
", empName='" + empName + '\'' +
", empSalary=" + empSalary +
'}';
}
}
- 定义Mapper接口
java
public interface EmployeeMapper {
//根据id查询员工信息
Employee queryById(Integer id);
//根据id删除员工
int deleteById(Integer id);
}
- 定义Mapper.xml文件
(1) namespace = mapper对应接口的全限定符
(2)声明标签写sql语句crud:select insert update delete,每个标签对应接口的一个方法
(3)id是对应方法的名称,resultType是返回值。
xml
<?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.EmployeeMapper">
<select id="queryById" resultType="com.atguigu.pojo.Employee">
select emp_id empId,emp_name empName, emp_salary empSalary from
t_emp where emp_id = #{id}
</select>
<delete id="deleteById">
delete from t_emp where emp_id = #{id}
</delete>
</mapper>
- 准备MyBatis配置文件
mybatis框架配置文件: 数据库连接信息,性能配置,mapper.xml配置等。
xml
<?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>
<!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
<environments default="development">
<!-- environment表示配置Mybatis的一个具体的环境 -->
<environment id="development">
<!-- Mybatis的内置的事务管理器 -->
<transactionManager type="JDBC"/>
<!-- 配置数据源 -->
<dataSource type="POOLED">
<!-- 建立数据库连接的具体信息 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis-example"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- mapper标签:配置一个具体的Mapper映射文件 -->
<!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
<mapper resource="mapper/EmployeeMapper.xml"/>
</mappers>
</configuration>
- 测试
java
public class MybatisTest {
@Test
public void test_01() throws IOException {
//1.读取外部配置文件(mybatis-config.xml)
InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");
//2.创建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
//3.根据sqlSessionFactory创建sqlSession (每次业务创建一个,用完就释放)
SqlSession sqlSession = sqlSessionFactory.openSession();
//4.获取接口的代理对象 (代理技术) 调用代理对象的方法,就会查找mapper接口的方法
//jdk动态代理技术生成的mapper代理对象
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
// 内部拼接接口的全限定符号.方法名 去查找sql语句标签
Employee employee = mapper.queryById(1);
System.out.println("employee = " + employee);
//5.提交事务(非DQL)和释放资源
sqlSession.commit();
sqlSession.close();
}
}
2.基本使用
2.1 #{}和${}
- #{ }:常用
Mybatis会将SQL语句中的#{}转换为占位符 - { } {}形式传参,底层Mybatis做的是字符串拼接操作。
2.2 参数传入
2.2.1 概念说明
这里参数传入是指上层方法(例如Service方法)调用Mapper接口时,数据传入的形式。
- 简单类型:只包含一个值的数据类型
- 基本数据类型:int、byte、short、double、......
- 基本数据类型的包装类型:Integer、Character、Double、......
- 字符串类型:String
- 复杂类型:包含多个值的数据类型
- 实体类类型:Employee、Department、......
- 集合类型:List、Set、Map、......
- 数组类型:int[]、String[]、......
- 复合类型:List、实体类中包含集合......
2.2.2 单个简单类型
sql
//Mapper接口中抽象方法的声明
Employee selectEmployee(Integer empId);
//xml
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{empId}
</select>
2.2.3 实体类型
sql
//Mapper接口中抽象方法的声明
int insertEmployee(Employee employee);
//xml
<insert id="insertEmployee">
insert into t_emp(emp_name,emp_salary) values(#{empName},#{empSalary})
</insert>
2.2.4 零散简单数据类型
零散的多个简单类型参数,如果没有特殊处理,那么Mybatis无法识别自定义名称:
sql
//Mapper接口中抽象方法的声明
int updateEmployee(@Param("empId") Integer empId,@Param("empSalary") Double empSalary);
//XML
<update id="updateEmployee">
update t_emp set emp_salary=#{empSalary} where emp_id=#{empId}
</update>
2.2.5 Map类型
sql
//Mapper接口中抽象方法的声明
int updateEmployeeByMap(Map<String, Object> paramMap);
//XML
<update id="updateEmployeeByMap">
update t_emp set emp_salary=#{empSalaryKey} where emp_id=#{empIdKey}
</update>
2.3 返回值
2.3.1 单个简单类型
sql
//Mapper接口中的抽象方法Java
int selectEmpCount();
//XML
<select id="selectEmpCount" resultType="int">
select count(*) from t_emp
</select>
select标签,通过resultType指定查询返回值类型!
resultType = "全限定符 | 别名 | 如果是返回集合类型,写范型类型即可"
关于别名可以参考MyBatis官方文档官方文档关于别名的说明
2.3.2 实体类型
sql
//Mapper接口的抽象方法
Employee selectEmployee(Integer empId);
//XML
<!-- 编写具体的SQL语句,使用id属性唯一的标记一条SQL语句 -->
<!-- resultType属性:指定封装查询结果的Java实体类的全类名 -->
<select id="selectEmployee" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id empId,emp_name empName,emp_salary empSalary from t_emp where emp_id=#{maomi}
</select>
2.3.3 Map类型
sql
//Mapper接口的抽象方法
Map<String,Object> selectEmpNameAndMaxSalary();
//XML
<select id="selectEmpNameAndMaxSalary" resultType="map">
SELECT
emp_name 员工姓名,
emp_salary 员工工资,
(SELECT AVG(emp_salary) FROM t_emp) 部门平均工资
FROM t_emp WHERE emp_salary=(
SELECT MAX(emp_salary) FROM t_emp
)
</select>
2.3.4 List类型
查询结果返回多个实体类对象,希望把多个实体类对象放在List集合中返回。此时不需要任何特殊处理,在resultType属性中还是设置实体类类型即可。
sql
//Mapper接口中抽象方法
List<Employee> selectAll();
//XML
<select id="selectAll" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id empId,emp_name empName,emp_salary empSalary
from t_emp
</select>
3.多表映射
3.1 什么是多表映射
在实际的开发中,经常需要联合多个表进行查询。例如有2张表:客户表和订单表。分别有如下属性:
sql
customer:customer_id,customer_name
order:order_id,order_name,customer_id
如果有需求:根据客户id查询客户的信息和订单信息。或者根据订单id查询客户的信息。这时会需要联合多个表查询。
如何对查询得到的结果集进行映射,即多表映射。一般需要完成以下工作:
- 自己编写sql语句完成查询
- 自己设计存储数据的实体类
- 自己定义结果集映射
3.2 和 3.3 中的数据库如下
3.2 对一映射
- 需求:根据id查询订单信息和订单对应的客户(显然,一个订单只能对应一个客户,所以是对一)
- 代码实现
java
//Order
public class Order {
private Integer orderId;
private String orderName;
private Integer customerId;
private Customer customerInfo; //因为需要返回客户的信息 所以还需要customer类
}
//OrderMapper
public interface OrderMapper {
//根据id查询订单信息和订单对应的客户
Order queryOrderById(Integer id);
}
//OrderMapper.xml
<?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.OrderMapper">
<resultMap id="orderMap" type="pojo.Order">
<id column="order_id" property="orderId"/>
<association property="customerInfo" javaType="pojo.Customer">
<id column="customer_id" property="customerId"/>
</association>
</resultMap>
<select id="queryOrderById" resultMap="orderMap">
SELECT * FROM t_order JOIN t_customer ON t_order.customer_id = t_customer.customer_id
WHERE t_order.order_id = #{id}
</select>
</mapper>
//mybatis-config.xml 与后面的3.3共用这个mybatis-config.xml
<?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>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
//开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn
<setting name="mapUnderscoreToCamelCase" value="true"/>
//自动映射任何复杂的结果集(无论是否嵌套)
<setting name="autoMappingBehavior" value="FULL"/>
</settings>
<!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
<environments default="development">
<!-- environment表示配置Mybatis的一个具体的环境 -->
<environment id="development">
<!-- Mybatis的内置的事务管理器 -->
<transactionManager type="JDBC"/>
<!-- 配置数据源 -->
<dataSource type="POOLED">
<!-- 建立数据库连接的具体信息 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/multidb?allowMultiQueries=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- mapper标签:配置一个具体的Mapper映射文件 -->
<!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
<mapper resource="mapper/OrderMapper.xml"/>
<mapper resource="mapper/CustomerMapper.xml"/>
</mappers>
</configuration>
3.3 对多映射
- 需求:根据id查询客户和客户关联的订单信息(单个客户可能有多个订单信息,所以是对多)
- 代码实现
java
//Customer类
public class Customer {
private Integer customerId;
private String customerName;
private List<Order> orderList; //因为需要返回订单信息,是一个列表
}
//CustomerMapper接口
public interface CustomerMapper {
//根据id查询客户和客户关联的订单信息
List<Customer> queryCustomerListById(Integer id);
}
//CustomerMapper.xml
<?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.CustomerMapper">
<resultMap id="customer" type="pojo.Customer">
<id column="customer_id" property="customerId"/>
<collection property="orderList" ofType="pojo.Order">
<id column="order_id" property="orderId"/>
</collection>
</resultMap>
<select id="queryCustomerListById" resultMap="customer">
SELECT * FROM t_customer INNER JOIN t_order ON t_customer.customer_id=t_order.customer_id WHERE t_customer.customer_id=#{id}
</select>
</mapper>
//mbatis-config.xml见3.2代码实现 同
3.4 对一映射和对多映射的说明
- 开启结果集自动映射,就可以省略部分自定义结果集映射时的代码书写
java
//在mybatis-config.xml 开启方式如下
<setting name="autoMappingBehavior" value="FULL"/>
开启后就可以省略result部分的书写,mybatis会自动映射
xml
<resultMap id="orderMap" type="pojo.Order">
<id column="order_id" property="orderId"/>
<!-- <result column="order_name" property="orderName"/>-->
<!-- <result column="customer_id" property="customerId"/>-->
<association property="customerInfo" javaType="pojo.Customer">
<id column="customer_id" property="customerId"/>
<!-- <result column="customer_name" property="customerName"/>-->
</association>
</resultMap>
- 对一映射和对多映射中使用的分别是:
xml
<association property="" javaType="">
</association>
<collection property="" ofType="">
</collection>
- 对orderMapper.xml和customerMapper.xml中对应的说明(颜色相同说明需要保持一致)
4.动态语句
4.1 为什么需要动态语句
在进行按照条件查询的情况下,如果有多个条件,但是有时候有的条件为空,后台需要动态的完成sql语句。例如:淘宝商品筛选中,提供了A-H多个筛选条件,但是客户可能会随机指定几个筛选条件,后台就需要动态的完成sql语句。
4.2 if where标签
- if 标签test=" "判断test是否成立,如果成立则添加if语句内到睡起来语句中
- where标签: where内部有任何一个if满足,自动添加where关键字,不满足去掉where。自动去掉多余的and 和 or 关键字。
java
//接口
List<Employee> queryByCondition(@Param("name") String name, @Param("salary") Double salary);
xml
<select id="queryByCondition" resultType="pojo.Employee">
select emp_id empId,emp_name empName, emp_salary empSalary from t_emp
<where>
<if test="name != null">
emp_name=#{name}
</if>
<if test="salary != null">
and emp_salary=#{salary}
</if>
</where>
</select>
4.3 set标签
set标签自动去掉多余的逗号并自动添加set关键字
java
//接口
int updateInfo(@Param("id") String id, @Param("name") String name, @Param("salary") int salary);
xml
<update id="updateInfo" >
update t_emp
<set>
<if test="name !=null">
emp_name=#{name},
</if>
<if test="salary !=null">
emp_salary=${salary}
</if>
</set>
where emp_id=#{id}
</update>
4.4 choose when otherwise标签
在多个分支条件中,仅执行一个。从上到下依次执行条件判断:遇到的第一个满足条件的分支会被采纳,被采纳分支后面的分支都将不被考虑,如果所有的when分支都不满足,那么就执行otherwise分支。类似switch-case
java
//根据两个条件查询,如果姓名不为null使用姓名查询,如果姓名为null,薪水不为空就使用薪水查询! 都为null查询全部
List<Employee>queryByNameOrSalary(@Param("name") String name, @Param("salary") Double salary);
xml
<select id="queryByNameOrSalary" resultType="pojo.Employee">
select emp_id empId,emp_name empName, emp_salary empSalary from t_emp t_emp
where
<choose>
<when test="name != null">
emp_name = #{name}
</when>
<when test="salary != null">
emp_salary = #{salary}
</when>
<otherwise>1=1</otherwise>
</choose>
</select>
4.5 foreach标签
collection属性:要遍历的集合
item属性:遍历集合的过程中能得到每一个具体对象,在item属性中设置一个名字,将来通过这个名字引用遍历出来的对象
separator属性:指定当foreach标签的标签体重复拼接字符串时,各个标签体字符串之间的分隔符
open属性:指定整个循环把字符串拼好后,字符串整体的前面要添加的字符串
close属性:指定整个循环把字符串拼好后,字符串整体的后面要添加的字符串
index属性:这里起一个名字,便于后面引用
遍历List集合,这里能够得到List集合的索引值
遍历Map集合,这里能够得到Map集合的key
xml
<foreach collection="empList" item="emp" separator="," open="values" index="myIndex">
<!-- 在foreach标签内部如果需要引用遍历得到的具体的一个对象,需要使用item属性声明的名称 -->
(#{emp.empName},#{myIndex},#{emp.empSalary},#{emp.empGender})
</foreach>
5.MyBatis高级扩展
5.1 批量映射
在myBatis.config.xml的配置文件中,如果有多个Mapper,通常需要进行多次映射,如下所示:
可以直接在配置文件中使用以下代码代替:
xml
<mappers>
<package name="com.daoheng.mapper"/>
</mappers>
但是必须满足以下要求:
- Mapper 接口和 Mapper 配置文件名称一致:EmployeeMapper.java对应EmployeeMapper.xml
- 在Rsources下创建mapper接口包一致的文件夹结构 存放mapper.xml文件
5.2 分页插件 pageHelper
- pom.xml引入依赖
xml
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.11</version>
</dependency>
- mybatis-config.xml配置分页插件
xml
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
</plugin>
</plugins>
- 测试代码
java
@Test
public void testTeacherRelationshipToMulti() {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
PageHelper.startPage(2,2);
List<Employee> employees = mapper.queryBySalary(500.0);
PageInfo<Employee> pageInfo = new PageInfo<>(employees);
System.out.println("pageInfo = " + pageInfo);
long total = pageInfo.getTotal(); // 获取总记录数
System.out.println("total = " + total);
int pages = pageInfo.getPages(); // 获取总页数
System.out.println("pages = " + pages);
int pageNum = pageInfo.getPageNum(); // 获取当前页码
System.out.println("pageNum = " + pageNum);
int pageSize = pageInfo.getPageSize(); // 获取每页显示记录数
System.out.println("pageSize = " + pageSize);
List<Employee> list = pageInfo.getList();//获取查询页的数据集合
System.out.println("teachers = " + list);
employees.forEach(System.out::println);
}
//输出
pageInfo = PageInfo{pageNum=2, pageSize=2, size=2, startRow=3, endRow=4, total=4, pages=2, list=Page{count=true, pageNum=2, pageSize=2, startRow=2, endRow=4, total=4, pages=2, reasonable=false, pageSizeZero=false}[Employee{empId=3, empName='andy', empSalary=500.0}, Employee{empId=4, empName='tom', empSalary=500.0}], prePage=1, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true, hasNextPage=false, navigatePages=8, navigateFirstPage=1, navigateLastPage=2, navigatepageNums=[1, 2]}
total = 4
pages = 2
pageNum = 2
pageSize = 2
teachers = Page{count=true, pageNum=2, pageSize=2, startRow=2, endRow=4, total=4, pages=2, reasonable=false, pageSizeZero=false}[Employee{empId=3, empName='andy', empSalary=500.0}, Employee{empId=4, empName='tom', empSalary=500.0}]
Employee{empId=3, empName='andy', empSalary=500.0}
Employee{empId=4, empName='tom', empSalary=500.0}
5.3 MybatisX插件
- ORM:ORM(Object-Relational Mapping,对象-关系映射)是一种将数据库和面向对象编程语言中的对象之间进行转换的技术。它将对象和关系数据库的概念进行映射,最后我们就可以通过方法调用进行数据库操作
- 逆向工程:逆向工程是一种自动化生成持久层代码和映射文件的工具,它可以根据数据库表结构和设置的参数生成对应的实体类、Mapper.xml 文件、Mapper 接口等代码文件
- MybatisX插件能完成逆向工程,但是只能生成单表crud的操作,多表查询依然需要我们自己编写
- 步骤