MyBatis 与 JPA 的核心对比

news/2025/9/30 11:27:12/文章来源:https://www.cnblogs.com/hanease/p/19120252

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

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

复杂业务逻辑通常涉及 动态 SQL(多条件拼接)、多表关联查询、嵌套对象映射、批量操作、子查询 等场景。两种方式在这些场景下的处理能力和体验有显著区别:
复杂场景Mapper 注解方式XML 映射文件方式
动态 SQL 处理 需通过 @SelectProvider 等 Provider 类 实现,需在 Java 代码中用 SQL 类拼接 SQL(类似字符串拼接),逻辑分散在 Java 类中,可读性差,维护困难。
 
例:多条件查询需手动判断参数非空并拼接 WHERE 子句,容易出错。
原生支持 <if><where><foreach><choose> 等动态标签,SQL 逻辑与条件判断在 XML 中结构化呈现,直观清晰,便于修改。
 
例:用 <if test="name != null"> 直接控制条件拼接,无需手动处理 AND 前缀。
多表关联与结果映射 复杂结果映射(如嵌套对象、集合)需通过 @Results + @Result 注解手动配置,多层嵌套时注解冗长,易混淆。
 
例:用户关联部门 + 订单列表,需嵌套多个 @Result,可读性差。
通过 <resultMap> 标签统一管理映射关系,支持 <association>(一对一)、<collection>(一对多)等标签,清晰表达对象间关系,多层嵌套结构一目了然。
 
