MyBatis 与 JPA 的核心对比

亚洲365bet 📅 2026-11-04 09:07:56 👤 admin 👁️ 9054 ❤️ 498
MyBatis 与 JPA 的核心对比

-------------------------------------------------------------------------------------------

一、MyBatis 中 Mapper 注解与 XML 方式在处理复杂业务逻辑时的核心差异

复杂业务逻辑通常涉及 动态 SQL(多条件拼接)、多表关联查询、嵌套对象映射、批量操作、子查询 等场景。两种方式在这些场景下的处理能力和体验有显著区别:

复杂场景Mapper 注解方式XML 映射文件方式

动态 SQL 处理

需通过 @SelectProvider 等 Provider 类 实现,需在 Java 代码中用 SQL 类拼接 SQL(类似字符串拼接),逻辑分散在 Java 类中,可读性差,维护困难。

例:多条件查询需手动判断参数非空并拼接 WHERE 子句,容易出错。

原生支持 、、、 等动态标签,SQL 逻辑与条件判断在 XML 中结构化呈现,直观清晰,便于修改。

例:用 直接控制条件拼接,无需手动处理 AND 前缀。

多表关联与结果映射

复杂结果映射(如嵌套对象、集合)需通过 @Results + @Result 注解手动配置,多层嵌套时注解冗长,易混淆。

例:用户关联部门 + 订单列表,需嵌套多个 @Result,可读性差。

通过 标签统一管理映射关系,支持 (一对一)、(一对多)等标签,清晰表达对象间关系,多层嵌套结构一目了然。

例:用户→部门→订单列表的映射可在一个 resultMap 中分层定义。

批量操作(插入 / 更新)

