Given I have 4 tables:
@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;
}
As you can see, SourceEvent is a parent of PrimaryEvent and SecondaryEvent and both events point at Account.
I'd like to filter SourceEvents by Account's account number using JPA Criteria API.
I put together this:
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)
            );
        };
    }       
}       
where I use treat operator to downcast from SourceEvent to PrimaryEvent and SecondaryEvent and I put both equals Predicates to cb.or(...)
When I call sourceEventRepository.findAll(filter.toSpecification()), 
whereas sourceEventRepository is:
public interface SourceEventRepository extends JpaSpecificationExecutor<SourceEvent>, JpaRepository<SourceEvent, Long> {}
then I am getting following SQL generated by Hibernate:
...
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
    )
what doesn't work for me, because it doesn't include SecondaryEvents with given account number.
How to make it work?
We hit this issue recently and found a workaround by changing the variable name of the one of the entities.
We so then forced left outer joins as on the downcasting.
As per your use case:
@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;
Query:
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)
            );
        };
    }       
}    
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With