思途Mybatis学习 0805

一、MyBatis 简介

  • 定义 :MyBatis 是一个 半自动化的 ORM(Object-Relational Mapping)框架,用于将 Java 对象与数据库记录进行映射。
  • 特点
    • 轻量级、灵活、易于掌握。
    • SQL 与代码分离,支持动态 SQL。
    • 相比 Hibernate 等全自动 ORM 框架,MyBatis 提供更高的 SQL 控制能力。

二、Spring Boot 整合 MyBatis 步骤

1. 添加依赖

pom.xml 中引入 MyBatis 启动器:

复制代码

xml

深色版本

复制代码
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.3</version> <!-- 推荐使用最新稳定版本 -->
</dependency>

⚠️ 注意:该依赖会自动包含 mybatismybatis-spring 和自动配置模块。


2. 配置文件设置(application.yml)

复制代码
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  type-aliases-package: com.example.demo.entity    # 别名包路径
  mapper-locations: classpath:mapper/*.xml         # XML 映射文件位置
  configuration:
    map-underscore-to-camel-case: true             # 开启驼峰命名自动映射 a_column -> aColumn

3. 编写 Mapper 接口

使用 @Mapper 注解标记接口,或在启动类上加 @MapperScan 扫描包。

复制代码
@Mapper
public interface UserMapper {
    User findById(Long id);
}

或在主启动类上:

复制代码
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

4. 编写映射 XML 文件(Mapper XML)

文件路径:resources/mapper/UserMapper.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="com.example.demo.mapper.UserMapper">

    <select id="findById" resultType="User">
        SELECT * FROM user WHERE id = #{id}
    </select>

</mapper>

✅ 注意:

  • namespace 必须对应 Mapper 接口的全限定名。
  • id 在同一个 XML 中必须唯一,且与接口方法名一致。
  • #{} 是预编译占位符,防止 SQL 注入;${} 是字符串拼接,需谨慎使用。

5. 安装 MyBatis-X 插件(推荐)

IntelliJ IDEA 插件:MyBatisX

  • 快速跳转:Mapper 接口 ↔ XML 映射文件。
  • 自动生成 CRUD 方法和 SQL 片段。
  • 提高开发效率。

三、动态 SQL 与参数传递

MyBatis 使用 OGNL(Object-Graph Navigation Language)表达式 解析参数,通过反射获取对象属性值。

1. 单个参数(POJO 或 Map)

可以直接通过属性名访问:

复制代码
public User findByName(User user); // user.getName()

<select id="findByName" resultType="User">
    SELECT * FROM user WHERE name = #{name}
</select>

#{name} 自动从 User 对象中调用 getName() 获取值。


2. 多个参数(必须指定参数名)

Java 方法:

复制代码
List<User> findUsers(@Param("name") String name, @Param("age") int age);

XML 使用:

复制代码
<select id="findUsers" resultType="User">
    SELECT * FROM user
    WHERE name LIKE #{name} AND age > #{age}
</select>

❗ 若不使用 @Param,多个参数需用 param1, param2arg0, arg1 引用,可读性差,强烈建议使用 @Param 显式命名


3. 使用 <bind> 标签构造新变量

用于模糊查询等场景:

复制代码
<select id="findByNameLike" parameterType="string" resultType="User">
    <bind name="pattern" value="'%' + _parameter + '%'" />
    SELECT * FROM user WHERE name LIKE #{pattern}
</select>

或:

复制代码
<bind name="nameLike" value="'%'+name+'%'"/>
AND name LIKE #{nameLike}

🔍 说明:_parameter 表示整个参数对象。若参数是简单类型(如 String),则直接可用。


4. ${} vs #{}

方式 是否预编译 是否防注入 适用场景
#{} ✅ 是 ✅ 安全 绝大多数情况(推荐)
${} ❌ 否 ❌ 易被注入 动态表名、排序字段等

示例:

复制代码
ORDER BY ${columnName}  <!-- 只能用 ${} -->

⚠️ 使用 ${} 时务必校验输入合法性!


四、结果映射(ResultMap)

1. 手动映射字段(解决列名与属性不一致)

复制代码
<resultMap id="userResultMap" type="User">
    <id property="id" column="user_id"/>
    <result property="name" column="user_name"/>
    <result property="email" column="user_email"/>
</resultMap>

<select id="findAll" resultMap="userResultMap">
    SELECT user_id, user_name, user_email FROM user
</select>

2. 关联查询(一对一、一对多)

(1)一对一关联

例如:User ↔ Profile

复制代码
<resultMap id="userWithProfileMap" type="User">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <association property="profile" javaType="Profile">
        <id property="id" column="pid"/>
        <result property="bio" column="bio"/>
    </association>
</resultMap>

<select id="findUserWithProfile" resultMap="userWithProfileMap">
    SELECT u.id, u.name, p.id AS pid, p.bio
    FROM user u
    LEFT JOIN profile p ON u.id = p.user_id
    WHERE u.id = #{id}
</select>

(2)一对多关联

例如:Department ↔ List<Employee>

复制代码
<resultMap id="deptWithEmpsMap" type="Department">
    <id property="id" column="dept_id"/>
    <result property="name" column="dept_name"/>
    <collection property="employees" ofType="Employee">
        <id property="id" column="emp_id"/>
        <result property="name" column="emp_name"/>
    </collection>
</resultMap>

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

五、N+1 查询问题与性能优化

1. 什么是 N+1 问题?

当使用 associationcollection 延迟加载时,MyBatis 可能先查主表(1次),再对每条记录发起关联查询(N次),共 N+1 次 SQL。

2. 解决方案

✅ 方案一:使用 联合查询 + 嵌套映射(推荐)

如上一对多示例,一次性 JOIN 查询,避免 N+1。

✅ 方案二:开启 延迟加载(Lazy Loading)
复制代码
mybatis:
  configuration:
    lazy-loading-enabled: true
    aggressive-lazy-loading: false  # false:仅加载被调用的属性

⚠️ 延迟加载会创建新会话,一级缓存失效

✅ 方案三:使用 二级缓存

提升跨会话的数据复用性。

开启步骤:
  1. 全局开启缓存:

    mybatis:
    configuration:
    cache-enabled: true

  2. 在 Mapper XML 中声明缓存:

    <mapper namespace="com.example.mapper.UserMapper"> <cache/>
    复制代码
     <select id="findById" useCache="true" resultType="User">
         SELECT * FROM user WHERE id = #{id}
     </select>
    
     <update id="updateUser" flushCache="true">
         UPDATE user SET name = #{name} WHERE id = #{id}
     </update>
    </mapper>
  • useCache="true":查询结果放入二级缓存(默认 select 为 true)。
  • flushCache="true":执行后清空缓存(默认 insert/update/delete 为 true)。
  • 实体类需实现 Serializable 接口。

六、自动分页(集成 PageHelper)

1. 添加依赖

复制代码
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.7</version>
</dependency>

2. 使用

复制代码
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public PageInfo<User> getUsers(int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<User> users = userMapper.findAll();
        return new PageInfo<>(users);
    }
}

返回 JSON 示例:

复制代码
{
  "pageNum": 1,
  "pageSize": 10,
  "total": 100,
  "pages": 10,
  "list": [...]
}

七、补充知识点

1. @RestController@ResponseBody

  • @RestController = @Controller + @ResponseBody
  • 所有方法返回值直接作为响应体(JSON/XML),无需额外标注 @ResponseBody

2. 类型处理器(TypeHandler)

自定义 Java 类型与数据库类型的转换,如枚举、LocalDateTime 等。

复制代码
@MappedTypes(LocalDate.class)
@MappedJdbcTypes(JdbcType.DATE)
public class LocalDateTypeHandler extends BaseTypeHandler<LocalDate> {
    // 实现 setParameter、getResult 方法
}

注册方式:

复制代码
@Configuration
public class MyBatisConfig {
    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> configuration.getTypeHandlerRegistry()
            .register(LocalDate.class, new LocalDateTypeHandler());
    }
}

八、总结

功能 推荐做法
参数传递 多参数用 @Param 显式命名
模糊查询 <bind> 构造 pattern,配合 #{}
字段映射 列名转驼峰开启 map-underscore-to-camel-case
关联查询 优先使用 JOIN + resultMap 避免 N+1
性能优化 合理使用一级/二级缓存、延迟加载
分页 集成 PageHelper
开发效率 安装 MyBatisX 插件
相关推荐
Chan161 小时前
【智能协同云图库】第七期:基于AI调用阿里云百炼大模型,实现AI图片编辑功能
java·人工智能·spring boot·后端·spring·ai·ai作画
sakabu1 小时前
cJSON库应用
c语言·笔记·学习
hrrrrb2 小时前
【Spring Boot 快速入门】六、配置文件
java·spring boot·intellij-idea
心勤则明3 小时前
JVM(Java虚拟机)运行时数据区
java·jvm·chrome
大阳1233 小时前
数据结构2.(双向链表,循环链表及内核链表)
c语言·开发语言·数据结构·学习·算法·链表·嵌入式
皮皮林5513 小时前
多账号统一登录(实现方案)
java
越来越无动于衷3 小时前
智慧社区(八)——社区人脸识别出入管理系统设计与实现
java·开发语言·spring boot·python·mysql
Mr Aokey4 小时前
注解退散!纯XML打造MyBatis持久层的终极形态
xml·java·mybatis
向日葵花子(* ̄︶ ̄)4 小时前
Eclipse中导入新项目,右键项目没有Run on Server,Tomcat的add and remove找不到项目
java·eclipse