java Jpa @CollectionTable查询非常慢

6bc51xsx  于 5个月前  发布在  Java
关注(0)|答案(1)|浏览(48)

我有一个实体

@Entity
@Table(name = "GEO_MUNICIPALITY", indexes = {
        @Index(name = "idx_municipalityentity", columnList = "PROVINCE_CODE")
})
...
public class MunicipalityEntity {
    @Id
    @Column(name = "MUNICIPALITY_CODE", nullable = false)
    private String code;

    @Column(nullable = false)
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "PROVINCE_CODE")
    private ProvinceEntity province;

    @ElementCollection
    @CollectionTable(name = "GEO_MUNICIPALITY_POSTAL_CODE",
            joinColumns = @JoinColumn(name = "MUNICIPALITY_CODE"),
    indexes = {
            @Index(name = "idx_municipalityentity_postal_code", columnList = "MUNICIPALITY_CODE")
    })
    @Column(name = "POSTAL_CODE")
    private List<String> postalCodeList;
}

个字符
现在,我的API需要显示具有给定省份代码的所有城市(以及所有城市的邮政编码),因此我有一个像这样的存储库类:

@Repository
public interface MunicipalityJpaRepository extends JpaRepository<MunicipalityEntity, String> {
    List<MunicipalityEntity> findAllByProvinceCode(String province);

    @Query("select m from MunicipalityEntity m left join m.postalCodeList postalCodeList where m.province.code = ?1")
    List<MunicipalityEntity> findByCode(String code);
}


这两个方法做同样的事情,我尝试了两种方式(结果是相同的),因为当我调用getPostalCodeList()时,查询非常慢,因为它对市政当局的每个结果进行选择(检索邮政编码)。
我怎样才能提高性能?另外,因为我总是通过代码搜索市政当局,是一个好主意,使索引像在代码中?谢谢!
我尝试了使用jpa仓库和自定义查询,我期待一个快速的查询执行时间,但执行非常慢,因为在连接查询后,应用程序对每个结果城市进行查询,以获取邮政编码。

2fjabf4q

2fjabf4q1#

在JPQL中,left join不是left join fetch。如果我运行您的示例并尝试打印结果,我会得到可怕的LazyInitializationException。您说过您会得到一个单独的PostalCodes查询,但这是因为您仍在Session中访问邮政编码。这是您应该在应用程序中更好地设置的东西。我不再使用我的简单版本当我试图访问邮政编码时,我得到了LazyInitializationException
与您的原始查询:

@Query("from MunicipalityEntity m left join m.postalCodeList postalCodeList where m.province.code = ?1")

字符串
我得到这个SQL

Hibernate: select m1_0.municipality_code,m1_0.name,m1_0.province_code from geo_municipality m1_0 left join geo_municipality_postal_code p1_0 on m1_0.municipality_code=p1_0.municipality_code where m1_0.province_code=?


如果我在H2-CONSOLE中运行该SQL,
x1c 0d1x的数据
这是混乱的,因为sql的结果是CARTESIAN PRODUCT,但它不包括POSTAL_CODE属性。

@Query("from MunicipalityEntity m left join fetch m.postalCodeList postalCodeList where m.province.code = ?1")


我得到正确的结果

Hibernate: select m1_0.municipality_code,m1_0.name,p1_0.municipality_code,p1_0.postal_code,m1_0.province_code from geo_municipality m1_0 left join geo_municipality_postal_code p1_0 on m1_0.municipality_code=p1_0.municipality_code where m1_0.province_code=?
[MunicipalityEntity(code=MC1, name=MN1, postalCodeList=[PC1, PC2, PC3]), MunicipalityEntity(code=MC2, name=MN2, postalCodeList=[PC4, PC5, PC6]), MunicipalityEntity(code=MC3, name=MN3, postalCodeList=[PC7, PC8, PC9])]


如实际SQL中所示



请注意,似乎需要@Query来解决这个问题。在JPA中,您可以/应该使用ApertyGraphs来告诉JPA获取您想要的属性,这些属性通常不会被获取,例如postalCodeList。
所以你可以用一个JPA EntityGraph来修复你的常规SpringDataJpa方法。

Hibernate: select m1_0.municipality_code,m1_0.name,m1_0.province_code from geo_municipality m1_0 where m1_0.province_code=?
...
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.jpaplay.MunicipalityEntity.postalCodeList: could not initialize proxy - no Session
at org.hibernate.collection.spi.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:631) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at org.hibernate.collection.spi.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at org.hibernate.collection.spi.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:611) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at org.hibernate.collection.spi.AbstractPersistentCollection.read(AbstractPersistentCollection.java:136) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at org.hibernate.collection.spi.PersistentBag.toString(PersistentBag.java:584) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]


如果我把它改成

@EntityGraph(value = "MunicipalityEntity.postalCodeList",attributePaths = {"postalCodeList"}, type = EntityGraph.EntityGraphType.FETCH)
List<MunicipalityEntity> findAllByProvinceCode(String province);


然后我在SQL中得到你想要的

Hibernate: select m1_0.municipality_code,m1_0.name,p2_0.municipality_code,p2_0.postal_code,m1_0.province_code from geo_municipality m1_0 left join geo_municipality_postal_code p2_0 on m1_0.municipality_code=p2_0.municipality_code where m1_0.province_code=?
[MunicipalityEntity(code=MC1, name=MN1, postalCodeList=[PC1, PC2, PC3]), MunicipalityEntity(code=MC2, name=MN2, postalCodeList=[PC4, PC5, PC6]), MunicipalityEntity(code=MC3, name=MN3, postalCodeList=[PC7, PC8, PC9])]


另外请注意,我没有特别打印出PROVINCE_CODE。如果我这样做,也会导致LazyInitializationException

List<MunicipalityEntity> r = playService.query2();
    System.out.println(r);
    System.out.println(r.get(0).getProvince());


给我

Hibernate: select m1_0.municipality_code,m1_0.name,p2_0.municipality_code,p2_0.postal_code,m1_0.province_code from geo_municipality m1_0 left join geo_municipality_postal_code p2_0 on m1_0.municipality_code=p2_0.municipality_code where m1_0.province_code=?
[MunicipalityEntity(code=MC1, name=MN1, postalCodeList=[PC1, PC2, PC3]), MunicipalityEntity(code=MC2, name=MN2, postalCodeList=[PC4, PC5, PC6]), MunicipalityEntity(code=MC3, name=MN3, postalCodeList=[PC7, PC8, PC9])]
....
Caused by: org.hibernate.LazyInitializationException: could not initialize proxy [com.example.jpaplay.ProvinceEntity#PC1] - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:164) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:309) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:44) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]
at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95) ~[hibernate-core-6.1.5.Final.jar:6.1.5.Final]


我说你应该在你的应用程序中更好地处理你的Session,因为如果你在初始查询之后访问了PROVINCE_CODE,它也会导致另一个查询。如果你确保你的会话只在你想要的JPA访问期间活动,那么你会得到一个LazyInitializationException,而不是糟糕的性能。希望这能有所帮助。总是检查JPA生成的SQL查询,以确保你知道发生了什么。
PS>你可能会问,当查询返回的是PROVINCE_CODE时,为什么要用LazyInitializationException来代替ProvinceEntity呢?

System.out.println(r.get(0).getProvince().getCode());


希望这对你有帮助。

相关问题