在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();
}
}
}
评论