例:用户→部门→订单列表的映射可在一个 resultMap 中分层定义。
批量操作(插入 / 更新) 批量插入需在注解中写长 SQL 字符串(如 VALUES (#{u1.name}), (#{u2.name})...),参数多时字符串拼接繁琐,易出错,且不支持动态集合长度。 通过 <foreach> 标签遍历集合动态生成 SQL,支持任意长度的批量操作,语法简洁(如 VALUES <foreach collection="list" item="u" separator=",">(#{u.name})</foreach>)。
子查询与复杂 JOIN 子查询或多表 JOIN 的 SQL 语句冗长,写在注解中会导致接口类臃肿,且换行、缩进等格式难以维护,影响可读性。 XML 中可通过格式化 SQL(换行、缩进)清晰表达子查询和 JOIN 逻辑,甚至可拆分 SQL 为多行便于理解(如将 LEFT JOIN 拆分到单独行)。
SQL 复用与模块化 无法直接复用 SQL 片段,相同逻辑需在多个注解中重复编写,冗余度高。 支持 <sql> 标签定义可复用 SQL 片段(如通用查询字段、条件),通过 <include> 标签引用,减少重复代码,便于统一维护。
团队协作与 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
 
 
<!-- Spring Data JPA -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL 驱动 -->
<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope>
</dependency>
 

2. 配置数据源与 JPA 特性

在 application.yml 中配置数据库连接和 JPA 行为:
yaml
 
 
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/jpa_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghaiusername: rootpassword: rootjpa:hibernate:ddl-auto: update  # 自动创建/更新表结构(create:每次启动重建表;update:保留数据更新结构;none:关闭)show-sql: true      # 控制台打印 SQLproperties:hibernate:format_sql: true  # 格式化 SQLdatabase-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<User, Long> {// 可自定义查询方法(Spring Data JPA 自动解析方法名生成 SQL)
}
 
Spring Data JPA 核心接口:
  • Repository:最基础接口,无任何方法。
  • CrudRepository:继承 Repository,提供 CRUD 方法(savefindByIdfindAlldelete 等)。
  • 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 {@Autowiredprivate 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<User> 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<T>)。
  • findAll():查询所有。
  • deleteById(ID id):根据主键删除。
  • count():统计总数。

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

Spring Data JPA 支持通过 “方法名规则” 自动生成 SQL,无需手动编写。规则是:动词 + 条件(属性 + 关键字)
常用关键字:By(条件)、And/Or(多条件)、LikeBetweenOrderByTop 等。
示例:
java
 
运行
 
 
 
 
public interface UserRepository extends JpaRepository<User, Long> {// 1. 根据 username 查询(等价于:SELECT * FROM t_user WHERE username = ?)User findByUsername(String username);// 2. 根据 age 大于指定值查询(等价于:SELECT * FROM t_user WHERE age > ?)List<User> findByAgeGreaterThan(Integer age);// 3. 根据 username 模糊查询 + 年龄范围(等价于:SELECT * FROM t_user WHERE username LIKE %?% AND age BETWEEN ? AND ?)List<User> findByUsernameLikeAndAgeBetween(String username, Integer minAge, Integer maxAge);// 4. 查询年龄最大的 3 条数据(等价于:SELECT * FROM t_user ORDER BY age DESC LIMIT 3)List<User> 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<User, Long> {// 1. JPQL(表名用实体类名,字段用属性名)@Query("SELECT u FROM User u WHERE u.age > :age AND u.username LIKE %:name%")List<User> 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<User> 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 {@Autowiredprivate UserRepository userRepository;// 分页查询(第 1 页,每页 10 条,按 createTime 降序)public Page<User> 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<User> 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<Order> 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 {@Autowiredprivate UserRepository userRepository;// 声明事务(默认:发生异常时回滚)@Transactionalpublic void batchCreate(List<User> users) {userRepository.saveAll(users);// 若中途抛出异常,所有保存操作会回滚}// 自定义事务属性(如只读、超时时间)@Transactional(readOnly = true, timeout = 10)  // 只读事务,超时 10 秒public List<User> getUsersWithLock() {// 复杂查询逻辑return userRepository.findAll();}
}
 

四、缓存机制

JPA 自带两级缓存,提升查询性能:
  1. 一级缓存:默认开启,作用于 EntityManager 生命周期(同一事务内多次查询同一数据,只查一次数据库)。
  2. 二级缓存:全局缓存(跨事务共享),需手动开启(通过 @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_userschema = "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 {@Idprivate Long id;private String username;// 临时属性:不映射到数据库(如用于计算的临时结果)@Transientprivate 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<Order> 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<Role> 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<User> 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<User, Long> {// JPQL:查询年龄大于指定值的用户(表名用实体类名 User,字段用属性名)@Query("SELECT u FROM User u WHERE u.age > :minAge")List<User> findByAgeGreaterThan(@Param("minAge") Integer minAge);
}
 
示例 2:原生 SQL 查询(面向数据库表)
java
 
运行
 
 
 
 
public interface UserRepository extends JpaRepository<User, Long> {// 原生 SQL:查询指定日期后创建的用户(表名用实际表名 t_user,字段用数据库字段名)@Query(value = "SELECT * FROM t_user WHERE create_time > :startTime",nativeQuery = true  // 标记为原生 SQL)List<User> findByCreateTimeAfter(@Param("startTime") LocalDateTime startTime);
}
 

2. @Modifying

  • 作用:标记 @Query 注解的方法为 修改操作(UPDATE/DELETE),而非查询。
  • 注意:
    • 必须与 @Query 配合使用。
    • 执行修改操作的方法必须在事务中运行(需在 Service 层添加 @Transactional 注解)。
java
 
运行
 
 
 
 
public interface UserRepository extends JpaRepository<User, Long> {// 更新用户年龄@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 {@Autowiredprivate 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<User> 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<User, Long> { ... }
 

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

  • 作用:声明方法或类需要在事务中运行(如新增、更新、删除操作),确保数据一致性。
  • 核心属性:
    • readOnly = true:声明为只读事务(查询操作,可优化性能)。
    • rollbackFor:指定哪些异常触发回滚(默认 RuntimeException 及其子类)。
    • propagation:事务传播行为(如 REQUIREDREQUIRES_NEW 等)。
  • 位置:Service 层的方法或类级别(推荐方法级,更灵活)。
java
 
运行
 
 
 
 
@Service
public class UserService {@Autowiredprivate 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)和业务需求选择合适的注解配置。

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/922769.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

完整教程:Redis 提供了两种主要的持久化机制:RDB 和 AOF

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

本土化战略赋能:Gitee如何领跑中国DevOps黄金赛道

本土化战略赋能:Gitee如何领跑中国DevOps黄金赛道 中国软件开发领域正在经历一场深刻的效率革命。随着国家"十四五"数字经济规划明确要求企业加速数字化转型,DevOps作为连接开发与运维的关键技术栈,其市场…

陕西省建设注册中心网站建设网站需要哪些

时序数据库全称为时间序列数据库。时间序列数据库指主要用于处理带时间标签&#xff08;按照时间的顺序变化&#xff0c;即时间序列化&#xff09;的数据&#xff0c;带时间标签的数据也称为时间序列数据。 时间序列数据主要由电力行业、化工行业、气象行业、地理信息等各类型…

打印机错误0x0000709,问题排查和修复指南

办公时突然弹出打印机报错窗口,电脑显示打印机错误 0x0000709不知道什么意思,连重新连接打印机都不管用,急着打文件的话真的很闹心。但其实打印机错误码是有规律的,不同代码对应不同故障原因,比如 0x0000709 多和…

中国科技成就素材seo引擎优化

AlternationCount属性&#xff1a;表示有几行不同的颜色来回替换&#xff0c;如果设置2则表示有两个颜色交替循环 AutoGenerateColumns属性&#xff1a;是否生成列 CanUserAddRows属性&#xff1a;用户是否可以添加行 CanUserDeleteRows属性&#xff1a;用户是否可以删除行 …

2025.9.29 测试

2025.9.29 测试T1. 马赛克上色 就是个随机题目 告诉我们烤柿要大胆 所有点都是偶度数,直接构造欧拉回路,三个截一段输出 然后有奇数点呢? 这里构出一种正确性很高的 trick 度数越小要求越严格,于是我们每次选出一个…

深度学习(CVAE)

自编码器这类模型有下面几个: 自编码器(AE):目标是压缩与重建。它是一个判别模型,目标是学习数据的高效表示,主要用于降维、去噪和数据压缩,而不是生成新数据。 变分自编码器(VAE): 学习数据的概率分布并生成…

c# aot orm 框架测试 mysql

SqlSugar 的文档比较齐全,一次通过:https://www.donet5.com/Home/Doc?typeId=2574,但是 SqlSugar Aot 发布在 40M 左右,感觉太大了点。 FreeSql 的 AOT 文档就不太友好了,试了一下各种报错,最后发现要使用 Free…

深入解析:论文阅读:硕士学位论文 2025 面向大语言模型的黑盒对抗性攻击与防御关键技术研究

深入解析:论文阅读:硕士学位论文 2025 面向大语言模型的黑盒对抗性攻击与防御关键技术研究2025-09-30 11:09 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !import…

网站常用的js效果做的网站被挂马

Java 汉字转拼音 1.TinyPinyin 功能&#xff1a; 适用于Java和Android的快速、低内存占用的汉字转拼音库。 特性&#xff1a; 生成的拼音不包含声调&#xff0c;均为大写&#xff1b;支持自定义词典&#xff0c;支持简体中文、繁体中文&#xff1b;执行效率很高(Pinyin4J的…

怎么给汽车网站做推广郑州建设局官网

1、使用父子关系调整下使其更加整洁 2、比如说我修改了下url,那所有的页面都要更改 优化&#xff1a;把这个url抽出来&#xff0c;新建一个Api文件夹用于存放所有接口的url&#xff0c;在业务里只需要关注业务就可以 使用时 导包 发请求 如果想要更改路径&#xff0c;在这里…

PK-2877电流互感器在高频脉冲电源模块测试中的应用方案

一、项目背景与需求 一家专注于高频脉冲电源模块研发与生产的电子设备企业,其产品广泛应用于工业自动化控制、通信基站电源等领域。这些电源模块在工作时会产生高频脉冲电流,电流峰值可达100A,脉冲频率在数百kHz至数…

VC++ 使用OpenSSL创建RSA密钥PEM档案

VC++ 使用OpenSSL创建RSA密钥PEM档案pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monac…

CF1699D Almost Triple Deletions

被神秘贪心标签误导了。 你考虑答案的最终形式长什么样,就是保留若干个相同的数,再将其中间的区间整段整段删干净。 你先枚举保留什么数,然后发现我们可以设 \(f_{i}\) 表示到了第 \(i\) 个位置最多能保留多少个数,…

QMT回测模式为什么要在副图进行

在QMT系统中,回测必须以副图模式进行,主要有以下原因: (1)数据处理与性能优化 副图模式允许策略专注于历史数据的分析和计算,避免与主图的实时行情显示产生冲突。回测过程中,系统需要遍历大量历史K线数据,副图…

DAY20 Channel(通道)NIO(同步,非阻塞)、Selector(选择器)、NIO2-AIO(异步、非阻塞) - 指南

DAY20 Channel(通道)NIO(同步,非阻塞)、Selector(选择器)、NIO2-AIO(异步、非阻塞) - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !impor…

详细介绍:Servlet完全上手:核心语法与生命周期详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

详细介绍:支持17种方言10种外语!阿里最新AI语音合成模型Qwen3-TTS-Flash震撼发布

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

判断权限通过遍历二叉树路由删除权限不展示的前端组件

判断权限通过遍历二叉树路由删除权限不展示的前端组件点击查看代码 def clean_node(nodes, full_name):names = full_name.split(.)current_name = names[0]for i, node in enumerate(nodes):assert isinstance(node, …

外国人做的网站wordpress主题大全

文章目录 rollup watch 实现流程watchWatchEmitter 实现 watchInternalWatcher 管理整个 watch 阶段Task 运行任务FileWatcher 实现文件监听 rollup watch 实现流程 每一个配置了 watch 的配置项都会变成一个 Task 任务&#xff0c;每个任务通过 FileWatcher 即 chokidar 进行…