Mybatis的应用及部分特性

初识Mybatis

Mybatis的概念

        MyBatis 是一个Java 持久层框架,核心作用是简化数据库操作,把 SQL 和 Java 代码解耦。

ORM框架

        MyBatis是一个ORM 框架

        所谓ORM 框架,就是把数据库里的表、字段、关系,映射成编程语言里的类、属性、对象引用,从而让我们用“面向对象”的方式去操作关系型数据库,而不用写大量繁复的 JDBC/SQL。

        简而言之,半自动化ORM,将SQL与Java对象映射,不自动生成SQL,只负责把“你写的 SQL”和“你写的 Java 对象”精准地对接起来,半自动化 ORM 是“SQL 与 Java 对象之间的翻译官”。

核心组件

SqlSessionFactory

       作用:  MyBatis 的核心工厂,负责创建和管理  SqlSession  实例。

特点

  • 全局单例:在整个应用中只需要一个实例,线程安全。
  • 初始化:加载 MyBatis 配置文件、解析映射文件,初始化数据库连接池等资源。

功能: 通过  openSession()  方法创建  SqlSession  实例,用于执行 SQL 操作。


SqlSession

作用:MyBatis 的一个会话对象,用于执行 SQL 操作。

特点

  • 线程不安全:每次请求或操作数据库时需要创建一个新的实例,用完后必须关闭。
  • 功能:提供  crud等方法,用于执行 SQL 语句。它还管理事务(提交或回滚)。
  • 生命周期:通常与一个请求或一个业务操作绑定,用完即关闭。
Executor

作用:它是 SqlSession的底层执行器,负责执行 SQL 语句。

类型

  • Simple:每次执行 SQL 时都创建新的 Statement,适合简单场景。
  • Reuse:复用 Statement,减少 Statement 的创建和销毁开销。
  • Batch:批量执行 SQL,适合批量插入或更新操作,可以减少数据库交互次数。

功能:负责执行 SQL 语句,管理 Statement 的生命周期,以及维护一级缓存。


MapperProxy

作用:它是 MyBatis 的动态代理机制,用于实现 Mapper 接口。

特点

  • 动态代理:通过 JDK 动态代理技术,为 Mapper 接口生成代理实例。
  • 功能:将 Mapper 接口的方法调用转换为具体的 SQL 执行操作。
  • 解耦:让开发者只需要关注 Mapper 接口的定义,而不需要关心底层的 SQL 执行细节。

配置文件

此处介绍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>
<!--    配置mybatis的环境-->
    <environments default="mysql">
<!--        id和default的值必须保持一致,接下来配置连接mysql的具体信息-->
        <environment id="mysql">
<!--            配置事务类型  JDBC-->
            <transactionManager type="JDBC"></transactionManager>
<!--            配置数据源  它的取值有POOLED 和 UNPOOLED  前者是mysql提供的默认数据源进行数据库的连接
后者是不使用数据源,多次与数据库进行交互-->
            <dataSource type="POOLED">
<!--                配置连接数据库的驱动 url 用户名和密码-->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
<!--    在核心配置文件里要导入接口的映射文件-->
    <mappers>
        <mapper resource="com/noy/dao/UserDao.xml"></mapper>
    </mappers>
</configuration>

Mapper XML 文件

若要通过xml来执行SQL操作,那么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">
<!--
    namespace  命名空间  值必须唯一 一般写接口的全限定名即可
-->
<mapper namespace="com.noy.dao.UserDao">
<!--
select标签  就是一个执行查询操作的标签
id必须和接口方法名称一样,才可以保持映射成功
parameterType参数类型的意思,这里不传参可以不写
resultType 是接口的返回值   如果是泛型 ,写泛型的全限定名
-->
    <select id="findAll" resultType="com.noy.pojo.User">
        SELECT * FROM user
    </select>
</mapper>

搭建mybatis入门案例的步骤

  1. 导入mybatis相关的依赖
  2. 创建pojo,将pojo和数据表进行关联映射  定义接口
  3. 创建mybatis的核心配置文件sqlMapConfig.xml
  4. 创建接口的映射文件    接口所在的目录  和接口文件所在的目录保持一致
  5. 编写测试代码

SQL映射

        本质上是解耦 SQL 与 Java 代码,通过声明式配置实现数据库操作

基础 CRUD 标签

标签核心属性
<select>idresultType/resultMap
<insert>idparameterType
<update>idparameterType
<delete>idparameterType

id  必须与 Mapper 接口方法同名,MyBatis 就靠这个把接口方法与 SQL 绑定在一起。

