在一个表上Hibernate两个ManyToOne关系,第一个获取Eager,第二个LAZY加载

Gui*_*mer 9 java postgresql hibernate eager many-to-one

我有以下实体,item最多可以有两个类别,主要和次要.两个类别都使用a 映射ManyToOnecategoryJoinColumnsOrFormulas.第一个EAGER按预期获取,但第二个不会出现在SQL语句中并且会延迟加载.这种延迟加载导致经典的n + 1问题.

这是我的项目实体,应该加入两个类别实体:

@Entity
@Table(name = "item", schema = "public", catalog = "stackoverflow_question")
@DynamicUpdate
public class Item extends StackOverflowQuestionEntity {

    @Id
    @Column(name = "id")
    protected Long id;

    @Column(name = "site")
    private String site;

    @ManyToOne
    @JoinColumnsOrFormulas({
            @JoinColumnOrFormula(formula = @JoinFormula(value = "site", referencedColumnName = "site")),
            @JoinColumnOrFormula(formula = @JoinFormula(value = "primary_category_id", referencedColumnName = "category_id"))
    })
    private Category primaryCategory;

    @Column(name = "primary_category_id")
    private Long primaryCategoryId;

    @ManyToOne
    @JoinColumnsOrFormulas({
            @JoinColumnOrFormula(formula = @JoinFormula(value = "site", referencedColumnName = "site")),
            @JoinColumnOrFormula(formula = @JoinFormula(value = "secondary_category_id", referencedColumnName = "category_id"))
    })
    private Category secondaryCategory;

    @Column(name = "secondary_category_id")
    private Long secondaryCategoryId;
}
Run Code Online (Sandbox Code Playgroud)

这是类别实体:

@Entity
@Table(name = "category", schema = "public", catalog = "stackoverflow_question")
public class Category extends StackOverflowQuestionEntity {

    @Column(name = "category_id")
    private Long categoryId;

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

    @Column(name = "site")
    private String site;
}
Run Code Online (Sandbox Code Playgroud)

生成的查询仅包含主要类别:

SELECT this_.id AS id1_9_9_,
       this_.inserted AS inserted2_9_9_,
       this_.updated AS updated3_9_9_,
       this_.primary_category_id AS formula174_9_,
       this_.secondary_category_id AS formula176_9_,
       category2_.id AS id1_0_0_,
       category2_.inserted AS inserted2_0_0_,
       category2_.updated AS updated3_0_0_,
       category2_.name AS name7_0_0_
FROM public.item this_
LEFT OUTER JOIN public.category category2_ ON this_.site=category2_.site
AND this_.primary_category_id=category2_.category_id
WHERE True;
Run Code Online (Sandbox Code Playgroud)

因此,次要类别加入懒惰:

SELECT category0_.id AS id1_0_0_,
       category0_.inserted AS inserted2_0_0_,
       category0_.updated AS updated3_0_0_,
       category0_.name AS name4_0_0_,
       category0_.site AS site5_0_0_
FROM public.category category0_
WHERE category0_.site=?
  AND category0_.category_id=?;
Run Code Online (Sandbox Code Playgroud)

为什么Hibernate加入了次要类别lazy,注释似乎是相同的.

我使用的hibernate版本是5.0.10.Final.

这是基本实体的样子:

@MappedSuperclass
abstract public class StackOverflowQuestionEntity implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", unique = true, insertable = true, updatable = false, nullable = false)
    protected Long id;

    @Type(type="LocalDateTime")
    @Column(name = "created", nullable = false, insertable = true, updatable = false)
    protected LocalDateTime created;

    @Type(type="LocalDateTime")
    @Column(name = "refreshed", nullable = false, insertable = true, updatable = true)
    protected LocalDateTime refreshed;

    @PreUpdate
    protected void onUpdate() {
        refreshed = now();
    }

    @PrePersist
    protected void onCreate() { 
        created = refreshed = now();
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个示例"查询",正如我所说的使用hibernate标准以及HQL,两种方法都会出现问题.

session
    .createCriteria(Item.class)
    .add(eq("id", id))
    .uniqueResult();
Run Code Online (Sandbox Code Playgroud)

Kir*_*nov 4

使用标准 JPA 注释,它看起来像这样(已更新):

@ManyToOne
@JoinColumns({
    @JoinColumn(name="site", referencedColumnName="site", insertable = false, updatable = false),
    @JoinColumn(name="primary_category_id", referencedColumnName="category_id", insertable = false, updatable = false)
})
private Category primaryCategory;

@ManyToOne
@JoinColumns({
    @JoinColumn(name="site", referencedColumnName="site", insertable = false, updatable = false),
    @JoinColumn(name="secondary_category_id", referencedColumnName="category_id", insertable = false, updatable = false)
})
private Category secondaryCategory;
Run Code Online (Sandbox Code Playgroud)

更新:我发现仅当您使用组合键select时才会生成第二条语句:Hibernate 尝试解析使用的关联。但如果你写join{site=site, id=null}TwoPhaseLoad

@ManyToOne
@JoinColumn(name="secondary_category_id")
private Category secondaryCategory;
Run Code Online (Sandbox Code Playgroud)

然后将生成唯一一条语句secondary_category_id,其值为。也许它会以某种方式帮助你。例如,您可以在构建标准时添加对字段的约束:nullselectsecondaryCategorynullsite

Category c = (Category) session.createCriteria(Category.class)
    .add(Restrictions.eq("id", 1L)) // for example
    // here you define additional restriction on site field
    .createAlias("secondaryCategory", "sc", JoinType.LEFT_OUTER_JOIN, Restrictions.sqlRestriction("this_.site = {alias}.site"))
    .uniqueResult();
Run Code Online (Sandbox Code Playgroud)