JPA Criteria:将根向下转换为多个实体子类并按相同的链接实体自然 id 进行过滤
JPA Criteria: downcast root to multiple entity subclasses and filter by the same linked entity natural id
假设我有 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;
}
如您所见,SourceEvent
是 PrimaryEvent
和 SecondaryEvent
的父级,并且两个事件都指向 Account
。
我想使用 JPA 条件 API.
按 Account
的帐号过滤 SourceEvent
s
我整理了这个:
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
向下转换为 PrimaryEvent
和 SecondaryEvent
并且我把两个等于 Predicate
s 到 cb.or(...)
当我打电话给 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
s。
如何让它发挥作用?
我们最近遇到了这个问题,并通过更改其中一个实体的变量名称找到了解决方法。
然后我们像向下转换一样强制使用左外连接。
根据您的用例:
@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)
);
};
}
}
假设我有 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;
}
如您所见,SourceEvent
是 PrimaryEvent
和 SecondaryEvent
的父级,并且两个事件都指向 Account
。
我想使用 JPA 条件 API.
按Account
的帐号过滤 SourceEvent
s
我整理了这个:
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
向下转换为 PrimaryEvent
和 SecondaryEvent
并且我把两个等于 Predicate
s 到 cb.or(...)
当我打电话给 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
s。
如何让它发挥作用?
我们最近遇到了这个问题,并通过更改其中一个实体的变量名称找到了解决方法。
然后我们像向下转换一样强制使用左外连接。
根据您的用例:
@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)
);
};
}
}