Hibernate Spring JPA只加载特定的惰性关系

Hibernate Spring JPA只加载特定的惰性关系,hibernate,jpa,spring-data-jpa,lazy-loading,lazy-initialization,Hibernate,Jpa,Spring Data Jpa,Lazy Loading,Lazy Initialization,我在Spring、Hibernate和惰性加载方面有一些困难。有很多问题和答案,但不是我真正想要的 假设我有一辆汽车课。具有id,名称,以及与门,窗和轮子的一对多关系。因为它们是oneToMany,所以它们是默认的延迟加载,这是首选的,因为当我想查看汽车的名称时,我不想看到轮胎和其他东西。在我的例子中,使用我的存储库的默认方法findOne() 但是当我想查看轮胎压力时,我需要初始化轮胎关系。我过去常常使用Hibernate.initialize(car.getTires())来实现这一点。这将

我在Spring、Hibernate和惰性加载方面有一些困难。有很多问题和答案,但不是我真正想要的

假设我有一辆
汽车
课。具有
id
名称
,以及与
轮子
的一对多关系。因为它们是oneToMany,所以它们是默认的延迟加载,这是首选的,因为当我想查看
汽车的名称时,我不想看到轮胎和其他东西。在我的例子中,使用我的存储库的默认方法
findOne()

但是当我想查看轮胎压力时,我需要初始化
轮胎
关系。我过去常常使用
Hibernate.initialize(car.getTires())
来实现这一点。这将为数据库生成另一个
SELECT
查询。现在我想改进我的数据库查询,只选择带有
轮胎的
汽车
,而不选择
窗口
车门
。(这在MySQL中是可能的)。让它们加载的选项是毫无疑问的,因为我并不总是想加载
tires
关系(我的对象上有一些大的关系)

我尝试了以下方法:

@Query("SELECT c FROM Car c JOIN FETCH c.tires WHERE c.id = :id")
Car findOneWithTiresLoaded(@Param("id") Long id);
这确实提供了正确的数据。但是在分析了从存储库返回的对象之后,我注意到所有的关系都被加载了。因此,这不仅返回具有
轮胎
关系的
汽车
,还返回
车门
车窗
。 下面给出了相同的输出(加载了monetomy关系)

不想要的东西。我希望它只输出
Car
对象,而不输出它的所有惰性关系

互联网上的人们还建议调用
Car.getTires().size()
。这也将产生另一个
SELECT
查询

是否有任何方法仅在装载轮胎的情况下选择
汽车
?没有
fetch=FetchType.LAZY
Hibernate.initialize()
size()
方法?为什么不能只连接一个表?此外,我不使用XML进行任何配置


谢谢

我总是建议使用实体图来实现这一点。我将给出一个使用Spring数据的示例。此外,无论发生什么情况,都应该始终使用延迟加载,所有其他关系都可以使用特定的图连接

通过这种方式,您可以非常具体地了解查询,只获取业务逻辑所需的数据。您甚至可以定义子图,以显示要从
轮胎
实体中选择的内容。这意味着您总是对所有
Tire
实体关系进行延迟抓取。默认情况下,您得到的只是轮胎(根据请求),而没有其他关系。如果您还需要
轮胎
中的任何其他内容,那么您唯一要做的就是在那里定义另一组图定义,并从您的存储库中引用它们,在那里您可以将查询作为子图进行

@Entity
@Table(name = "car")
@NamedEntityGraph(name = Car.TIRES_GRAPH, attributeNodes = @NamedAttributeNode("tires"))
public class Car {

  public static final String TIRES_GRAPH = "Car.tires";

  @OneToMany(mappedBy = "car", fetch = FetchType.LAZY}
  private Set<Tire> tires = new HashSet<>();

}
@实体
@表(name=“car”)
@NamedEntityGraph(name=Car.TIRES\u图,attributeNodes=@NamedAttributeNode(“轮胎”))
公车{
公共静态最终字符串轮胎\u GRAPH=“Car.TIRES”;
@OneToMany(mappedBy=“car”,fetch=FetchType.LAZY}
私有集轮胎=新HashSet();
}
对于您的存储库,您可以有一个方法

@Query("SELECT c FROM Car c")
@EntityGraph(Car.TIRES_GRAPH)
Set<Car> findAllWithTires();
@Query(“从c车中选择c”)
@实体图(汽车轮胎图)
设置FindAllWithTyres();
即使您没有使用Spring数据,方法也是一样的,您可以很容易地找到这方面的好例子

编辑

另一个已测试的工作示例。只需确保您的字段名与Spring数据的域匹配即可解析它们

public interface CarRepository extends JpaRepository<Car, Long> {

  @EntityGraph(attributePaths = { "tires" })
  Set<Car> findAllWithTiresByCarId(Long id)
}
public interface CarRepository扩展了JpaRepository{
@EntityGraph(AttributePath={“轮胎”})
设置findAllWithTiresByCarId(长id)
}

对于文档

我是否需要使用
@表
注释对实体进行注释?因为我们现在不这样做,如果不需要,那么不编写它会更干净。或者它与对象图一起使用?我现在就尝试一下。如果这对我们有效,我会将您的答案标记为解决方案。感谢您的回复!否
@表
可以省略,我只是用它来定义实体。我刚刚发现类
Car
扩展了类
Vehicle
。因此它使用
@heritation
注释。(也是
@DiscriminatorValue('Car')注释)。当我尝试在
Car`实体中使用命名图时,引导时会出现错误。
无法在此ManagedType[com.example.domain.Vehicle]上找到具有给定名称[Tires]的属性
。但是我在
Car
存储库中查询
Car
表。这是什么原因造成的?好的,扩展没有问题。我在注释中对
Tires
使用了大写T。因此它找不到该表的变量,因为它的统计值是较低的T。无论如何,我现在在启动时出错,说它无法自动连接我的repository作为
Car
对象没有findOneWithTires属性。
org.springframework.data.mapping.PropertyReferenceException:找不到Car类型的findOneWithTires属性!
我认为这个异常是因为Spring数据试图用它来构建一个查询方法。方法的名称必须不同,或者应该不同d将被注释为
@Query
,以将其排除在外。你能试着处理一下吗。
public interface CarRepository extends JpaRepository<Car, Long> {

  @EntityGraph(attributePaths = { "tires" })
  Set<Car> findAllWithTiresByCarId(Long id)
}