【问题标题】:JPA Criteria: Left outer joins instead of inner joins when doing projection from entity hierarchyJPA 标准:从实体层次结构进行投影时,左外连接而不是内连接
【发布时间】:2021-02-15 04:49:01
【问题描述】:

假设我有 1 个父实体和 2 个子实体:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Notification {

    protected Long id;
    protected Long code;

    protected Notification() {
    }
}

@Entity
@PrimaryKeyJoinColumn(name = "NOTIFICATION_ID")
public class Sms extends Notification {

    private String phoneNumber;
    private String smsText;

    public Sms() {
    }
}

@Entity
@PrimaryKeyJoinColumn(name = "NOTIFICATION_ID")
public class Push extends Notification {

    private String application;
    private String pushText;
    
    public Push() {
    }
}

我想使用 JPA 标准 API 进行投影,如下所示:

CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<NotificationSummary> query = builder.createQuery(NotificationSummary.class);
Root<Notification> root = query.from(Notification.class);

query.select(builder.construct(NotificationSummary.class,
        root.get("code"),
        builder.treat(root, Sms.class).get("smsText"),
        builder.treat(root, Push.class).get("pushText"),
        builder.treat(root, Sms.class).get("phoneNumber"),
        builder.treat(root, Push.class).get("application")
));

class NotificationSummary {

    private final Long code;
    private final String smsText;
    private final String pushText;
    private final String phoneNumber;
    private final String application;

    public NotificationSummary(Long code, String smsText, String pushText, String phoneNumber, String application) {
        this.code = code;
        this.smsText = smsText;
        this.pushText = pushText;
        this.phoneNumber = phoneNumber;
        this.application = application;     
    }
}   

当我执行它时,它会生成 SQL 查询:

select
    notificati0_.code as col_1_0_,
    notificati0_3_.sms_text as col_5_0_,
    notificati0_2_.push_text as col_6_0_,
    notificati0_3_.phone_number as col_7_0_,
    notificati0_2_.application as col_8_0_ 
from
    notification notificati0_ 
inner join
    push notificati0_2_ 
        on notificati0_.id=notificati0_2_.notification_id 
inner join
    sms notificati0_3_ 
        on notificati0_.id=notificati0_3_.notification_id

我希望它是左外连接。

我可以以某种方式将其更改为左外连接而不是内连接吗?

【问题讨论】:

    标签: hibernate jpa spring-data-jpa jpa-2.0 criteria-api


    【解决方案1】:

    不幸的是,这是不可能的,因为 HQL 中的 TREAT 当前会导致内部连接,因为 JPA 规范在语义上不是很清楚。 IMO 可以有不同的解释,但现在就是这样。

    在 HQL 中,您实际上可以省略 TREAT 来隐式访问子类型属性,这会导致左连接,就像您期望的那样。如果您不想在 HQL 中重写您的查询,您可以使用一个不同的 JPA Criteria 实现,该实现基本上呈现为 JPQL/HQL。 Blaze-Persistence 是一个在 JPA/Hibernate 之上工作的库,它支持许多高级 SQL 功能,同时保持在 JPA 模型的范围内。以下是使用 JPA Criteria 实现的快速入门链接:https://github.com/Blazebit/blaze-persistence#jpa-criteria-api-quick-start

    您还需要一些dependencies,但由于您想创建投影,您可能还喜欢Blaze-Persistence Entity-Views,它允许您以更具声明性的方式创建投影。使用实体视图时,您的用例可能如下所示:

    @EntityView(Notification.class)
    public interface NotificationSummary {
        Long getCode();
        @Mapping("TREAT(this AS Sms).smsText")
        String getSmsText();
        @Mapping("TREAT(this AS Push).pushText")
        String getPushText();
        @Mapping("TREAT(this AS Sms).phoneNumber")
        String getPhoneNumber();
        @Mapping("TREAT(this AS Push).application")
        String getApplication();
    }  
    

    【讨论】:

    • 非常感谢,吸取了关于 HQL 无需治疗即可工作的教训。 HQL 查询现在对我有用。
    • 我稍后会尝试 blaze-persistence 项目。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-29
    • 2011-08-19
    • 2018-11-13
    • 2011-10-31
    • 1970-01-01
    • 2017-03-25
    相关资源
    最近更新 更多