参数 & 占位符

#{}表示一个占位符号 通过#{}可以实现 preparedStatement 向占位符中设置值,自动进行 java 类型和 jdbc 类型转换,
#{}可以有效防止 sql 注入。 #{}可以接收简单类型值或 pojo 属性值。
   如果 parameterType 传输单个简单类 型值,#{}括号中可以是 value 或其它名称。

${}表示拼接 sql 串 通过${}可以将 parameterType 传入的内容拼接在 sql 中且不进行 jdbc 类型转换,
${}可以接收简单类型值或 pojo 属性值,如果 parameterType传输单个简单类型值,${}括号中只能是value。

总而言之,安全用  #{} ,拼接才用  ${} 

列表演示:

特性#{} (预编译占位符)${} (字符串替换)
SQL 安全防止 SQL 注入 ✅有注入风险 ❗
类型处理自动类型转换 (Java→JDBC)直接字符串替换
使用场景值参数(WHERE 条件值)动态表名/列名
日志输出? 占位符替换后的完整 SQL

返回结果

resultType :简单场景,直接写全限定类名或别名,列名与属性名一致即可自动映射。
 例: <select id="getById" resultType="com.noy.User"> 

resultMap :复杂场景,自定义映射规则(列名≠属性名、一对多、枚举转换等)。
 例: <resultMap id="userMap" type="User"><result column="user_name" property="name"/></resultMap> 

主键回填(insert 专属)

 useGeneratedKeys="true"  开启 JDBC 自动生成主键回填。

keyProperty="id"  指定把数据库生成的主键值塞进参数对象的哪个属性。

