如何在Spring Data JPA中通过单查询获取多表关联结果?
这种情况我之前也碰到过,大概率是你的AddressData实体类中存在与Address的关联映射(比如@ManyToOne或@OneToOne),并且该关联的fetch类型为EAGER——很多关联注解的默认fetch类型就是EAGER,比如@ManyToOne,这就是问题的根源。
当你用createNativeQuery(query, AddressData.class)把原生查询结果映射到实体类时,Hibernate会先执行你写的内连接SQL获取address_data的数据,但因为关联的Address对象是EAGER加载模式,它会额外触发一条查询去拉取每个AddressData对应的Address记录,最终就出现了两条查询语句。
几个可行的解决方法
方法1:把关联改成懒加载(LAZY)
如果你的业务场景里不需要每次都加载Address对象,直接修改AddressData中的关联注解,设置fetch = FetchType.LAZY就行:
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "ad_id") // 根据你的实际关联字段调整 private Address address;
这样Hibernate只会在你实际调用AddressData.getAddress()时才会触发查询,而不是默认就加载关联对象。
方法2:用DTO接收结果(推荐不需要实体关联时用)
如果你的需求只是获取address_data的部分或全部数据,不需要关联的Address实体,可以定义一个简单的DTO类,让原生查询直接映射到DTO:
// 定义一个简单的DTO public class AddressDataDTO { private Long adId; private String addressLine; // 其他你需要的字段 // 注意:构造方法的参数顺序要和查询结果的列顺序完全一致 public AddressDataDTO(Long adId, String addressLine) { this.adId = adId; this.addressLine = addressLine; } // getter和setter按需添加 } // 对应的查询代码 String query = "Select ad.ad_id, ad.address_line from address_data ad inner join address a ON a.ad_id = ad.ad_id where a.address = :address"; Query q = entityManager.createNativeQuery(query) .setResultTransformer(new AliasToBeanResultTransformer(AddressDataDTO.class)); q.setParameter("address", address); List<AddressDataDTO> permitList = q.getResultList();
方法3:用@SqlResultSetMapping同时加载关联数据(如果确实需要关联对象)
如果你必须同时获取AddressData和对应的Address,可以通过@SqlResultSetMapping定义结果映射,让Hibernate一次性从原生查询中拿到所有数据,避免额外查询:
// 在AddressData实体类上添加映射定义 @Entity @SqlResultSetMapping( name = "AddressDataWithAddress", entities = { @EntityResult(entityClass = AddressData.class), @EntityResult(entityClass = Address.class) } ) public class AddressData { // 你的实体字段和关联映射代码 } // 查询代码 String query = "Select ad.*, a.* from address_data ad inner join address a ON a.ad_id = ad.ad_id where a.address = :address"; Query q = entityManager.createNativeQuery(query, "AddressDataWithAddress"); q.setParameter("address", address); List<Object[]> results = q.getResultList(); // 从结果数组中提取实体并关联 List<AddressData> permitList = new ArrayList<>(); for (Object[] result : results) { AddressData ad = (AddressData) result[0]; Address a = (Address) result[1]; ad.setAddress(a); // 手动把Address关联到AddressData上 permitList.add(ad); }
这样Hibernate只会执行一次原生查询,同时返回两个实体的数据,你只需要手动关联它们就可以了。
内容的提问来源于stack exchange,提问作者Krish

