尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

SpringBoot 整合 JPA:从入门到精通

SpringBoot 整合 JPA:从入门到精通 1. 引言Spring Boot 作为当前 Java 后端开发的主流框架以其「约定优于配置」的理念大幅简化了企业级应用的搭建过程。而在数据持久化层面Spring Data JPA 凭借其强大的抽象能力让开发者可以用极少的样板代码完成数据库操作成为 Spring 生态中最受欢迎的 ORM 方案之一。本文将从零开始带你完整走一遍 Spring Boot 整合 JPA 的全过程从环境准备、基础配置到实体映射、Repository 使用再到复杂查询、事务管理与性能优化循序渐进最终达到「从入门到精通」的目标。2. 环境准备在开始编码之前我们需要准备好开发环境。本文使用的技术栈如下JDK 17Maven 3.6 或 Gradle 7Spring Boot 3.x数据库MySQL 8.x也可使用 H2 内存数据库快速体验2.1 创建项目推荐使用 Spring Initializr 快速创建项目勾选以下依赖Spring WebSpring Data JPAMySQL Driver或 H2 Database如果你使用 Maven核心依赖如下dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-jpa/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdcom.mysql/groupIdartifactIdmysql-connector-j/artifactIdscoperuntime/scope/dependency2.2 配置数据源在application.yml中配置数据源与 JPA 相关属性spring:datasource:url:jdbc:mysql://localhost:3306/demo?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghaiusername:rootpassword:your_passworddriver-class-name:com.mysql.cj.jdbc.Driverjpa:hibernate:ddl-auto:updateshow-sql:trueproperties:hibernate:format_sql:true提示ddl-auto在开发阶段可设为update生产环境建议使用validate或交由 Flyway/Liquibase 管理。3. 实体映射JPA 的核心思想是将 Java 对象与数据库表建立映射关系。下面我们创建一个User实体。3.1 基础实体packagecom.example.demo.entity;importjakarta.persistence.*;importjava.time.LocalDateTime;EntityTable(namet_user)publicclassUser{IdGeneratedValue(strategyGenerationType.IDENTITY)privateLongid;Column(nullablefalse,uniquetrue,length50)privateStringusername;Column(nullablefalse)privateStringpassword;privateStringemail;privateIntegerage;Column(namecreated_at,updatablefalse)privateLocalDateTimecreatedAt;Column(nameupdated_at)privateLocalDateTimeupdatedAt;// 生命周期回调插入前自动填充创建时间PrePersistpublicvoidprePersist(){this.createdAtLocalDateTime.now();this.updatedAtLocalDateTime.now();}PreUpdatepublicvoidpreUpdate(){this.updatedAtLocalDateTime.now();}// getter / setter 省略}3.2 常用注解说明注解作用Entity标记该类为 JPA 实体Table指定映射的表名Id声明主键GeneratedValue主键生成策略Column字段映射及约束Transient不映射到数据库的字段PrePersist/PreUpdate生命周期回调方法4. Repository 层Spring Data JPA 最大的亮点在于你只需要定义一个接口就能获得一整套 CRUD 能力。4.1 基础 Repositorypackagecom.example.demo.repository;importcom.example.demo.entity.User;importorg.springframework.data.jpa.repository.JpaRepository;importorg.springframework.stereotype.Repository;RepositorypublicinterfaceUserRepositoryextendsJpaRepositoryUser,Long{}仅仅这一个接口你就已经拥有了save、findById、findAll、deleteById、count等常用方法无需编写任何实现类。4.2 派生查询方法Spring Data JPA 会根据方法名自动解析 SQL无需手写查询语句publicinterfaceUserRepositoryextendsJpaRepositoryUser,Long{// 按用户名精确查询OptionalUserfindByUsername(Stringusername);// 按年龄区间查询ListUserfindByAgeBetween(Integerstart,Integerend);// 模糊查询 排序ListUserfindByUsernameContainingOrderByCreatedAtDesc(Stringkeyword);// 多条件查询ListUserfindByEmailNotNullAndAgeGreaterThan(Integerage);// 统计longcountByAgeGreaterThan(Integerage);// 判断是否存在booleanexistsByUsername(Stringusername);}4.3 方法命名规则速查关键字示例对应 SQL 片段AndfindByNameAndAgeWHERE name ? AND age ?OrfindByNameOrAgeWHERE name ? OR age ?BetweenfindByAgeBetweenWHERE age BETWEEN ? AND ?LessThanfindByAgeLessThanWHERE age ?GreaterThanEqualfindByAgeGreaterThanEqualWHERE age ?ContainingfindByNameContainingWHERE name LIKE %?%OrderByfindByAgeOrderByNameDescORDER BY name DESCTop/Top3findTop3ByOrderByAgeDescLIMIT 35. 复杂查询当方法名无法满足需求时可以使用Query注解编写 JPQL 或原生 SQL。5.1 JPQL 查询publicinterfaceUserRepositoryextendsJpaRepositoryUser,Long{Query(SELECT u FROM User u WHERE u.age :minAge AND u.age :maxAge)ListUserfindUsersByAgeRange(Param(minAge)IntegerminAge,Param(maxAge)IntegermaxAge);Query(SELECT u.username, u.email FROM User u WHERE u.age :age)ListObject[]findUsernameAndEmailByAgeGreaterThan(Param(age)Integerage);}5.2 原生 SQL 查询publicinterfaceUserRepositoryextendsJpaRepositoryUser,Long{Query(valueSELECT * FROM t_user WHERE age :age,nativeQuerytrue)ListUserfindUsersByNativeSql(Param(age)Integerage);}5.3 分页与排序ServicepublicclassUserService{AutowiredprivateUserRepositoryuserRepository;publicPageUsergetUsers(intpage,intsize,StringsortBy){PageablepageablePageRequest.of(page,size,Sort.by(sortBy).descending());returnuserRepository.findAll(pageable);}// 带条件的分页查询publicPageUsersearchUsers(Stringkeyword,Pageablepageable){returnuserRepository.findByUsernameContaining(keyword,pageable);}}6. 事务管理JPA 的所有写操作都应在事务中执行。Spring 通过Transactional注解声明式地管理事务。6.1 基本用法ServicepublicclassUserService{AutowiredprivateUserRepositoryuserRepository;TransactionalpublicvoidcreateUserWithValidation(Useruser){if(userRepository.existsByUsername(user.getUsername())){thrownewRuntimeException(用户名已存在);}userRepository.save(user);}Transactional(readOnlytrue)publicUsergetUserById(Longid){returnuserRepository.findById(id).orElseThrow(()-newRuntimeException(用户不存在));}}6.2 事务传播行为传播行为说明REQUIRED默认有事务则加入无则新建REQUIRES_NEW总是新建事务挂起当前事务NESTED嵌套事务回滚到保存点SUPPORTS有事务则加入无则非事务执行Transactional(propagationPropagation.REQUIRES_NEW)publicvoidlogOperation(Stringmessage){// 即使外层事务回滚此操作也会提交}7. 关联关系映射实际业务中实体之间往往存在关联关系。下面以「用户-订单」为例演示一对多映射。7.1 一对多映射EntityTable(namet_user)publicclassUser{IdGeneratedValue(strategyGenerationType.IDENTITY)privateLongid;privateStringusername;OneToMany(mappedByuser,cascadeCascadeType.ALL,orphanRemovaltrue)privateListOrderordersnewArrayList();// 辅助方法维护双向关联publicvoidaddOrder(Orderorder){orders.add(order);order.setUser(this);}}EntityTable(namet_order)publicclassOrder{IdGeneratedValue(strategyGenerationType.IDENTITY)privateLongid;privateStringorderNo;privateBigDecimalamount;ManyToOne(fetchFetchType.LAZY)JoinColumn(nameuser_id)privateUseruser;}7.2 抓取策略策略说明适用场景EAGER默认 ManyToOne立即加载关联对象关联对象必须使用且数据量小LAZY延迟加载使用时才查询推荐默认使用避免 N1 问题注意OneToMany默认是LAZYManyToOne默认是EAGER。建议显式声明fetch FetchType.LAZY以保持行为一致。8. 性能优化8.1 避免 N1 查询N1 问题是指查询 1 条主记录后又额外发出 N 条查询加载关联对象。解决方案是使用JOIN FETCHpublicinterfaceOrderRepositoryextendsJpaRepositoryOrder,Long{Query(SELECT o FROM Order o JOIN FETCH o.user WHERE o.id :id)OptionalOrderfindByIdWithUser(Param(id)Longid);}8.2 使用 EntityGraphpublicinterfaceOrderRepositoryextendsJpaRepositoryOrder,Long{EntityGraph(attributePaths{user})Query(SELECT o FROM Order o)ListOrderfindAllWithUser();}8.3 批量操作ModifyingQuery(UPDATE User u SET u.age u.age 1 WHERE u.age :maxAge)intincreaseAgeForAll(Param(maxAge)IntegermaxAge);注意Modifying操作必须配合事务使用且执行后一级缓存可能失效必要时调用entityManager.clear()。9. 常见问题与最佳实践9.1 常见问题问题解决方案表名或字段名冲突使用Table/Column显式指定名称懒加载报LazyInitializationException在事务内访问关联对象或使用JOIN FETCH循环引用导致 JSON 序列化失败使用JsonIgnore或 DTO 投影大批量插入性能差使用saveAll批量保存或 JDBC Batch9.2 最佳实践实体设计字段尽量使用包装类型Long、Integer避免基本类型默认值带来的歧义。Repository 命名方法名语义化避免过长的派生查询方法名。DTO 投影查询不需要的字段时使用接口投影或Query返回 DTO减少数据传输量。事务边界事务应放在 Service 层避免在 Controller 层直接操作 Repository。分页查询大表查询务必使用分页禁止findAll()全量加载。10. 总结本文从环境搭建开始系统性地介绍了 Spring Boot 整合 JPA 的完整链路实体映射掌握Entity、Table、Column等核心注解Repository 层利用派生查询与Query覆盖绝大多数查询场景事务管理理解Transactional的传播行为与回滚机制关联映射掌握一对多、多对一关系及抓取策略性能优化通过JOIN FETCH、EntityGraph和批量操作规避常见性能陷阱。JPA 的学习曲线虽然略陡但一旦掌握其设计思想就能大幅提升开发效率。建议读者在本地动手实践本文的每个示例并结合官方文档深入理解底层原理逐步从「会用」走向「精通」。
返回列表