JPA标准:将根向下转换为多个实体子类,并按相同的链接实体自然ID进行过滤

时间:2019-03-04 21:22:43

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

鉴于我有4张桌子:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class SourceEvent {
    @Id
    private Long id;
}

@Entity
@PrimaryKeyJoinColumn(name = "SOURCE_EVENT_ID")
public class PrimaryEvent extends SourceEvent {
    @ManyToOne
    private Account account;
}

@Entity
@PrimaryKeyJoinColumn(name = "SOURCE_EVENT_ID")
public class SecondaryEvent extends SourceEvent {
    @ManyToOne
    private Account account;
}

@Entity
public class Account {
    @Id
    private Long id;

    @NaturalId
    @Column(name = "ACCOUNT_NUMBER", unique = true, nullable = false)
    private long accountNumber;
}

如您所见,SourceEventPrimaryEventSecondaryEvent的父级,两个事件都指向Account

我想使用JPA Criteria API通过SourceEvent的帐号过滤Account

我把这个放在一起:

final class Filter {

    private Long accountNumber;

    Specification<SourceEvent> toSpecification() {
        return (Root<SourceEvent> root, CriteriaQuery query, CriteriaBuilder cb) -> {
            return cb.or(
                    cb.equal(cb.treat(root, PrimaryEvent.class).get("account").get("accountNumber"), accountNumber),
                    cb.equal(cb.treat(root, SecondaryEvent.class).get("account").get("accountNumber"), accountNumber)
            );
        };
    }       
}       

在这里我使用treat运算符从SourceEvent下放到PrimaryEventSecondaryEvent,然后将两个相等的Predicate放到cb.or(...) < / p>

当我致电sourceEventRepository.findAll(filter.toSpecification())时, 而sourceEventRepository是:

public interface SourceEventRepository extends JpaSpecificationExecutor<SourceEvent>, JpaRepository<SourceEvent, Long> {}

然后我得到了Hibernate生成的SQL:

...
from
    source_event sourceeven0_ 
inner join
    primary_event sourceeven0_1_ 
        on sourceeven0_.id=sourceeven0_1_.source_event_id 
inner join
    secondary_event sourceeven0_2_ 
        on sourceeven0_.id=sourceeven0_2_.source_event_id 
cross join
    account account1_ 
where
    sourceeven0_1_.account_id=account1_.id 
    and (
        account1_.account_number=123 
        or account1_.account_number=123
    )

什么对我不起作用,因为它不包括具有给定帐号的SecondaryEvent

如何使其工作?

1 个答案:

答案 0 :(得分:4)

我们最近遇到了这个问题,并通过更改实体之一的变量名称找到了解决方法。

然后我们像强制降级那样强制左外连接。

根据您的用例:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class SourceEvent {
    @Id
    private Long id;
}

@Entity
@PrimaryKeyJoinColumn(name = "SOURCE_EVENT_ID")
public class PrimaryEvent extends SourceEvent {
    @ManyToOne
    private Account account;
}

@Entity
@PrimaryKeyJoinColumn(name = "SOURCE_EVENT_ID")
public class SecondaryEvent extends SourceEvent {
    @ManyToOne
    private Account account1;
}

@Entity
public class Account {
    @Id
    private Long id;

    @NaturalId
    @Column(name = "ACCOUNT_NUMBER", unique = true, nullable = false)
    private long accountNumber;

查询:

final class Filter {

    private Long accountNumber;

    Specification<SourceEvent> toSpecification() {
        return (Root<SourceEvent> root, CriteriaQuery query, CriteriaBuilder cb) -> {
            Root<PrimaryEvent> primaryEventRoot = cb.treat(root, PrimaryEvent.class);
            Join<PrimaryEvent, Account> primaryEvent = primaryEventRoot.join(PrimaryEvent_.account, JoinType.LEFT);

            Root<SecondaryEvent> secondaryEventRoot = cb.treat(root, SecondaryEvent.class);
            Join<SecondaryEvent, Account> secondaryEvent = secondaryEventRoot.join(SecondaryEvent_.account1, , JoinType.LEFT);
            return cb.or(
                    cb.equal(primaryEvent.get("accountNumber"), accountNumber),
                    cb.equal(secondaryEvent.get("accountNumber"), accountNumber)
            );
        };
    }       
}