在Spring Boot项目中整合ORM(Object-Relational Mapping,对象关系映射)开发框架,是为了简化数据库操作,将数据库中的表与Java对象进行映射,从而实现通过操作Java对象来间接操作数据库,避免直接编写繁琐的SQL语句。

什么是ORM?

ORM 是一种编程技术,用于在面向对象语言(如Java)和关系型数据库(如MySQL、PostgreSQL)之间建立映射关系。它允许开发者以操作对象的方式操作数据库,而无需直接编写SQL语句。

常见的Java ORM 框架有:

  • Hibernate

  • MyBatis

  • JPA(Java Persistence API)(是规范,Hibernate是其实现之一)

  • MyBatis-Plus(MyBatis的增强工具)

Spring Boot 为什么整合 ORM?

Spring Boot 本身提供了强大的自动配置能力,可以轻松集成主流 ORM 框架,简化配置和开发流程。通过整合 ORM,开发者可以:

  • 快速进行数据库操作

  • 提高开发效率

  • 减少样板代码(boilerplate code)

  • 实现数据库操作的面向对象编程

常见的整合方式

方式1:整合 JPA + Hibernate

JPA 是 Java EE 的持久化规范,Hibernate 是其最流行的实现。

添加依赖(Maven)
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
配置数据库连接(application.yml)
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/testdb
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update  # 自动建表
    show-sql: true
    database-platform: org.hibernate.dialect.MySQL8Dialect
创建实体类(Entity)
@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private Integer age;

    // getter 和 setter 省略
}
创建 Repository 接口
public interface UserRepository extends JpaRepository<User, Long> {
}
使用服务类调用
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    public User saveUser(User user) {
        return userRepository.save(user);
    }
}

方式2:整合 MyBatis

MyBatis 是半自动化的ORM框架,需要手动写SQL,但灵活性更高。

添加依赖
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
配置 application.yml(路径:src/main/resources/application.yml)
  • type-aliases-package:自动为 cn.example.vo 包下的类注册别名(如 Dept)。

  • mapper-locations:指定 XML 映射文件位置(可选,如果使用注解可省略)。

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  config-location: classpath:mybatis/mybatis.cfg.xml
  type-aliases-package: cn.example.vo
  mapper-locations: classpath:mapper/*.xml
创建实体类(vo)(路径:src/main/java/cn/example/vo/Dept.java)
package cn.example.vo;

import java.io.Serializable;

public class Dept implements Serializable {
    private Long deptno;
    private String dname;

    // 无参构造
    public Dept() {}

    // 全参构造
    public Dept(Long deptno, String dname) {
        this.deptno = deptno;
        this.dname = dname;
    }

    // Getter 和 Setter
    public Long getDeptno() {
        return deptno;
    }

    public void setDeptno(Long deptno) {
        this.deptno = deptno;
    }

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "deptno=" + deptno +
                ", dname='" + dname + '\'' +
                '}';
    }
}

创建DAO 接口(路径:src/main/java/cn/example/dao/IDeptDAO.java)

使用 @Mapper 注解,Spring Boot 会将其注册为 MyBatis 的 Mapper Bean。

@Mapper
public interface UserMapper {
    List<User> findAll();
    User findById(Long id);
    void insertUser(User user);
}
创建Service 接口(路径:src/main/java/cn/example/service/impl/DeptServiceImpl.java)
package cn.example.service;

import cn.example.vo.Dept;
import java.util.List;

public interface IDeptService {
    List<Dept> list();
}
创建Service 实现类(路径:src/main/java/cn/example/service/impl/DeptServiceImpl.java)
package cn.example.service.impl;

import cn.example.dao.IDeptDAO;
import cn.example.service.IDeptService;
import cn.example.vo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service  // 注册为 Spring Bean
public class DeptServiceImpl implements IDeptService {

    @Autowired
    private IDeptDAO deptDAO;

    @Override
    public List<Dept> list() {
        return deptDAO.findAll();
    }
}
创建 Mapper XML 文件(路径:src/main/resources/mybatis/mybatis.cfg.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>
    <settings>
        <!-- 开启二级缓存 -->
        <setting name="cacheEnabled" value="true"/>
        <!-- 开启驼峰命名自动映射: db_column -> camelCase -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
</configuration>

事务处理

事务(Transaction)是指一组数据库操作,它们被当作一个整体来执行,满足 ACID 特性:

  • A(Atomicity)原子性:事务中的操作要么全部完成,要么全部不完成。

  • C(Consistency)一致性:事务前后数据处于一致状态。

  • I(Isolation)隔离性:多个事务之间互不干扰。

  • D(Durability)持久性:事务一旦提交,数据永久保存。


Spring Boot 使用 @Transactional 注解 来声明事务,底层基于 Spring 的事务管理器(PlatformTransactionManager),与 MyBatis 完美集成。

添加事务支持

我们以上一个 example 项目为基础,扩展一个 插入多个部门 的功能,并加入事务控制。

向 dept 表插入两条记录,如果其中一条失败(如主键冲突或字段超长),则整个操作回滚。

修改 IDeptDAO.java
package cn.example.dao;

import cn.example.vo.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import java.util.List;

@Mapper
public interface IDeptDAO {

    @Select("SELECT deptno, dname FROM dept")
    List<Dept> findAll();

    @Insert("INSERT INTO dept(deptno, dname) VALUES(#{deptno}, #{dname})")
    int insert(Dept dept);
}
修改 IDeptService.java
package cn.example.service;

import cn.example.vo.Dept;
import java.util.List;

public interface IDeptService {
    List<Dept> list();
    void addDepts(List<Dept> depts);  // 新增批量插入方法
}
在 DeptServiceImpl.java 中添加事务
package cn.example.service.impl;

import cn.example.dao.IDeptDAO;
import cn.example.service.IDeptService;
import cn.example.vo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
public class DeptServiceImpl implements IDeptService {

    @Autowired
    private IDeptDAO deptDAO;

    @Override
    public List<Dept> list() {
        return deptDAO.findAll();
    }

    /**
     * 批量插入部门,加入事务控制
     */
    @Transactional  // 开启事务
    @Override
    public void addDepts(List<Dept> depts) {
        for (Dept dept : depts) {
            deptDAO.insert(dept);
            // 模拟异常:如果部门名包含 "异常",则抛出异常,触发回滚
            if (dept.getDname().contains("异常")) {
                throw new RuntimeException("模拟插入失败,触发事务回滚!");
            }
        }
    }
}

@Transactional 加在 Service 实现方法上,这是最佳实践(不要加在 Controller 或 DAO 上)。

添加 Controller 接口
package cn.example.controller;

import cn.example.service.IDeptService;
import cn.example.vo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.Arrays;
import java.util.List;

@RestController
@RequestMapping("/dept")
public class DeptController {

    @Autowired
    private IDeptService deptService;

    @GetMapping("/list")
    public List<Dept> getAllDepts() {
        return deptService.list();
    }

    @PostMapping("/batch")
    public String addDepts() {
        Dept dept1 = new Dept(10L, "财务部");
        Dept dept2 = new Dept(20L, "研发部");
        Dept dept3 = new Dept(30L, "异常部门"); // 会触发异常

        try {
            deptService.addDepts(Arrays.asList(dept1, dept2, dept3));
            return "插入成功";
        } catch (Exception e) {
            return "插入失败:" + e.getMessage();
        }
    }
}