Spring Data JPA(PostgreSQL)中不必要关联查询的解决办法
Spring Boot中@OneToMany关联引发不必要关联查询的解决方法
问题背景
我有一个Spring Boot应用,实体间存在@OneToMany关联关系,核心代码如下:
ThingType.java
@Entity public class ThingType { @Id private String thingTypeId; @OneToMany(orphanRemoval=true,mappedBy="thingType",cascade = CascadeType.ALL,fetch = FetchType.LAZY) private List<Thing> things; }
Thing.java
@Entity public class Thing { @Id private String thingId; @ManyToOne(fetch = FetchType.LAZY,optional = false) @JoinColumn(name = "thingType",nullable = false) @JsonIgnore private ThingType thingType; }
ThingTypeRepository.java
@Repository public interface ThingTypeRepository extends JpaRepository<ThingType, String> { @Override Optional<ThingType> findById(String s); @Query("select t from ThingType t where t.thingTypeId = ?1") Optional<ThingType> findThingTypeById(@NonNull String thingTypeId); }
ThingTypeController.java
@Autowired private ThingTypeService thingTypeService; @RestController public class ThingController{ @GetMapping("/{id}") public ResponseEntity<Object> getThing(@PathVariable("id") String id){ ThingType thingType = thingTypeService.getThingType(id); return ResponseEntity.status(HttpStatus.OK).body(thingType); } }
ThingTypeService.java
@Service public class ThingTypeService{ @Autowired private ThingTypeRepository thingTypeRepository; public ThingType getThingType(String thingTypeId) { ThingType thingType = thingTypeRepository.findThingTypeById(thingTypeId).orElse(null); if(thingType == null){ throw new ResultNotFoundException(); } return thingType; } }
最初使用findById查询单个ThingType时,执行的是表关联查询而非单表查询。添加optional=false和nullable=false后问题依旧,自定义@Query方法也无法解决。
解决方案
1. 阻止序列化触发懒加载
虽然配置了FetchType.LAZY,但控制器直接返回ThingType实体时,Jackson序列化可能会触发懒加载(即使Thing上加了@JsonIgnore)。给实体类添加@JsonIgnoreProperties忽略Hibernate懒加载代理属性:
@Entity @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) public class ThingType { // 原有代码 } @Entity @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) public class Thing { // 原有代码 }
2. 用@EntityGraph强制单表查询
在仓库方法上添加@EntityGraph,明确指定只加载主表属性,不加载关联集合:
@Repository public interface ThingTypeRepository extends JpaRepository<ThingType, String> { @Override @EntityGraph(attributePaths = {}, type = EntityGraph.EntityGraphType.LOAD) Optional<ThingType> findById(String s); @Query("select t from ThingType t where t.thingTypeId = ?1") @EntityGraph(attributePaths = {}, type = EntityGraph.EntityGraphType.LOAD) Optional<ThingType> findThingTypeById(@NonNull String thingTypeId); }
attributePaths = {}表示不加载任何关联属性,强制Hibernate执行单表查询。
3. 检查Hibernate懒加载全局配置
确保application.properties中没有开启事务外懒加载:
# 禁止事务外触发懒加载 spring.jpa.properties.hibernate.enable_lazy_load_no_trans=false
如果开启该属性,即使未主动访问关联集合,序列化操作也可能在事务外触发关联查询。
4. 使用DTO隔离实体与返回结果
创建ThingTypeDTO只包含需要返回的字段,在服务层将实体转换为DTO返回,彻底避免懒加载触发:
// DTO类 public class ThingTypeDTO { private String thingTypeId; // 构造器、getter、setter public ThingTypeDTO(String thingTypeId) { this.thingTypeId = thingTypeId; } public String getThingTypeId() { return thingTypeId; } }
修改服务层代码:
@Service public class ThingTypeService{ @Autowired private ThingTypeRepository thingTypeRepository; public ThingTypeDTO getThingType(String thingTypeId) { ThingType thingType = thingTypeRepository.findThingTypeById(thingTypeId) .orElseThrow(ResultNotFoundException::new); return new ThingTypeDTO(thingType.getThingTypeId()); } }
控制器返回DTO:
@GetMapping("/{id}") public ResponseEntity<Object> getThing(@PathVariable("id") String id){ ThingTypeDTO thingTypeDTO = thingTypeService.getThingType(id); return ResponseEntity.status(HttpStatus.OK).body(thingTypeDTO); }
内容的提问来源于stack exchange,提问作者Badhusha
相关产品推荐
相关产品推荐