批量插入需在注解中写长 SQL 字符串(如 VALUES (#{u1.name}), (#{u2.name})...),参数多时字符串拼接繁琐,易出错,且不支持动态集合长度。

通过 标签遍历集合动态生成 SQL,支持任意长度的批量操作,语法简洁(如 VALUES (#{u.name}))。

子查询与复杂 JOIN

子查询或多表 JOIN 的 SQL 语句冗长,写在注解中会导致接口类臃肿,且换行、缩进等格式难以维护,影响可读性。

XML 中可通过格式化 SQL(换行、缩进)清晰表达子查询和 JOIN 逻辑,甚至可拆分 SQL 为多行便于理解(如将 LEFT JOIN 拆分到单独行)。

SQL 复用与模块化

无法直接复用 SQL 片段,相同逻辑需在多个注解中重复编写,冗余度高。

支持 标签定义可复用 SQL 片段(如通用查询字段、条件),通过 标签引用,减少重复代码,便于统一维护。

团队协作与 DBA 介入

SQL 逻辑嵌入 Java 代码,DBA 需熟悉 Java 语法才能优化,协作成本高。

SQL 集中在 XML 文件中,DBA 可直接修改优化,无需关注 Java 代码,协作更顺畅。

总结:在复杂业务逻辑下,XML 方式是更优选择—— 其结构化标签、动态 SQL 支持、结果映射灵活性和 SQL 与代码分离的特性,能显著降低复杂逻辑的维护成本;而注解方式仅适合简单场景,复杂逻辑下会导致代码臃肿、可读性差。

二、MyBatis 与 JPA 的核心对比

MyBatis 和 JPA 都是 Java 生态中主流的 ORM(对象关系映射)框架,但设计理念和适用场景截然不同:

对比维度MyBatisJPA(以 Hibernate 实现为例)

设计理念

半自动 ORM:聚焦 SQL 与 Java 对象的映射,需手动编写 SQL(或通过注解 / XML 定义),保留对 SQL 的完全控制。

全自动 ORM:基于 “对象模型驱动”,通过注解 / XML 定义实体与表的映射关系,自动生成 SQL,屏蔽数据库细节。

SQL 控制能力

完全手动控制 SQL,支持复杂查询(多表 JOIN、子查询、动态条件等),可针对性优化 SQL 性能(如索引利用、分页逻辑)。

默认自动生成 SQL(简单 CRUD),复杂查询需通过 JPQL(Java 持久化查询语言)或原生 SQL 实现,自动生成的 SQL 可能存在性能问题(如冗余字段查询、低效 JOIN)。

学习成本

低:核心是 SQL + 映射规则,开发者只需熟悉 SQL 和 MyBatis 基本标签 / 注解,上手快。

高:需理解 ORM 核心概念(如持久化上下文、一级缓存)、JPQL 语法、关联映射策略(如懒加载、级联)等,对 SQL 不熟悉的开发者可能难以调试。

灵活性与定制化

高:可根据业务需求自由编写 SQL,适配各种复杂场景(如分库分表、多数据源),对数据库特性(如 MySQL 存储过程、PostgreSQL 特有函数)支持友好。

中:简单场景无需写 SQL,但复杂场景(如动态多条件查询、特殊索引利用)需绕过自动生成逻辑,定制化成本高。

性能

性能可控:手动编写 SQL 可避免冗余操作(如只查必要字段),适合高并发、大数据量场景。

性能依赖自动生成的 SQL 质量,复杂查询可能产生低效 SQL(如 N+1 查询问题),需额外优化(如 FetchType 调整、查询缓存)。

适用场景

1. 复杂业务逻辑(多表关联、动态 SQL、批量操作);

2. 需精细优化 SQL 性能(如电商订单、金融交易);

3. 数据库特性依赖强(如存储过程、特殊函数);

4. 分库分表、多数据源等复杂架构。

1. 简单 CRUD 场景(如后台管理系统);

2. 快速开发原型或中小型项目;

3. 更关注对象模型而非 SQL 细节(如领域驱动设计);

4. 需跨数据库兼容(JPA 可屏蔽部分数据库差异)。

总结:

MyBatis 是 “SQL 优先” 的框架,适合需要 精细控制 SQL、处理复杂业务逻辑 的场景,灵活度和性能可控性更强;

JPA 是 “对象优先” 的框架,适合 快速开发、简单 CRUD 为主 的场景,能减少重复代码,但复杂场景下灵活性不足。

选择时需结合业务复杂度(简单 vs 复杂)、团队技术栈(熟悉 SQL vs 熟悉 ORM)、性能要求(高并发 vs 一般需求)综合决策。

-------------------------------------------------------------------------------------------

Spring Boot JPA 是 Spring 生态中基于 JPA(Java Persistence API,Java 持久化规范) 的简化数据访问方案,通过 Spring Data JPA 实现对 JPA 的封装,大幅减少数据访问层(DAO)的重复代码,让开发者更专注于业务逻辑。

一、核心概念与关系

JPA:是 Java 官方定义的 ORM(对象关系映射)规范,定义了实体类与数据库表的映射规则、CRUD 操作接口等,但本身不提供实现。

Spring Data JPA:是 Spring 对 JPA 规范的具体实现,通过 “接口定义方法签名” 自动生成 SQL,无需手动编写 DAO 实现类。

Spring Boot JPA:Spring Boot 对 Spring Data JPA 的自动配置支持(如自动配置数据源、实体管理器等),进一步简化集成流程。

二、Spring Boot 集成 JPA 步骤

1. 引入依赖

在 pom.xml 中添加核心依赖(以 MySQL 为例):

xml

org.springframework.boot

spring-boot-starter-data-jpa

com.mysql

mysql-connector-j

runtime

2. 配置数据源与 JPA 特性

在 application.yml 中配置数据库连接和 JPA 行为:

yaml

spring:

datasource:

driver-class-name: com.mysql.cj.jdbc.Driver

url: jdbc:mysql://localhost:3306/jpa_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai

username: root

password: root

jpa:

hibernate:

ddl-auto: update # 自动创建/更新表结构(create:每次启动重建表;update:保留数据更新结构;none:关闭)

show-sql: true # 控制台打印 SQL

properties:

hibernate:

format_sql: true # 格式化 SQL

database-platform: org.hibernate.dialect.MySQL8Dialect # 指定数据库方言(适配数据库特性)

3. 定义实体类(Entity)

通过 JPA 注解将 Java 类映射到数据库表:

java

运行

import jakarta.persistence.*;

import java.time.LocalDateTime;

@Entity // 标记为 JPA 实体(对应数据库表)

@Table(name = "t_user") // 指定映射的表名(默认类名小写)

public class User {

@Id // 标记为主键

@GeneratedValue(strategy = GenerationType.IDENTITY) // 主键生成策略(IDENTITY:自增 ID)

private Long id;

@Column(name = "username", length = 50, nullable = false, unique = true) // 映射表字段(长度、非空、唯一)

private String username;

@Column(name = "age")

private Integer age;

@Column(name = "create_time")

private LocalDateTime createTime;

// 省略 getter、setter、构造方法

}

核心注解说明:

@Entity:声明类为实体类(必须)。

@Table:指定映射的表名(可选,默认类名小写)。

@Id:标记主键字段(必须)。

@GeneratedValue:指定主键生成策略(IDENTITY:自增;SEQUENCE:序列;AUTO:自动选择)。

@Column:配置字段属性(名称、长度、非空、唯一等)。

4. 定义 Repository 接口

通过继承 Spring Data JPA 提供的接口,自动获得 CRUD 能力,无需编写实现类:

java

运行

import org.springframework.data.jpa.repository.JpaRepository;

import com.example.jpa.entity.User;

// 继承 JpaRepository<实体类, 主键类型>,自动获得基础 CRUD 方法

public interface UserRepository extends JpaRepository {

// 可自定义查询方法(Spring Data JPA 自动解析方法名生成 SQL)

}

Spring Data JPA 核心接口:

Repository:最基础接口,无任何方法。

CrudRepository:继承 Repository,提供 CRUD 方法(save、findById、findAll、delete 等)。

PagingAndSortingRepository:继承 CrudRepository,增加分页和排序方法(findAll(Pageable)、findAll(Sort))。

JpaRepository:继承 PagingAndSortingRepository,扩展更多 JPA 特性(如批量操作、刷新缓存等),最常用。

5. 业务层调用

在 Service 中注入 UserRepository 直接使用:

java

运行

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import java.time.LocalDateTime;

import java.util.List;

@Service

public class UserService {

@Autowired

private UserRepository userRepository;

// 新增用户

public User createUser(String username, Integer age) {

User user = new User();

user.setUsername(username);

user.setAge(age);

user.setCreateTime(LocalDateTime.now());

return userRepository.save(user); // 调用 JpaRepository 自带的 save 方法

}

// 查询所有用户

public List getAllUsers() {

return userRepository.findAll(); // 自带的查询所有方法

}

// 根据 ID 查询用户

public User getUserById(Long id) {

// findById 返回 Optional,需处理空值

return userRepository.findById(id).orElseThrow(() -> new RuntimeException("用户不存在"));

}

}

三、核心功能详解

1. 自动生成 CRUD 方法

继承 JpaRepository 后,无需编写实现,直接使用以下方法:

save(T entity):新增或更新(根据主键是否为空判断)。

findById(ID id):根据主键查询(返回 Optional)。

findAll():查询所有。

deleteById(ID id):根据主键删除。

count():统计总数。

2. 自定义查询方法(方法名解析)

Spring Data JPA 支持通过 “方法名规则” 自动生成 SQL,无需手动编写。规则是:动词 + 条件(属性 + 关键字)。

常用关键字:By(条件)、And/Or(多条件)、Like、Between、OrderBy、Top 等。

示例:

java

运行

public interface UserRepository extends JpaRepository {

// 1. 根据 username 查询(等价于:SELECT * FROM t_user WHERE username = ?)

User findByUsername(String username);

// 2. 根据 age 大于指定值查询(等价于:SELECT * FROM t_user WHERE age > ?)

List findByAgeGreaterThan(Integer age);

// 3. 根据 username 模糊查询 + 年龄范围(等价于:SELECT * FROM t_user WHERE username LIKE %?% AND age BETWEEN ? AND ?)

List findByUsernameLikeAndAgeBetween(String username, Integer minAge, Integer maxAge);

// 4. 查询年龄最大的 3 条数据(等价于:SELECT * FROM t_user ORDER BY age DESC LIMIT 3)

List findTop3ByOrderByAgeDesc();

}

3. 自定义 JPQL 或原生 SQL

复杂查询可通过 @Query 注解手动编写 JPQL(JPA 查询语言,面向对象)或原生 SQL。

示例:

java

运行

import org.springframework.data.jpa.repository.Query;

import org.springframework.data.repository.query.Param;

public interface UserRepository extends JpaRepository {

// 1. JPQL(表名用实体类名,字段用属性名)

@Query("SELECT u FROM User u WHERE u.age > :age AND u.username LIKE %:name%")

List findByAgeAndName(@Param("age") Integer age, @Param("name") String name);

// 2. 原生 SQL(需指定 nativeQuery = true,直接操作数据库表和字段)

@Query(value = "SELECT * FROM t_user WHERE create_time > :time", nativeQuery = true)

List findByCreateTimeAfter(@Param("time") LocalDateTime time);

// 3. 更新操作(需配合 @Modifying 和事务注解)

@Modifying // 标记为修改操作(更新/删除)

@Query("UPDATE User u SET u.age = :newAge WHERE u.id = :id")

int updateAgeById(@Param("id") Long id, @Param("newAge") Integer newAge);

}

注意:更新 / 删除操作需在 Service 层添加 @Transactional 注解开启事务。

4. 分页与排序

通过 Pageable 和 Sort 实现分页和排序,无需手动编写 LIMIT 或 ORDER BY。

示例:

java

运行

import org.springframework.data.domain.Page;

import org.springframework.data.domain.PageRequest;

import org.springframework.data.domain.Pageable;

import org.springframework.data.domain.Sort;

@Service

public class UserService {

@Autowired

private UserRepository userRepository;

// 分页查询(第 1 页,每页 10 条,按 createTime 降序)

public Page getUsersByPage() {

// 排序规则:按 createTime 降序

Sort sort = Sort.by(Sort.Direction.DESC, "createTime");

// 分页参数:page(从 0 开始)、size(每页条数)、排序规则

Pageable pageable = PageRequest.of(0, 10, sort);

return userRepository.findAll(pageable);

}

// 带条件的分页查询(结合自定义方法)

public Page getUsersByAgePage(Integer minAge, Pageable pageable) {

return userRepository.findByAgeGreaterThan(minAge, pageable);

}

}

5. 关联映射(多表关系)

JPA 支持通过注解定义实体间关系(一对一、一对多、多对多),自动处理表关联。

(1)一对一(如 User 与 UserDetail)

java

运行

// 主表实体(User)

@Entity

public class User {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

private String username;

// 一对一关联( cascade: 级联操作;fetch: 加载策略)

@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)

@JoinColumn(name = "detail_id", unique = true) // 外键字段(user 表的 detail_id 关联 user_detail 表的 id)

private UserDetail detail;

}

// 从表实体(UserDetail)

@Entity

public class UserDetail {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

private String address;

private String phone;

// 反向关联(可选)

@OneToOne(mappedBy = "detail") // mappedBy 指向主表中关联属性名

private User user;

}

cascade = CascadeType.ALL:操作 User 时自动级联操作 UserDetail(如保存 User 时自动保存 Detail)。

fetch = FetchType.LAZY:延迟加载(查询 User 时不主动查询 Detail,用到时才查)。

(2)一对多(如 User 与 Order)

java

运行

// 一的一方(User)

@Entity

public class User {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

private String username;

// 一对多关联(一个用户多个订单)

@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)

private List orders = new ArrayList<>();

}

// 多的一方(Order)

@Entity

public class Order {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

private String orderNo;

// 多对一关联(多个订单属于一个用户)

@ManyToOne(fetch = FetchType.LAZY)

@JoinColumn(name = "user_id") // 外键字段(order 表的 user_id 关联 user 表的 id)

private User user;

}

mappedBy = "user":指定关联由 Order 类的 user 属性维护(避免双向维护外键)。

orphanRemoval = true:当从 orders 集合中移除某个 Order 时,自动删除该 Order 记录。

6. 事务管理

通过 @Transactional 注解管理事务(Spring 提供,与 JPA 集成):

java

运行

import org.springframework.transaction.annotation.Transactional;

@Service

public class UserService {

@Autowired

private UserRepository userRepository;

// 声明事务(默认:发生异常时回滚)

@Transactional

public void batchCreate(List users) {

userRepository.saveAll(users);

// 若中途抛出异常,所有保存操作会回滚

}

// 自定义事务属性(如只读、超时时间)

@Transactional(readOnly = true, timeout = 10) // 只读事务,超时 10 秒

public List getUsersWithLock() {

// 复杂查询逻辑

return userRepository.findAll();

}

}

四、缓存机制

JPA 自带两级缓存,提升查询性能:

一级缓存:默认开启,作用于 EntityManager 生命周期(同一事务内多次查询同一数据,只查一次数据库)。

二级缓存:全局缓存(跨事务共享),需手动开启(通过 @Cacheable 注解)。

开启二级缓存(以 Hibernate 实现为例):

yaml

spring:

jpa:

properties:

hibernate:

cache:

use_second_level_cache: true # 开启二级缓存

region.factory_class: org.hibernate.cache.ehcache.EhCacheRegionFactory # 指定缓存实现(如 EhCache)

在实体类上添加缓存注解:

java

运行

@Entity

@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) // 二级缓存策略(读写)

public class User { ... }

五、优缺点与适用场景

优点:

简化代码:无需编写 DAO 实现类,通过接口方法签名自动生成 SQL。

面向对象:通过实体类和关联映射操作数据,屏蔽 SQL 细节。

快速开发:适合简单 CRUD 场景,大幅减少重复代码。

跨数据库兼容:通过方言适配不同数据库(如 MySQL、PostgreSQL),切换数据库无需修改 SQL。

缺点:

复杂查询灵活性低:自动生成的 SQL 可能不优,复杂查询需手动编写 JPQL 或原生 SQL。

性能优化成本高:默认查询可能存在冗余(如查询所有字段),需额外配置(如投影查询)。

学习曲线:关联映射、缓存机制等概念较复杂,新手需理解 ORM 核心思想。

适用场景:

简单 CRUD 为主的业务(如后台管理系统)。

快速开发原型或中小型项目。

团队更关注对象模型而非 SQL 细节。

需要跨数据库兼容的场景。

总结

Spring Boot JPA 是简化数据访问层的优秀方案,通过 “接口定义 + 注解配置” 实现无代码 DAO,适合快速开发简单业务。但在复杂查询或性能敏感场景,需结合 JPQL、原生 SQL 或缓存机制优化。实际开发中,需根据业务复杂度平衡开发效率与性能需求。

-------------------------------------------------------------------------------------------

Spring Boot JPA 基于 JPA 规范(Java Persistence API),通过注解实现 实体类与数据库表的映射、查询逻辑定义 等核心功能。掌握这些注解是使用 JPA 进行 ORM 开发的基础。以下是 Spring Boot JPA 中常用注解的详细说明,按功能分类整理:

一、实体与表映射注解(核心基础)

用于将 Java 实体类与数据库表建立映射关系,是 JPA 最基础的注解。

1. @Entity

作用:标记一个类为 JPA 实体类(即该类对应数据库中的一张表)。

位置:类级别(类定义上方)。

注意:

实体类必须有一个无参构造方法(JPA 反射需要)。

若未指定表名,默认表名与类名一致(小写)。

java

运行

@Entity // 标记为实体类,对应数据库表

public class User { ... }

2. @Table

作用:自定义实体类映射的数据库表名、 schema 等信息(补充 @Entity 的表映射细节)。

核心属性:

name:指定数据库表名(默认类名小写)。

schema:指定数据库 schema(如 MySQL 的数据库名,Oracle 的方案名)。

uniqueConstraints:定义表级唯一约束(多个字段组合唯一)。

位置:类级别,与 @Entity 配合使用。

java

运行

@Entity

@Table(

name = "t_user", // 映射到数据库表 t_user

schema = "jpa_demo", // 所属数据库(schema)

uniqueConstraints = { // 表级唯一约束:username 和 phone 组合唯一

@UniqueConstraint(columnNames = {"username", "phone"})

}

)

public class User { ... }

二、字段与列映射注解(属性级)

用于将实体类的属性与数据库表的字段建立映射,控制字段的类型、约束等。

1. @Id

作用:标记实体类的属性为 主键字段(对应数据库表的主键)。

位置:属性级别(或 getter 方法上,推荐属性级)。

注意:每个实体类必须有且仅有一个 @Id 标记的主键。

java

运行

@Entity

public class User {

@Id // 标记为主键

private Long id;

// 其他属性...

}

2. @GeneratedValue

作用:指定主键的 生成策略(如何自动生成主键值)。

核心属性:

strategy:生成策略(必选),取值包括:

GenerationType.IDENTITY:依赖数据库自增主键(如 MySQL 的 AUTO_INCREMENT),最常用。

GenerationType.SEQUENCE:依赖数据库序列(如 Oracle 的 SEQUENCE),需配合 @SequenceGenerator 使用。

GenerationType.TABLE:通过中间表维护主键生成(跨数据库兼容,但性能较差,很少用)。

GenerationType.AUTO:由 JPA 自动选择策略(默认值,不推荐,可能导致跨环境不一致)。

位置:主键属性上,与 @Id 配合使用。

java

运行

@Entity

public class User {

@Id

// 主键自增(适合 MySQL)

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

}

SEQUENCE 策略示例(适合 Oracle):

java

运行

@Entity

public class User {

@Id

// 使用序列生成主键

@GeneratedValue(

strategy = GenerationType.SEQUENCE,

generator = "user_seq" // 关联序列生成器

)

// 定义序列生成器

@SequenceGenerator(

name = "user_seq", // 生成器名称(与 generator 对应)

sequenceName = "SEQ_USER_ID", // 数据库中序列名

allocationSize = 1 // 每次增长步长

)

private Long id;

}

3. @Column

作用:自定义属性映射的数据库字段细节(如字段名、类型、约束等)。

核心属性:

name:数据库字段名(默认与属性名一致)。

nullable:是否允许为 null(默认 true)。

unique:是否唯一约束(默认 false)。

length:字符串长度(默认 255,仅对字符串类型有效)。

precision/scale:用于数值类型(precision 总长度,scale 小数位数,如 DECIMAL(10,2))。

columnDefinition:直接指定字段的数据库定义(如 VARCHAR(50) NOT NULL COMMENT '用户名'),优先级最高。

updatable:是否允许更新(默认 true,设为 false 则 update 时忽略该字段)。

insertable:是否允许插入(默认 true,设为 false 则 insert 时忽略该字段)。

位置:属性级别。

java

运行

@Entity

@Table(name = "t_user")

public class User {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

// 映射到字段 username,非空,唯一,长度 50

@Column(

name = "username", // 字段名

nullable = false, // 非空

unique = true, // 唯一

length = 50 // 长度 50

)

private String username;

// 年龄字段:允许为 null,整数类型

@Column(name = "age")

private Integer age;

// 余额字段:DECIMAL(10,2)(总长度 10,2 位小数)

@Column(

name = "balance",

precision = 10,

scale = 2

)

private BigDecimal balance;

// 自定义字段定义(直接写 SQL 片段)

@Column(

name = "create_time",

columnDefinition = "DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间'"

)

private LocalDateTime createTime;

}

4. @Transient

作用:标记属性 不映射到数据库表(即该属性仅在 Java 代码中使用,与数据库无关)。

位置:属性级别。

注意:若未加此注解,JPA 会默认将所有属性映射到数据库字段(可能导致表结构异常)。

java

运行

@Entity

public class User {

@Id

private Long id;

private String username;

// 临时属性:不映射到数据库(如用于计算的临时结果)

@Transient

private String tempInfo;

}

5. @Temporal(JPA 2.2 前,推荐用 Java 8 时间类)

作用:在使用 java.util.Date 或 java.util.Calendar 时,指定日期时间在数据库中的存储类型(DATE/Time/TIMESTAMP)。

替代方案:Java 8 及以上推荐使用 LocalDate(日期)、LocalTime(时间)、LocalDateTime(日期时间),无需 @Temporal,JPA 会自动映射。

示例:

java

运行

// 旧方式(不推荐)

@Temporal(TemporalType.DATE) // 仅存储日期(如 '2024-09-30')

private Date birthday;

@Temporal(TemporalType.TIMESTAMP) // 存储日期+时间(如 '2024-09-30 12:00:00')

private Date createTime;

// 新方式(推荐)

private LocalDate birthday; // 自动映射为 DATE

private LocalDateTime createTime; // 自动映射为 TIMESTAMP

6. @CreationTimestamp 与 @UpdateTimestamp(Hibernate 扩展,非 JPA 标准)

作用:自动填充创建时间和更新时间(插入时自动设为当前时间,更新时自动刷新)。

注意:这是 Hibernate 的扩展注解(非 JPA 标准),但 Spring Boot JPA 默认集成 Hibernate,可直接使用。

示例:

java

运行

@Entity

public class User {

// 插入时自动设置为当前时间(不随更新变化)

@CreationTimestamp

@Column(name = "create_time", updatable = false) // 禁止更新

private LocalDateTime createTime;

// 插入和更新时自动设置为当前时间

@UpdateTimestamp

@Column(name = "update_time")

private LocalDateTime updateTime;

}

三、关联映射注解(多表关系)

用于定义实体类之间的关系(如一对一、一对多、多对多),对应数据库表的外键关联。

1. @OneToOne(一对一关系)

作用:定义两个实体之间的 一对一关联(如 User 与 UserDetail)。

核心属性:

cascade:级联操作策略(如保存用户时自动保存详情),取值包括:

CascadeType.ALL:所有操作级联(推荐)。

CascadeType.PERSIST:级联保存。

CascadeType.MERGE:级联更新。

CascadeType.REMOVE:级联删除。

fetch:加载策略(默认 FetchType.EAGER 立即加载,推荐 FetchType.LAZY 延迟加载)。

mappedBy:指定关联关系的维护方(在 “被控方” 中使用,值为 “主控方” 中关联属性的名称)。

@JoinColumn:在 “主控方” 中指定外键字段(与 @OneToOne 配合使用)。

示例:用户(User)与用户详情(UserDetail)一对一

java

运行

// 主控方(User):维护外键 detail_id

@Entity

public class User {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

private String username;

// 一对一关联 UserDetail,主控方

@OneToOne(

cascade = CascadeType.ALL, // 级联所有操作(保存用户时自动保存详情)

fetch = FetchType.LAZY // 延迟加载(查询用户时不主动加载详情)

)

@JoinColumn(

name = "detail_id", // 外键字段名(user 表的 detail_id 关联 user_detail 表的 id)

unique = true // 一对一必须唯一(一个详情只能属于一个用户)

)

private UserDetail detail;

}

// 被控方(UserDetail):不维护外键,由 User 维护

@Entity

public class UserDetail {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

private String address;

private String phone;

// 反向关联(可选),mappedBy 指向主控方的关联属性名(User 类的 detail)

@OneToOne(mappedBy = "detail")

private User user;

}

2. @OneToMany 与 @ManyToOne(一对多 / 多对一关系)

作用:定义两个实体之间的 一对多 / 多对一关联(如 User 与 Order:一个用户多个订单,多个订单属于一个用户)。

核心属性:

@OneToMany 用在 “一” 的一方,@ManyToOne 用在 “多” 的一方。

mappedBy:在 @OneToMany 中指定,值为 “多” 的一方中关联属性的名称(表示由 “多” 的一方维护外键)。

cascade 和 fetch:同 @OneToOne(@ManyToOne 默认 FetchType.EAGER,@OneToMany 默认 FetchType.LAZY)。

示例:用户(User)与订单(Order)一对多

java

运行

// “一”的一方(User)

@Entity

public class User {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

private String username;

// 一对多关联订单,由 Order 类的 user 属性维护外键

@OneToMany(

mappedBy = "user", // 关联由 Order.user 维护

cascade = CascadeType.ALL, // 级联操作(保存用户时保存订单)

orphanRemoval = true // 移除集合中的订单时,自动删除数据库记录

)

private List orders = new ArrayList<>(); // 初始化避免空指针

}

// “多”的一方(Order)

@Entity

public class Order {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

private String orderNo;

// 多对一关联用户(维护外键)

@ManyToOne(fetch = FetchType.LAZY) // 延迟加载(查询订单时不主动加载用户)

@JoinColumn(name = "user_id") // 外键字段(order 表的 user_id 关联 user 表的 id)

private User user;

}

3. @ManyToMany(多对多关系)

作用:定义两个实体之间的 多对多关联(如 User 与 Role:一个用户多个角色,一个角色多个用户)。

核心属性:

joinTable:在 “主控方” 中定义中间表(多对多通过中间表实现)。

mappedBy:在 “被控方” 中指定,值为 “主控方” 中关联属性的名称。

注意:多对多关系必须通过中间表关联,中间表的两个外键分别指向两个实体的主键。

示例:用户(User)与角色(Role)多对多

java

运行

// 主控方(User):定义中间表

@Entity

public class User {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

private String username;

@ManyToMany(

cascade = CascadeType.PERSIST, // 级联保存(保存用户时保存角色,但不级联删除)

fetch = FetchType.LAZY

)

// 定义中间表 user_role

@JoinTable(

name = "user_role", // 中间表名

joinColumns = @JoinColumn(name = "user_id"), // 中间表关联当前实体的外键(user_id → user.id)

inverseJoinColumns = @JoinColumn(name = "role_id") // 中间表关联关联实体的外键(role_id → role.id)

)

private Set roles = new HashSet<>(); // 用 Set 避免重复

}

// 被控方(Role):不定义中间表,由 User 维护

@Entity

public class Role {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Long id;

private String roleName;

// 反向关联,mappedBy 指向 User 类的 roles 属性

@ManyToMany(mappedBy = "roles")

private Set users = new HashSet<>();

}

四、查询与操作注解(Repository 层)

用于在 Repository 接口中自定义查询逻辑(替代 SQL)。

1. @Query

作用:在 Repository 方法上定义自定义查询(支持 JPQL 或原生 SQL)。

核心属性:

value:查询语句(JPQL 或 SQL)。

nativeQuery:是否为原生 SQL(默认 false,即 JPQL;设为 true 则使用数据库原生 SQL)。

countQuery:分页查询时的总数查询语句(可选)。

参数绑定:通过 @Param 注解指定参数名,与查询语句中的 :参数名 对应。

示例 1:JPQL 查询(面向实体,非表)

java

运行

public interface UserRepository extends JpaRepository {

// JPQL:查询年龄大于指定值的用户(表名用实体类名 User,字段用属性名)

@Query("SELECT u FROM User u WHERE u.age > :minAge")

List findByAgeGreaterThan(@Param("minAge") Integer minAge);

}

示例 2:原生 SQL 查询(面向数据库表)

java

运行

public interface UserRepository extends JpaRepository {

// 原生 SQL:查询指定日期后创建的用户(表名用实际表名 t_user,字段用数据库字段名)

@Query(

value = "SELECT * FROM t_user WHERE create_time > :startTime",

nativeQuery = true // 标记为原生 SQL

)

List findByCreateTimeAfter(@Param("startTime") LocalDateTime startTime);

}

2. @Modifying

作用:标记 @Query 注解的方法为 修改操作(UPDATE/DELETE),而非查询。

注意:

必须与 @Query 配合使用。

执行修改操作的方法必须在事务中运行(需在 Service 层添加 @Transactional 注解)。

java

运行

public interface UserRepository extends JpaRepository {

// 更新用户年龄

@Modifying // 标记为修改操作

@Query("UPDATE User u SET u.age = :newAge WHERE u.id = :id")

int updateAgeById(@Param("id") Long id, @Param("newAge") Integer newAge);

// 删除指定年龄以下的用户

@Modifying

@Query("DELETE FROM User u WHERE u.age < :maxAge")

int deleteByAgeLessThan(@Param("maxAge") Integer maxAge);

}

// Service 层需添加事务注解

@Service

public class UserService {

@Autowired

private UserRepository userRepository;

@Transactional // 必须开启事务

public void updateUserAge(Long id, Integer newAge) {

userRepository.updateAgeById(id, newAge);

}

}

3. @Param

作用:在 Repository 方法参数前标记参数名,便于在 @Query 语句中通过 :参数名 引用。

位置:方法参数级别。

注意:若方法参数只有一个且为基本类型,可省略 @Param,但多参数时必须添加(避免混淆)。

java

运行

// 多参数必须用 @Param 标记

@Query("SELECT u FROM User u WHERE u.username LIKE %:name% AND u.age > :age")

List findByUsernameLikeAndAgeGreaterThan(

@Param("name") String username,

@Param("age") Integer age

);

五、其他常用注解

1. @Repository

作用:标记接口为数据访问层(DAO)组件,Spring 会自动扫描并注入该接口的实现(JPA 自动生成代理实现)。

注意:Spring Data JPA 的 Repository 接口继承 JpaRepository 后,可省略 @Repository(Spring 会自动识别),但显式添加更清晰。

java

运行

@Repository // 标记为数据访问组件

public interface UserRepository extends JpaRepository { ... }

2. @Transactional(Spring 注解,非 JPA 标准)

作用:声明方法或类需要在事务中运行(如新增、更新、删除操作),确保数据一致性。

核心属性:

readOnly = true:声明为只读事务(查询操作,可优化性能)。

rollbackFor:指定哪些异常触发回滚(默认 RuntimeException 及其子类)。

propagation:事务传播行为(如 REQUIRED、REQUIRES_NEW 等)。

位置:Service 层的方法或类级别(推荐方法级,更灵活)。

java

运行

@Service

public class UserService {

@Autowired

private UserRepository userRepository;

// 只读事务(查询操作)

@Transactional(readOnly = true)

public User getUserById(Long id) {

return userRepository.findById(id).orElse(null);

}

// 读写事务(更新操作)

@Transactional(rollbackFor = Exception.class) // 所有异常都回滚

public void updateUser(User user) {

userRepository.save(user);

}

}

总结

Spring Boot JPA 的注解是实现 ORM 映射的核心,按功能可分为:

实体映射:@Entity、@Table 定义类与表的关系;

字段映射:@Id、@GeneratedValue、@Column 定义属性与字段的关系;

关联映射:@OneToOne、@OneToMany、@ManyToMany 定义多表关系;

查询操作:@Query、@Modifying、@Param 定义自定义查询逻辑。

掌握这些注解的使用场景和属性,能帮助开发者高效实现数据访问层,减少重复代码,同时灵活处理复杂业务场景(如多表关联、自定义查询)。实际开发中,需根据数据库类型(如 MySQL/Oracle)和业务需求选择合适的注解配置。

-------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------

相关推荐

工业 DTU:工业通信的隐形守护者
365bet备用

工业 DTU:工业通信的隐形守护者

📅 07-27 👁️ 4428
从电视台主播到顶流新人!99 后 “最强女主播”,气质直接封神
好e借下款经历解析:好e借轻松下款5000元
28365备用网址官方网站

好e借下款经历解析:好e借轻松下款5000元

📅 08-16 👁️ 5392