<insert id="save" parameterType="User"
        useGeneratedKeys="true" keyProperty="id">
    INSERT INTO user(name) VALUES(#{name})
</insert>

插入完成后, User#getId()  就能直接拿到新主键。

多级映射

        一个人可能有不同的身份,不同的人可能有同一种身份,不同的人也可能会有不同的身份,所以映射也是如此

关系类型数据库表现对象关系示例MyBatis 标签使用场景
一对一主表外键指向关联表主键用户 ↔ 身份证<association>用户详情页显示身份证信息
一对多主表主键出现在关联表的外键字段部门 ↔ 员工<collection>部门页面显示下属员工列表
多对多通过中间表维护两个主表的关系学生 ↔ 课程两个<collection>学生选课系统/课程学生名单

一对一

        SQL

CREATE TABLE user (
  id INT PRIMARY KEY,
  name VARCHAR(50)
);

CREATE TABLE id_card (
  id INT PRIMARY KEY,
  card_number VARCHAR(20),
  user_id INT UNIQUE,  -- 唯一外键
  FOREIGN KEY (user_id) REFERENCES user(id)
);

        Java

public class User {
  private Integer id;
  private String name;
  private IdCard idCard; // 一对一关联对象
}

public class IdCard {
  private String cardNumber;
}

        MyBatis 映射

<resultMap id="userMap" type="User">
  <id property="id" column="user_id"/>
  <result property="name" column="user_name"/>
  
  <!-- 一对一映射 -->
  <association property="idCard" javaType="IdCard">
    <result property="cardNumber" column="card_number"/>
  </association>
</resultMap>

<select id="getUserWithCard" resultMap="userMap">
  SELECT 
    u.id AS user_id, 
    u.name AS user_name,
    c.card_number
  FROM user u
  JOIN id_card c ON u.id = c.user_id
  WHERE u.id = #{id}
</select>
一对多

        SQL

CREATE TABLE department (
  id INT PRIMARY KEY,
  name VARCHAR(50)
);

CREATE TABLE employee (
  id INT PRIMARY KEY,
  name VARCHAR(50),
  dept_id INT,  -- 非唯一外键
  FOREIGN KEY (dept_id) REFERENCES department(id)
);

        Java

public class Department {
  private Integer id;
  private String name;
  private List<Employee> employees; // 一对多关联集合
}

public class Employee {
  private String name;
}

        MyBatis 映射

<resultMap id="deptMap" type="Department">
  <id property="id" column="dept_id"/>
  <result property="name" column="dept_name"/>
  
  <!-- 一对多映射 -->
  <collection property="employees" ofType="Employee">
    <result property="name" column="emp_name"/>
  </collection>
</resultMap>

<select id="getDeptWithEmployees" resultMap="deptMap">
  SELECT 
    d.id AS dept_id,
    d.name AS dept_name,
    e.name AS emp_name
  FROM department d
  LEFT JOIN employee e ON d.id = e.dept_id
  WHERE d.id = #{id}
</select>
多对多

        SQL

CREATE TABLE student (
  id INT PRIMARY KEY,
  name VARCHAR(50)
);

CREATE TABLE course (
  id INT PRIMARY KEY,
  name VARCHAR(50)
);

-- 中间表
CREATE TABLE student_course (
  student_id INT,
  course_id INT,
  PRIMARY KEY (student_id, course_id),
  FOREIGN KEY (student_id) REFERENCES student(id),
  FOREIGN KEY (course_id) REFERENCES course(id)
);

        Java

public class Student {
  private Integer id;
  private String name;
  private List<Course> courses; // 多对多关联
}

public class Course {
  private String name;
  private List<Student> students; // 反向关联(可选)
}

       MyBatis 映射

<resultMap id="studentMap" type="Student">
  <id property="id" column="student_id"/>
  <result property="name" column="student_name"/>
  
  <!-- 多对多映射(通过中间表) -->
  <collection property="courses" ofType="Course">
    <result property="name" column="course_name"/>
  </collection>
</resultMap>

<select id="getStudentWithCourses" resultMap="studentMap">
  SELECT 
    s.id AS student_id,
    s.name AS student_name,
    c.name AS course_name
  FROM student s
  JOIN student_course sc ON s.id = sc.student_id
  JOIN course c ON sc.course_id = c.id
  WHERE s.id = #{id}
</select>

实际上,一对多/多对多使用<collection>时,大数据量建议开启延迟加载,具体操作见下列映射策略

映射策略

  • 自动映射:配置 autoMappingBehavior (PARTIAL/FULL/NONE)

  • 嵌套查询<association select="queryDeptById"> + column="dept_id"

  • 延迟加载:在全局启用 lazyLoadingEnabled=true

<settings>
    <!-- 开启全局延迟加载 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 关闭积极加载(3.4.1+ 默认为 false,高于此版本的mybatis可以不写) -->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

动态 SQL用法

其目的为

  • 动态生成 SQL:根据参数条件自动拼接不同 SQL 片段

  • 替代硬编码:避免在 Java 中手动拼接 SQL 字符串

  • 智能去除多余关键字(AND/OR/)

  • 用声明式 XML 标签代替过程式代码拼接,使 SQL 更清晰、更安全、更易维护。

核心标签为

标签作用常用场景
<if>条件判断按需添加 WHERE 条件
<choose>多选一(类似 switch-case)多种查询方案选择
<where>智能处理 WHERE 子句自动去除开头多余的 AND/OR
<set>智能处理 UPDATE 的 SET 子句自动去除尾部逗号
<foreach>遍历集合IN 查询、批量插入

它们的用法语法也类似于Java

基础条件控制 <if>

<select id="search">
  SELECT * FROM users
  <where>
    <!-- 姓名不为空时添加条件 -->
    <if test="name != null">
      AND name = #{name}
    </if>
    
    <!-- 年龄大于0时添加条件 -->
    <if test="age > 0">
      AND age > #{age}
    </if>
  </where>
</select>

 多分支选择 <choose>

<select id="getData">
  SELECT * FROM products
  <where>
    <choose>
      <!-- 优先按ID查 -->
      <when test="id != null">
        id = #{id}
      </when>
      
      <!-- 其次按名称查 -->
      <when test="name != null">
        name LIKE #{name}
      </when>
      
      <!-- 默认查上架商品 -->
      <otherwise>
        status = 'ON_SALE'
      </otherwise>
    </choose>
  </where>
</select>

智能更新 <set>

<update id="updateUser">
  UPDATE users
  <set>
    <!-- 动态更新字段 -->
    <if test="name != null">name=#{name},</if>
    <if test="email != null">email=#{email},</if>
    update_time = NOW() <!-- 固定更新 -->
  </set>
  WHERE id = #{id}
</update>

集合遍历 <foreach>

<!-- IN 查询 -->
<select id="findByIds">
  SELECT * FROM users
  WHERE id IN
  <foreach item="id" collection="ids" 
           open="(" separator="," close=")">
    #{id}
  </foreach>
</select>

<!-- 批量插入 -->
<insert id="batchInsert">
  INSERT INTO users (name, age)
  VALUES
  <foreach item="user" collection="list" separator=",">
    (#{user.name}, #{user.age})
  </foreach>
</insert>

此外,在动态SQL中,WHERE条件 → 用 <where> 标签,UPDATE 操作 → 用 <set> 标签

空集合处理

<!-- 检查集合非空 -->
<if test="list != null and !list.isEmpty()">
  ...
</if>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值