Hibernate为可选的组合键联接生成额外的查询

时间:2019-07-16 23:37:14

标签: java spring hibernate spring-data-jpa

我正在使用带有Hibernate Core 5.3.9.Final的Spring Boot 2.1.4(以及Lombok,您可能会知道)。我有一个包含两个不同连接的实体:一个在单个字段上,另一个在多个字段上。我正在使用NamedEntityGraph来防止Hibernate N + 1问题,但是尽管我竭尽全力,Hibernate仍会为第二个联接生成额外的查询。

这是我的父实体:

    @Entity
    @Table(name = "PROC_STAT", uniqueConstraints = {@UniqueConstraint(columnNames = {"PROC_ID",
            "STAT_CDE", "STAT_VAR_VAL_TXT","PRVD_KEY_VAL_TXT","DTSET_NME"})})
    @IdClass(ProcessStatId.class)
    @Getter
    @Setter
    @AllArgsConstructor
    @NoArgsConstructor
    @NamedEntityGraph(name = "proc-stat",
        attributeNodes = {
            @NamedAttributeNode(value = "statType", subgraph = "stat-category"),
            @NamedAttributeNode(value = "statVariantValue")
        },
        subgraphs = {
            @NamedSubgraph(name = "stat-category",
                attributeNodes = {
                    @NamedAttributeNode(value = "statCategory")})
    })
    public class ProcessStat extends StatsBookkeeping {
        @Id
        @Column(name = "PROC_ID")
        private Long processId;

        @Id
        @Column(name = "STAT_CDE")
        private Integer statId;

        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "STAT_CDE", nullable = false, updatable = false, insertable = false)
        @Fetch(FetchMode.JOIN)
        private StatType statType;

        @Id
        @Column(name = "STAT_VAR_VAL_TXT")
        private String statVariant;

        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumns({@JoinColumn(name = "STAT_CDE", updatable = false, insertable = false),
                      @JoinColumn(name = "STAT_VAR_VAL_TXT", updatable = false, insertable = false)})
        @NotFound(action = NotFoundAction.IGNORE)
        private StatVariantValue statVariantValue;

        @Id
        @Column(name = "PRVD_KEY_VAL_TXT")
        private String providerKey;

        @Id
        @Column(name = "DTSET_NME")
        private String datasetName;

        @Column(name = "PROC_STAT_CNT")
        private Integer count;
    }

这是有问题的子实体:

    @Entity
    @Table(name = "STAT_VAR_VAL", uniqueConstraints = {@UniqueConstraint(columnNames = {"STAT_CDE","STAT_VAR_VAL_TXT"})})
    @IdClass(StatVariantValueId.class)
    @Getter
    @Setter
    @AllArgsConstructor
    @NoArgsConstructor
    public class StatVariantValue extends StatsBookkeeping {
        @Id
        @Column(name = "STAT_CDE")
        private Integer statId;

        @Id
        @Column(name = "STAT_VAR_VAL_TXT")
        private String statVariant;

        @Column(name = "STAT_VAR_VAL_DSC")
        private String statVariantDescription;

        @Column(name = "SEQ_NUM")
        private Integer sequenceNumber;
    }

(我已经省略了两个实体的ID类;它们仅通过@NoArgsConstructor和@EqualsAndHashCode使用它们各自的@Id字段进行定义。StatsBookkeeping类也已被省略,仅包含审核字段)

如前所述,在“ STAT_CDE”字段上使用“ StatType”进行联接就可以了。使用EntityGraph时没有多余的查询。但是与“ StatVariantValue”的联接总是会产生额外的查询。

这里最棘手的部分是,此联接位于多个字段上,是可选的,并且可能不会填充父记录(STAT_VAR_VAL_TXT)中的字段之一;但是,在Oracle数据库中,它将表示为空白值(“”)。由于STAT_VAR_VAL表中永远不会有一个带有空白STAT_VAR_VAL_TXT值的记录,因此这些记录将永远不会联接(完全可以)。

我正在尝试通过PROC_ID字段查询父表以返回许多行。我尝试使用自动生成的JPA存储库方法(findByProcessId)以及使用@Query w / JPQL(尽管不是本地的)指定我自己的方法-结果相同。

Hibernate始终会生成一个完美的查询,该查询实际上返回我需要的所有内容(其他字段已从粘贴中删除):

    Hibernate: 
    [...]
    statvarian1_.crat_tsp as crat_tsp3_9_1_, 
    statvarian1_.crat_user_id as crat_user_id4_9_1_, 
    statvarian1_.upd_tsp as upd_tsp5_9_1_, 
    statvarian1_.upd_user_id as upd_user_id6_9_1_, 
    statvarian1_.seq_num as seq_num7_9_1_, 
    statvarian1_.stat_var_val_dsc as stat_var_val_dsc8_9_1_, 
    [...]
    from stats.proc_stat processsta0_ 
    left outer join stats.stat_var_val statvarian1_ on processsta0_.stat_cde=statvarian1_.stat_cde and processsta0_.stat_var_val_txt=statvarian1_.stat_var_val_txt 
    left outer join stats.stat_typ stattype2_ on processsta0_.stat_cde=stattype2_.stat_cde 
    left outer join stats.stat_cat statcatego3_ on stattype2_.stat_cat_cde=statcatego3_.stat_cat_cde 
    where processsta0_.proc_id=?

但是随后变得嘈杂并产生很多这样的声音:

    Hibernate: select statvarian0_.stat_cde as stat_cde1_9_0_,
    statvarian0_.stat_var_val_txt as stat_var_val_txt2_9_0_, 
    statvarian0_.crat_tsp as crat_tsp3_9_0_, 
    statvarian0_.crat_user_id as crat_user_id4_9_0_, 
    statvarian0_.upd_tsp as upd_tsp5_9_0_, 
    statvarian0_.upd_user_id as upd_user_id6_9_0_, 
    statvarian0_.seq_num as seq_num7_9_0_, 
    statvarian0_.stat_var_val_dsc as stat_var_val_dsc8_9_0_ 
    from stats.stat_var_val statvarian0_ 
    where statvarian0_.stat_cde=? and statvarian0_.stat_var_val_txt=?

我是Java的新手,我在这里尝试了所有我能想到的事情:将ID类移入然后移出其父实体(然后将StatVariantValueId作为SubGraph包括在内)-考虑到ID类可能导致了额外的查询,但这没有任何改变。我也尝试添加:

@Where(clause = "STAT_VAR_VAL_TXT <> ' '")

添加到ProcessStat中的@ManyToOne联接,但这也不会改变查询。

我很困惑。关于如何防止多余查询的任何想法?

0 个答案:

没有答案