Spring Boot 整合 MyBatis 详细教程(含 CRUD、注解、分页插件等)

引言

在 Java 开发中,Spring Boot 与 MyBatis 的结合是实现高效、灵活数据库操作的常用方案。本文将详细介绍如何在 Spring Boot 项目中整合 MyBatis,并涵盖 CRUD 操作、注解使用、分页插件集成、结果映射和自动映射等内容。

一、环境准备


开发工具:推荐使用 IntelliJ IDEA 或 Eclipse。
数据库:以 MySQL 为例,确保本地已安装 MySQL 数据库并创建好测试数据库。
项目结构:典型的 Spring Boot 项目结构包括 Controller、Service、Mapper 和 Entity 等层。

二、创建 Spring Boot 项目

  1. 使用 Spring Initializr:访问 Spring Initializr,选择以下依赖:

    • Spring Web

    • MyBatis Framework

    • MySQL Driver

    • Lombok(可选,用于简化实体类开发)

  2. 生成项目:点击“Generate”按钮下载项目压缩包,解压后导入到开发工具中。

三、添加 Maven 依赖

pom.xml 文件中添加以下依赖:

<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <!-- MyBatis Starter -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>3.0.0</version>
    </dependency>

    <!-- MySQL Driver -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

四、配置数据库连接


