MyBatis 核心知识与实践
一、MyBatis 概述
1. 框架简介
MyBatis 是一款支持自定义 SQL、存储过程以及高级映射的持久层框架。它避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的操作,使开发人员能够更专注于 SQL 语句的编写和业务逻辑的处理。
2. 核心组件
- SqlSessionFactoryBuilder:用于创建 SqlSessionFactory 实例。
- SqlSessionFactory:是 MyBatis 的核心对象,负责创建 SqlSession 实例。
- SqlSession:提供了执行 SQL 语句、管理事务等功能。
3. 工作原理
MyBatis 通过读取配置文件和映射文件,将 SQL 语句与 Java 方法进行映射。在运行时,根据方法调用生成相应的 SQL 语句,并执行数据库操作,最后将结果映射为 Java 对象返回。
二、MyBatis 基础操作
1. 环境搭建
- 引入 MyBatis 依赖(以 Maven 项目为例):
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>x.x.x</version>
</dependency>
- 配置
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>
<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>
<mappers>
<mapper resource="mapper/EmpMapper.xml" />
<mapper resource="mapper/DeptMapper.xml" />
</mappers>
</configuration>
2. 单表操作
- 实体类定义:
public class Emp {
private Integer empid;
private String empname;
private String empjob;
private BigDecimal empsalary;
private Integer empdid;
// 省略getter和setter方法
}
- Mapper 接口定义:
public interface EmpDao {
List<Emp> findAll();
Emp findById(Integer id);
int insert(Emp emp);
int update(Emp emp);
int delete(Integer id);
}
- Mapper 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="org.example.Dao.EmpDao">
<resultMap id="BaseResultMap" type="Emp">
<id property="empid" column="emp_id" jdbcType="INTEGER" />
<result property="empname" column="emp_name" jdbcType="VARCHAR" />
<result property="empjob" column="emp_job" jdbcType="VARCHAR" />
<result property="empsalary" column="emp_salary" jdbcType="DECIMAL" />
<result property="empdid" column="did" jdbcType="INTEGER" />
</resultMap>
<select id="findAll" resultMap="BaseResultMap">
select * from tbl_emp
</select>
<select id="findById" resultMap="BaseResultMap">
select * from tbl_emp where emp_id = #{id}
</select>
<insert id="insert" keyProperty="empid" useGeneratedKeys="true">
insert into tbl_emp(emp_name, emp_job, emp_salary, did)
values (#{empname}, #{empjob}, #{empsalary}, #{empdid})
</insert>
<update id="update">
update tbl_emp
set emp_name = #{empname}, emp_job = #{empjob}, emp_salary = #{empsalary}, did = #{empdid}
where emp_id = #{empid}
</update>
<delete id="delete">
delete from tbl_emp where emp_id = #{id}
</delete>
</mapper>
3. 测试类编写
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.example.Entity.Emp;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class EmpDaoTest {
@Test
public void testFindAll() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpDao empDao = sqlSession.getMapper(EmpDao.class);
List<Emp> emps = empDao.findAll();
for (Emp emp : emps) {
System.out.println(emp);
}
sqlSession.close();
}
@Test
public void testFindById() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpDao empDao = sqlSession.getMapper(EmpDao.class);
Emp emp = empDao.findById(1);
System.out.println(emp);
sqlSession.close();
}
@Test
public void testInsert() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpDao empDao = sqlSession.getMapper(EmpDao.class);
Emp emp = new Emp();
emp.setEmpname("张三");
emp.setEmpjob("开发工程师");
emp.setEmpsalary(new BigDecimal("8000"));
emp.setEmpdid(1);
int result = empDao.insert(emp);
System.out.println("插入成功,影响行数:" + result);
sqlSession.commit();
sqlSession.close();
}
@Test
public void testUpdate() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpDao empDao = sqlSession.getMapper(EmpDao.class);
Emp emp = new Emp();
emp.setEmpid(1);
emp.setEmpname("李四");
emp.setEmpjob("高级开发工程师");
emp.setEmpsalary(new BigDecimal("10000"));
emp.setEmpdid(2);
int result = empDao.update(emp);
System.out.println("更新成功,影响行数:" + result);
sqlSession.commit();
sqlSession.close();
}
@Test
public void testDelete() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpDao empDao = sqlSession.getMapper(EmpDao.class);
int result = empDao.delete(1);
System.out.println("删除成功,影响行数:" + result);
sqlSession.commit();
sqlSession.close();
}
}
三、MyBatis 高级特性
1. 分页查询
- 引入 PageHelper 依赖:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>6.0.0</version>
</dependency>
- 在 MyBatis 配置文件中添加插件配置:
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 可配置参数 -->
<property name="param1" value="value1" />
</plugin>
</plugins>
- 代码示例:
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
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.example.Dao.EmpDao;
import org.example.Entity.Emp;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class PaginationTest {
@Test
public void testPagination() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
EmpDao empDao = sqlSession.getMapper(EmpDao.class);
// 开启分页,查询第一页,每页显示3条数据
PageHelper.startPage(1, 3);
List<Emp> emps = empDao.findAll();
// 将查询结果封装到PageInfo中
PageInfo<Emp> pageInfo = new PageInfo<>(emps);
// 输出相关信息
System.out.println("总条数:" + pageInfo.getTotal());
System.out.println("总页数:" + pageInfo.getPages());
System.out.println("当前页记录:");
List<Emp> list = pageInfo.getList();
for (Emp emp : list) {
System.out.println(emp);
}
sqlSession.close();
}
}
2. 联表查询
- 多对一关系查询(以员工和部门为例):
- 使用
association
标签:
<?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="org.example.Dao.EmpDao">
<resultMap id="EmpWithDeptResultMap" type="Emp">
<id property="empid" column="emp_id" jdbcType="INTEGER" />
<result property="empname" column="emp_name" jdbcType="VARCHAR" />
<result property="empjob" column="emp_job" jdbcType="VARCHAR" />
<result property="empsalary" column="emp_salary" jdbcType="DECIMAL" />
<result property="empdid" column="did" jdbcType="INTEGER" />
<association property="dept" javaType="Dept">
<id property="deptid" column="dept_id" jdbcType="INTEGER" />
<result property="deptname" column="dept_name" jdbcType="VARCHAR" />
<result property="deptloc" column="dept_loc" jdbcType="VARCHAR" />
</association>
</resultMap>
<select id="findAllWithDept" resultMap="EmpWithDeptResultMap">
select e.*, d.dept_id, d.dept_name, d.dept_loc
from tbl_emp e
join tbl_dept d on e.did = d.dept_id
</select>
</mapper>
- 一对多关系查询(以部门和员工为例):
- 使用
collection
标签:
<?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="org.example.Dao.DeptDao">
<resultMap id="DeptWithEmpsResultMap" type="Dept">
<id property="deptid" column="dept_id" jdbcType="INTEGER" />
<result property="deptname" column="dept_name" jdbcType="VARCHAR" />
<result property="deptloc" column="dept_loc" jdbcType="VARCHAR" />
<collection property="emps" ofType="Emp">
<id property="empid" column="emp_id" jdbcType="INTEGER" />
<result property="empname" column="emp_name" jdbcType="VARCHAR" />
<result property="empjob" column="emp_job" jdbcType="VARCHAR" />
<result property="empsalary" column="emp_salary" jdbcType="DECIMAL" />
<result property="empdid" column="did" jdbcType="INTEGER" />
</collection>
</resultMap>
<select id="findAllWithEmps" resultMap="DeptWithEmpsResultMap">
select d.*, e.*
from tbl_dept d
left join tbl_emp e on d.dept_id = e.did
</select>
</mapper>
3. 动态 SQL
<trim>:通过修剪 SQL 语句的开头和结尾来动态生成 SQL 片段。它可以用于去除不必要的 SQL 关键字或条件语句,并提供了一些属性来定义修剪规则。
<where>:用于在生成的 SQL 语句中添加 WHERE 子句。它可以自动处理条件语句的前缀,并在有条件语句存在时添加 WHERE 关键字。而且会去除sql的第一个and标签。
<set>:用于在生成的 SQL 语句中添加 SET 子句。它主要用于更新操作,可以根据条件来动态生成需要更新的列。
<foreach>:用于在生成的 SQL 语句中进行循环操作。它可以遍历集合或数组,并根据指定的模板将集合元素或数组元素插入到 SQL 语句中。
<if>:用于在生成的 SQL 语句中添加条件判断。可以根据指定的条件决定是否包含某个 SQL 语句片段。
<choose>:类似于 Java 中的 switch 语句,根据条件选择执行不同的 SQL 语句片段。它可以包含多个 <when> 和一个可选的 <otherwise> 标签。
<when>:用于在 <choose> 标签中定义条件分支。可以根据指定的条件判断是否执行特定的 SQL 语句片段。
<otherwise>:在 <choose> 标签中可选的标签,用于定义当没有任何 <when> 条件匹配时执行的 SQL 语句片段。
if
标签示例(根据条件查询员工):
<?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="org.example.Dao.EmpDao">
<select id="selectByCondition" resultMap="BaseResultMap">
select * from tbl_emp
<where>
<if test="name!= null and name!= ''">
and emp_name like concat('%', #{name}, '%')
</if>
<if test="job!= null and job!= ''">
and emp_job = #{job}
</if>
<if test="salary!= null">
and emp_salary = #{salary}
</if>
</where>
</select>
</mapper>
choose
、when
、otherwise
标签示例(根据条件查询员工):
<?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="org.example.Dao.EmpDao">
<select id="selectByCondition2" resultMap="BaseResultMap">
select * from tbl_emp
<where>
<choose>
<when test="name!= null and name!= ''">
and emp_name like concat('%', #{name}, '%')
</when>
<when test="job!= null and job!= ''">
and emp_job = #{job}
</when>
<when test="salary!= null">
and emp_salary = #{salary}
</when>
<otherwise>
and 1 = 1
</otherwise>
</choose>
</where>
</select>
</mapper>
foreach
标签示例(批量删除员工):
<?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="org.example.Dao.EmpDao">
<delete id="batchDelete">
delete from tbl_emp where emp_id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
</mapper>