Spring Boot + MyBatis:高效构建持久层的最佳实践

1. 概述

MyBatis是一个Java持久化框架,它通过XML描述符或注解把对象存储过程SQL语句关联起来,映射成数据库内对应的纪录。Spring Boot 是一个快速开发框架,能够简化 Spring 应用的初始搭建和开发过程。本文将介绍如何在 Spring Boot 项目中集成 MyBatis,并提供实例代码。

2. 环境准备

  • JDK 1.8 或更高版本
  • IDEA
  • Maven 3.x 或更高版本
  • Spring Boot 2.x
  • MyBatis 3.x

3.为什么要在Spring Boot 项目中集成 MyBatis呢

Spring Boot 中集成 MyBatis 的好处

1. 简化数据库操作

  • MyBatis 提供了强大的 SQL 映射功能,开发者可以直接编写 SQL 语句,并通过简单的配置将 SQL 与 Java 对象映射。
  • 相比于纯 JDBC,MyBatis 减少了大量的样板代码(如手动处理 ResultSet、PreparedStatement 等)。

2. 灵活的 SQL 控制

  • MyBatis 允许开发者完全控制 SQL 语句的编写,适合复杂查询或需要高度优化的场景。
  • 支持动态 SQL(通过 等标签),能够根据条件动态生成 SQL。

3. 与 Spring Boot 无缝集成

  • MyBatis 提供了 mybatis-spring-boot-starter,可以快速集成到 Spring Boot 项目中。
  • 通过注解或 XML 配置,开发者可以轻松管理数据库操作。

4. 易于调试和优化

  • 由于 SQL 语句是显式编写的,开发者可以直观地看到执行的 SQL,便于调试和性能优化。
  • 支持日志输出 SQL 语句,方便排查问题。

5. 支持多种数据库

  • MyBatis 支持多种数据库(如 MySQL、PostgreSQL、Oracle 等),只需更换数据源配置即可。

6. 良好的扩展性

  • MyBatis 支持插件机制,可以通过自定义插件实现分页、缓存、SQL 拦截等功能。
  • 支持与第三方框架(如 PageHelper)集成,简化分页操作。

7. 降低学习成本

  • MyBatis 的学习曲线相对较低,尤其是对于熟悉 SQL 的开发者。
  • 相比于 JPA 的复杂抽象,MyBatis 更贴近传统数据库操作。

如果不集成 MyBatis 呢,我们使用其他的的替代方案

如果不集成 MyBatis,Spring Boot 项目仍然可以通过其他方式操作数据库,常见的替代方案包括:

1. 使用 Spring Data JPA

  • 优点
    • 完全面向对象,无需编写 SQL 语句。
    • 支持自动生成 SQL,适合简单的 CRUD 操作。
    • 提供丰富的查询方法命名规则,减少代码量。
  • 缺点
    • 对于复杂查询,SQL 生成可能不够灵活。
    • 性能优化相对困难,尤其是需要手动优化 SQL 的场景。

2. 使用纯 JDBC

  • 优点
    • 完全控制 SQL 语句,适合需要极致性能优化的场景。
    • 不依赖任何 ORM 框架,轻量级。
  • 缺点
    • 需要手动处理 ResultSet、PreparedStatement 等,代码量大且容易出错。
    • 缺乏对象关系映射(ORM)功能,开发效率低。

3. 使用其他 ORM 框架

  • 例如 Hibernate、JOOQ 等。
  • 优点
    • Hibernate 提供了强大的 ORM 功能,适合复杂的对象关系映射。
    • JOOQ 提供了类型安全的 SQL 构建方式。
  • 缺点
    • 学习成本较高,尤其是 Hibernate 的复杂配置和缓存机制。
    • 对于简单项目,可能显得过于重量级。

4. 使用 Spring JDBC Template

  • 优点
    • 相比于纯 JDBC,Spring JDBC Template 简化了数据库操作。
    • 提供了更好的异常处理和资源管理。
  • 缺点
    • 仍然需要手动编写 SQL 语句,缺乏 ORM 功能。
    • 对于复杂查询,代码量较大。