在 src/main/resources/application.yml 文件中配置数据库连接信息和 MyBatis 相关配置: 

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false&serverTimezone=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  mapper-locations: classpath:mapper/*.xml # 映射文件路径
  type-aliases-package: com.example.demo.model # 实体类包路径
  • url:数据库连接地址。

  • usernamepassword:数据库的用户名和密码。

  • mapper-locations:指定 MyBatis 的 XML 映射文件路径。

  • type-aliases-package:指定实体类所在的包。

五、创建项目结构

1. 实体类(Entity)

创建 User 实体类,用于映射数据库表 user

package com.example.demo.entity;

import lombok.Data;

@Data
public class User {
    private Long id;
    private String username;
    private String password;
    private String email;
}

2. Mapper 接口

创建 UserMapper 接口,定义数据库操作方法。

package com.example.demo.mapper;

import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
    User getUserById(Long id);
    void insertUser(User user);
    void updateUser(User user);
    void deleteUser(Long id);
}

3. Mapper XML 文件

src/main/resources/mapper 目录下创建 UserMapper.xml 文件,编写 SQL 映射。

<?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="getUserById" parameterType="long" resultType="com.example.demo.entity.User">
        SELECT * FROM user WHERE id = #{id}
    </select>
    <insert id="insertUser" parameterType="com.example.demo.entity.User">
        INSERT INTO user (username, password, email) VALUES (#{username}, #{password}, #{email})
    </insert>
    <update id="updateUser" parameterType="com.example.demo.entity.User">
        UPDATE user SET username = #{username}, password = #{password}, email = #{email} WHERE id = #{id}
    </update>
    <delete id="deleteUser" parameterType="long">
        DELETE FROM user WHERE id = #{id}
    </delete>
</mapper>

4. Service 层

创建 UserService 接口及其实现类 UserServiceImpl,用于业务逻辑处理。

package com.example.demo.service;

import com.example.demo.entity.User;

public interface UserService {
    User getUserById(Long id);
    void createUser(User user);
    void updateUser(User user);
    void deleteUser(Long id);
}
package com.example.demo.service.impl;

import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public User getUserById(Long id) {
        return userMapper.getUserById(id);
    }

    @Override
    public void createUser(User user) {
        userMapper.insertUser(user);
    }

    @Override
    public void updateUser(User user) {
        userMapper.updateUser(user);
    }

    @Override
    public void deleteUser(Long id) {
        userMapper.deleteUser(id);
    }
}

5. Controller 层

创建 UserController,提供 REST API 接口。

package com.example.demo.controller;

import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/users")
public class UserController {
    @Autowired
    private UserService userService;

    @PostMapping
    public String createUser(@RequestBody User user) {
        userService.createUser(user);
        return "User created successfully!";
    }

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return userService.getUserById(id);
    }

    @PutMapping
    public String updateUser(@RequestBody User user) {
        userService.updateUser(user);
        return "User updated successfully!";
    }

    @DeleteMapping("/{id}")
    public String deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
        return "User deleted successfully!";
    }
}

六、MyBatis 的高级用法

1. 结果映射与自动映射

MyBatis 提供了强大的结果映射功能,用于将查询结果映射到 Java 对象。默认情况下,MyBatis 会自动映射字段名与实体类字段名一致的列。

例如,假设数据库表 user 的字段为 idusernameemail,实体类字段也为 idusernameemail,则 MyBatis 会自动完成映射。

如果字段名不一致,可以通过 resultMap 手动映射:

<resultMap id="UserResultMap" type="com.example.demo.entity.User">
    <id property="id" column="user_id"/>
    <result property="username" column="user_name"/>
    <result property="email" column="user_email"/>
</resultMap>

<select id="getUserById" parameterType="long" resultMap="UserResultMap">
    SELECT user_id, user_name, user_email FROM user WHERE user_id = #{id}
</select>

2.使用注解实现 MyBatis 的 resultMap

在 MyBatis 中,resultMap 是用于定义数据库字段与 Java 对象属性之间映射关系的工具。当数据库字段名与实体类属性名不一致时,可以通过注解来完成映射,而无需依赖 XML 文件。

2.1. 使用 @Results 和 @Result 注解

@Results 注解用于定义一个 resultMap,而 @Result 注解用于定义具体的字段映射关系。以下是示例:

示例 1:基本字段映射

假设数据库表 users 的字段为 user_iduser_name,而实体类 User 的属性为 idname,可以通过以下方式定义映射关系:

@Select("SELECT user_id, user_name FROM users WHERE user_id = #{id}")
@Results(id = "UserResultMap", value = {
    @Result(property = "id", column = "user_id", id = true),
    @Result(property = "name", column = "user_name"),
    @Result(property = "email", column = "user_email")
})
User getUserById(@Param("id") Integer id);

3. 多次引用 @Results 定义的 resultMap

在其他方法中,可以通过 @ResultMap 注解引用之前定义的 resultMap。例如:

在这个例子中:

  • @Select 注解定义了查询语句。

  • @Results 注解定义了 resultMap

  • @Result 注解定义了字段映射关系,property 表示实体类属性,column 表示数据库字段。

@Select("SELECT * FROM users WHERE id = #{id}")
@ResultMap("UserResultMap")
User getUserById(@Param("id") Integer id);

@Select("SELECT * FROM users WHERE name = #{name}")
@ResultMap("UserResultMap")
User getUserByName(@Param("name") String name);
示例 2:一对一关联

如果需要处理一对一关联关系,例如用户和其配置信息(Profile),可以通过 @One 注解完成:

假设数据库中有两个表:

users 表:存储用户信息。

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

profiles 表:存储用户配置信息。 

CREATE TABLE profiles (
    id INT PRIMARY KEY,
    bio TEXT
);

 实体类:(这里省略了,service,serviceImpl,mapper的内容,自己补上即可)

public class User {
    private Integer id;
    private String name;
    private Profile profile; // 一对一关联
}

public class Profile {
    private Integer id;
    private String bio;
}

 Mapper 接口:

@Mapper
public interface UserMapper {
    @Select("SELECT * FROM users WHERE id = #{id}")
    @Results({
        @Result(property = "id", column = "id"),
        @Result(property = "name", column = "name"),
        @Result(property = "profile", column = "profile_id", one = @One(select = "com.example.mapper.ProfileMapper.getProfileById"))
    })
    User getUserWithProfile(@Param("id") Integer id);
}

@Mapper
public interface ProfileMapper {
    @Select("SELECT * FROM profiles WHERE id = #{id}")
    Profile getProfileById(@Param("id") Integer id);
}

在这个例子中:

执行流程
  1. 查询 users 表,获取用户的基本信息。

  2. 使用 profile_id 调用 ProfileMapper.getProfileById 方法,加载关联的 Profile 对象。

  3. Profile 对象设置到 User 对象的 profile 属性中。

  • 解释

  • UserMapper 中,@Results 定义了用户信息的字段映射。

  • @One 注解用于定义 UserProfile 的一对一关系:property = "profile":指定关联对象的属性名。column = "profile_id":指定用于关联的字段(profile_id 是外键)。select = "com.example.mapper.ProfileMapper.getProfileById":指定关联对象的查询方法。

  • @One 注解用于定义一对一关联,select 属性指定了关联对象的查询方法,例如一个用户(User)对应一个配置(Profile)。

  • 作用:

  • @One 注解的作用是定义如何通过关联查询加载一对一关系中的另一个对象。它通常与 @Results@Result 注解一起使用,指定关联对象的查询方法。

  • 属性:

  • select:指定关联对象的查询方法,通常是另一个 Mapper 接口中的方法。

  • fetchType:(可选)指定是否立即加载关联对象。默认值为 FetchType.DEFAULT,表示由 MyBatis 配置决定。可以设置为 FetchType.EAGER(立即加载)或 FetchType.LAZY(延迟加载)。

  • 简单说就是先调用 在涉及到“Profile” 的值的时候,会去调用“getProfileById”这个方法,把查询结果的返回值给到“Profile”中,“getProfileById(@Param("id") Integer id)” 这个方法有一个“id”的参数,这个“id”的值来源于“getUserWithProfile”方法中的“@Select("SELECT * FROM users WHERE id = #{id}")” 这段SQL语句中的返回值。

示例 3:一对多关联

对于一对多关系,例如用户和其订单列表,可以通过 @Many 注解完成:

假设数据库中有两个表:

users 表:存储用户信息。

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

orders 表:存储订单信息。

 CREATE TABLE orders (
    id INT PRIMARY KEY,
    user_id INT,
    amount DECIMAL(10, 2)
);

  实体类:(这里省略了,service,serviceImpl,mapper的内容,自己补上即可)

public class User {
    private Integer id;
    private String name;
    private List<Order> orders; // 一对多关联
}

public class Order {
    private Integer id;
    private Integer userId;
    private BigDecimal amount;
}

 Mapper 接口

@Mapper
public interface UserMapper {
    @Select("SELECT * FROM users WHERE id = #{id}")
    @Results({
        @Result(property = "id", column = "id"),
        @Result(property = "name", column = "name"),
        @Result(property = "orders", column = "id", many = @Many(select = "com.example.mapper.OrderMapper.getOrdersByUserId"))
    })
    User getUserWithOrders(@Param("id") Integer id);
}

@Mapper
public interface OrderMapper {
    @Select("SELECT * FROM orders WHERE user_id = #{userId}")
    List<Order> getOrdersByUserId(@Param("userId") Integer userId);
}

在这个例子中:

执行流程
  1. 查询 users 表,获取用户的基本信息。

  2. 使用 id 调用 OrderMapper.getOrdersByUserId 方法,加载与该用户关联的所有订单。

  3. 将订单列表设置到 User 对象的 orders 属性中。

  • 解释:

  • UserMapper 中,@Results 定义了用户信息的字段映射。

  • @Many 注解用于定义 UserOrder 的一对多关系:property = "orders":指定关联对象的属性名。column = "id":指定用于关联的字段(id 是用户的主键)。select = "com.example.mapper.OrderMapper.getOrdersByUserId":指定关联对象的查询方法。

  • @Many 注解用于定义一对多关联,select 属性指定了关联对象的查询方法,例如一个用户(User)对应多个订单(Order

  • @Many 注解用于处理一对多关系,例如一个用户(User)对应多个订单(Order)。

  • 作用

  • @Many 注解的作用是定义如何通过关联查询加载一对多关系中的多个对象。它通常与 @Results@Result 注解一起使用,指定关联对象的查询方法。

  • 属性

  • select:指定关联对象的查询方法,通常是另一个 Mapper 接口中的方法。

  • fetchType:(可选)指定是否立即加载关联对象。默认值为 FetchType.DEFAULT,表示由 MyBatis 配置决定。可以设置为 FetchType.EAGER(立即加载)或 FetchType.LAZY(延迟加载)。

4. 动态 SQL 和注解

MyBatis 注解还支持动态 SQL 的编写,例如 @InsertProvider@SelectProvider 等注解,可以动态生成 SQL 语句。以下是一个动态插入的示例:

@InsertProvider(type = UserSqlProvider.class, method = "insertUserSql")
void insertUser(User user);

 其中,UserSqlProvider 是一个工具类,用于动态生成 SQL:

public class UserSqlProvider {
    public String insertUserSql(User user) {
        return "INSERT INTO users (name, email) VALUES ('" + user.getName() + "', '" + user.getEmail() + "')";
    }
}

MyBatis 的注解方式提供了强大的灵活性,可以替代 XML 文件完成复杂的映射关系定义。通过 @Results@Result 注解,可以实现字段映射、一对一和一对多关联关系的定义。此外,动态 SQL 注解(如 @InsertProvider)进一步增强了 MyBatis 的功能

5. 分页插件集成

MyBatis 提供了分页插件 PageHelper,可以方便地实现分页功能。

  1. 添加依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.5</version>
</dependency>

2.配置分页插件

application.yml 中添加配置:

pagehelper:
  helperDialect: mysql
  reasonable: true
  supportMethodsArguments: true
  params: count=countSql

 6.使用分页功能

在 Service 层中使用 PageHelper

import com.github.pagehelper.PageHelper;

public List<User> getUsersWithPagination(int pageNum, int pageSize) {
    PageHelper.startPage(pageNum, pageSize);
    return userMapper.getAllUsers();
}

7. 注解使用方法

除了 XML 映射文件,MyBatis 还支持注解方式。

例如,定义一个简单的 Mapper 接口:

@Mapper
public interface UserMapper {
    @Select("SELECT * FROM user WHERE id = #{id}")
    User getUserById(@Param("id") Long id);

    @Insert("INSERT INTO user (username, password, email) VALUES (#{username}, #{password}, #{email})")
    void insertUser(User user);

    @Update("UPDATE user SET username = #{username}, password = #{password}, email = #{email} WHERE id = #{id}")
    void updateUser(User user);

    @Delete("DELETE FROM user WHERE id = #{id}")
    void deleteUser(@Param("id") Long id);
}

七、连接池的设置

 在生产环境中,为了提高数据库访问效率,通常会配置连接池。Spring Boot 默认使用 HikariCP 作为连接池实现,我们可以通过修改 application.yml 来调整连接池参数。

1. 配置 HikariCP

application.yml 中添加以下内容:

spring:
  datasource:
    hikari:
      connection-timeout: 30000 # 连接超时时间(毫秒)
      maximum-pool-size: 10 # 最大连接数
      minimum-idle: 5 # 最小空闲连接数
      idle-timeout: 600000 # 空闲连接超时时间(毫秒)
      max-lifetime: 1800000 # 连接的最大生命周期(毫秒)
      pool-name: MyHikariCP # 连接池名称

2. 使用其他连接池(可选)

如果想使用其他连接池(如 Druid),可以在 pom.xml 中引入相关依赖,并替换默认配置。

 引入 Druid 依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.8</version>
</dependency>

配置 Druid: 

spring:
  datasource:
    druid:
      url: jdbc:mysql://localhost:3306/your_database?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
      username: root
      password: your_password
      driver-class-name: com.mysql.cj.jdbc.Driver #数据库驱动类的全限定名。
      initial-size: 5 #连接池初始化时创建的连接数
      max-active: 20 #连接池中允许的最大活动连接数。
      min-idle: 5 #连接池中保持的最小空闲连接数。
      max-wait: 60000 #获取连接的最大等待时间(毫秒)。

 八、MyBatis 缓存配置

1、一级缓存介绍

一级缓存,也称为会话缓存(Session Cache),是 MyBatis 默认提供的缓存机制。它在 SqlSession 的生命周期内有效,用于存储会话期间的查询结果。当 SqlSession 关闭时,一级缓存中的数据会被清除。

2、一级缓存的特点

  • 默认开启:MyBatis 会自动使用一级缓存,无需额外配置。

  • 会话级:一级缓存仅在当前 SqlSession 内有效,不同 SqlSession 之间不共享。

  • 自动管理:MyBatis 会根据需要自动从一级缓存中读取或写入数据。

什么是同一个SqlSession?

如果你是直接定义了一个 UserMapper,那么是否是同一个 SqlSession 取决于 UserMapper 是如何注入的。通常情况下,Spring Boot 会自动管理 SqlSession,并且在同一个请求或事务中复用同一个 SqlSession

你的代码可能长这样:

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public void someMethod() {
        User user1 = userMapper.findById(1L);  // 第一次查询
        User user2 = userMapper.findById(1L);  // 第二次查询
    }
}

 

同一个SqlSession关键点:

  1. 同一个 UserMapper 的调用:

    • 在同一个方法(如 someMethod)中,userMapper 的两次调用 userMapper 中的方法那么属于同一个 SqlSession

    • Spring 会自动复用同一个 SqlSession,直到方法结束或事务提交。

    • 如果 UserMapperxxxMapper 的调用发生在同一个事务内(例如,被同一个 @Transactional 注解的方法包裹),那么它们会共享同一个 SqlSession。------------在同一个事务内,Spring 会确保所有数据库操作都使用同一个 SqlSession,并且共享一级缓存。

  2. 不同的方法调用:

    • 如果你在 不同的方法 中调用 userMapper,Spring 会根据事务管理器决定是否复用同一个 SqlSession

    • 如果没有显式事务管理,每次方法调用可能使用不同的 SqlSession

    • 如果 UserMapperxxxMapper 的调用不在同一个事务内(例如,分别在不同的方法中调用,且这些方法没有被 @Transactional 注解包裹),那么它们会使用不同的 SqlSession。----------------------------每次调用没有事务管理的方法时,Spring 会创建一个新的 SqlSession,并且不会共享一级缓存。

3、二级缓存

application.yml 文件中配置数据源和 MyBatis 的基础配置:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/your_database_name?useSSL=false&serverTimezone=UTC
    username: your_username
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  configuration:
    cache-enabled: true  # 全局开启缓存
    default-executor-type: REUSE  # 使用复用的执行器
  mapper-locations: classpath:mappers/*.xml
  type-aliases-package: com.example.demo.entity

4.二级缓存的默认行为

  • 默认情况下,二级缓存会使用 LRU(最近最少使用) 策略回收数据。

  • 缓存会存储 1024 个对象引用

  • 缓存不会定时刷新(即没有 flushInterval)。

  • 缓存被视为 读/写缓存,这意味着缓存中的对象可以被修改。

 

5、Mapper 文件中的二级缓存配置

在具体的 Mapper XML 文件中,可以通过 <cache> 标签对二级缓存进行细粒度配置。以下是一个完整的示例:

<?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">

    <!-- 开启二级缓存 -->
    <cache
        eviction="FIFO"          # 缓存回收策略:先进先出
        flushInterval="60000"    # 缓存刷新间隔(毫秒)
        size="512"               # 缓存的最大对象数
        readOnly="true"          # 是否只读
    />

    <!-- 查询语句 -->
    <select id="findById" resultType="User" useCache="true">
        SELECT * FROM user WHERE id = #{id}
    </select>
</mapper>

配置说明

  1. eviction:缓存回收策略,可选值包括:

    • LRU(最近最少使用,默认值)。

    • FIFO(先进先出)。

    • SOFT(软引用)。

    • WEAK(弱引用)。

  2. flushInterval:缓存刷新间隔(单位:毫秒)。例如,60000 表示每隔 60 秒刷新一次。

  3. size:缓存的最大对象数。例如,512 表示最多存储 512 个对象。

  4. readOnly

    • true:缓存中的对象被视为只读,返回相同实例,适合查询操作。

    • false:缓存中的对象被视为可写,返回对象的拷贝。

  5. useCache:在 <select> 标签中,可以通过 useCache 属性控制是否对某个查询启用缓存。

 

3、实体类要求

为了将缓存数据存储到二级缓存中,实体类需要实现 Serializable 接口。例如:

import java.io.Serializable;

public class User implements Serializable {
    private Long id;
    private String name;
    private Integer age;

    // Getters and Setters
}

4、注意事项

  1. 二级缓存的作用范围:二级缓存是基于 Mapper 的,每个 Mapper 的二级缓存是独立的。

  2. 分布式环境下的缓存问题:在分布式系统中,MyBatis 的二级缓存可能会导致数据一致性问题。建议结合 Redis 等分布式缓存工具来解决。

  3. 禁用二级缓存:如果某些查询不适合缓存,可以在 <select> 标签中设置 useCache="false"

  4. 刷新缓存:如果某些 insertupdatedelete 操作不需要刷新缓存,可以设置 flushCache="false"

通过 YAML 文件和 Mapper XML 文件的结合,可以灵活地配置 MyBatis 的二级缓存。全局配置提供了默认行为,而 Mapper 文件中的 <cache> 标签则允许对每个 Mapper 进行细粒度的控制。这种配置方式不仅清晰,还能满足复杂的业务需求。

 

总结

MyBatis 的注解方式提供了强大的灵活性,可以替代 XML 文件完成复杂的映射关系定义。通过 @Results@Result 注解,可以实现字段映射、一对一和一对多关联关系的定义。此外,动态 SQL 注解(如 @InsertProvider)进一步增强了 MyBatis 的功能。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值