image-20250217143305781

4. 创建 Spring Boot 项目

  • 打开IDEA>新建项目>选择Spring Initializer,这里将服务器URL换成start.aliyun.com更快一点

image-20250217143644800

  • 接下来我们选择以下这三个依赖:然后点击创建
    • Spring Web
    • MyBatis Framework
    • MySQL Driver

image-20250217143934267

  • 当我们创建好之后,找到我们项目目录下的pom.xml文件中,可以看到我们的依赖已经添加进来,
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>demo</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.6.13</spring-boot.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.1</version>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.example.demo.DemoApplication</mainClass>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

5. 配置数据库连接

application.propertiesapplication.yml 中配置数据库连接信息,端口自己随便设置即可,不要跟其他端口冲突就行:

image-20250217144538317

server:
  port: 8666 # 后端端口

spring:
  banner:
    charset: UTF-8
    location: classpath:banner.txt
  datasource:
    url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root  # MySQL 用户名
    password: 你的密码  # MySQL 密码
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  # mapper xml 文件路径
  mapper-locations: classpath:mapper/*.xml
  # 实体类包路径
  type-aliases-package: com.example.demo.entity

  configuration:
    # 开启驼峰命名
    map-underscore-to-camel-case: true

6. Model 层(实体层/POJO 层)

创建一个简单的实体类 User

作用
定义与数据库表结构映射的 Java 对象(POJO),通常称为实体类(Entity)或数据模型(Model)。
每个字段对应数据库表中的一列,用于在层间传输数据(如 Service 到 Controller、Mapper 到 Service)

public class User {
    private Long id;
    private String name;
    private String email;
    // getters/setters
}

关键点

  • 使用 @Data(Lombok)可简化代码。
  • 可与 DTO(Data Transfer Object)区分,DTO 用于前后端数据传输,而 Model 直接映射数据库。

7.Mapper 层(DAO 层,数据访问层)

作用
直接与数据库交互,负责执行 SQL 操作(增删改查)。
通过 MyBatis 的接口与 XML 配置文件(或注解)实现 ORM 映射,隔离数据库操作细节。

  • 接口定义
@Mapper // Spring 自动生成实现类
public interface UserMapper {
    User selectById(Long id);
    void insert(User user);
}
  • XML 映射UserMapper.xml):
<select id="selectById" resultType="com.example.model.User">
    SELECT * FROM user WHERE id = #{id}
</select>

关键点

  • 使用 @Mapper@MapperScan 扫描接口,MyBatis 动态生成代理类。
  • 方法名与 XML 中的 id 对应,参数通过 #{field} 绑定。

8.Service 层(业务逻辑层)

作用
封装业务逻辑,处理复杂的业务规则(如数据校验、事务管理、多表操作组合)。
作为 Controller 和 Mapper 之间的桥梁,确保业务操作的原子性。

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    
    @Transactional // 事务管理
    public void createUser(User user) {
        if (user.getName() == null) throw new IllegalArgumentException("Name required");
        userMapper.insert(user);
    }
}

关键点

  • 使用 @Service 标记为 Spring 管理的 Bean。
  • 通过 @Transactional 注解声明事务(如插入失败自动回滚)

9.Controller 层(表现层)

作用
接收 HTTP 请求,解析参数,调用 Service 处理业务,返回响应(JSON/视图)。
负责处理路由、参数校验及响应格式。

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;
    
    @PostMapping
    public ResponseEntity<String> createUser(@RequestBody User user) {
        userService.createUser(user);
        return ResponseEntity.ok("User created");
    }
}

关键点

  • 使用 @RestController@Controller 标识。
  • 通过 @GetMapping/@PostMapping 映射请求路径。
  • 参数校验可使用 @Valid 配合 Hibernate Validator。

其他辅助组件

  • DTO(Data Transfer Object)
    定制化传输对象(如屏蔽敏感字段、聚合多个 Model 数据),避免直接暴露 Model。
  • Configuration 类
    配置数据源、MyBatis 等(Spring Boot 通过 application.yml 简化):
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/db
    username: root
    password: 123456
mybatis:
  mapper-locations: classpath:mapper/*.xml

各层协作流程

  1. 请求入口Controller 接收参数并校验格式。
  2. 业务处理Controller 调用 Service 方法。
  3. 数据操作Service 调用 Mapper 接口操作数据库。
  4. 结果返回Controller 将处理结果封装为 HTTP 响应。

分层优势

  • 解耦:修改数据库(如切换 MySQL 到 PostgreSQL)只需调整 Mapper 层,不影响业务逻辑。
  • 复用性:Service 方法可被多个 Controller 调用。
  • 可测试性:各层可独立测试(如 Mock Mapper 测试 Service)。

MyBatis 中的主要注解和作用

@Select

  • 作用: 用于定义查询语句。
@Select("SELECT * FROM users WHERE id = #{id}")
User getUserById(int id);

@Insert,@Update,@Delete

  • 作用: 用于定义插入,更新,删除语句。
@Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})")
void insertUser(User user);

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

@Delete("DELETE FROM users WHERE id = #{id}")
void deleteUser(int id);

@Param

  • 作用: 用于给方法参数命名,以便在 SQL 语句中引用。
@Results({
    @Result(property = "id", column = "id"),
    @Result(property = "name", column = "name"),
    @Result(property = "email", column = "email")
})
@Select("SELECT * FROM users WHERE id = #{id}")
User getUserById(int id);

@Results

  • 作用: 用于定义结果映射,将查询结果映射到 Java 对象。
@Results({
    @Result(property = "id", column = "id"),
    @Result(property = "name", column = "name"),
    @Result(property = "email", column = "email")
})
@Select("SELECT * FROM users WHERE id = #{id}")
User getUserById(int id);

@Result

  • 作用: 用于定义单个字段的映射关系,通常与 @Results 一起使用。
@Result(property = "id", column = "id")

@ResultMap

  • 作用: 用于指定查询结果的映射规则,通常与 @Select 一起使用。
@Select("SELECT * FROM users WHERE id = #{id}")
@ResultMap("userResultMap")
User getUserById(@Param("id") Long id);

@Options

  • 作用: 用于指定插入操作的选项,如是否使用自动生成的主键。
@Insert("INSERT INTO users(name, age) VALUES(#{name}, #{age})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insertUser(User user);

@MapKey

  • 作用: 用于指定返回结果为 Map 时,Map 的键对应的属性。
@Select("SELECT * FROM users")
@MapKey("id")
Map<Long, User> getUsersMap();

这些注解使得 MyBatis 的使用更加简洁和直观,减少了 XML 配置的复杂性。

实践总结:

  • 注解与 XML 混合使用: 在简单的 SQL 操作中,优先使用注解方式,提升开发效率;对于复杂的 SQL,使用 XML 文件进行管理,增强可维护性。
  • 动态 SQL 的应用: 充分利用 MyBatis 的动态 SQL 功能,处理复杂查询条件,避免在代码中拼接 SQL 字符串,提升代码的可读性和安全性。
  • 事务管理: 在需要保证数据一致性的操作中,使用 Spring 的事务管理机制,确保操作的原子性。
  • 性能优化: 合理使用 MyBatis 的缓存机制,减少数据库访问次数;对于分页查询,使用分页插件,避免一次性加载大量数据导致性能问题。

为 Map 时,Map 的键对应的属性。

@Select("SELECT * FROM users")
@MapKey("id")
Map<Long, User> getUsersMap();

这些注解使得 MyBatis 的使用更加简洁和直观,减少了 XML 配置的复杂性。

通过遵循上述最佳实践,开发者可以在 Spring Boot 项目中高效地构建持久层,提升系统的性能和可维护性。

正文到此结束
评论插件初始化中...
Loading...