简体   繁体   中英

Collecting all child entities from multiple different entities

I have 3 entities A, B and C.

  • A contains a one-to-one linked B entity
  • A contains a one-to-many linked List of C entities
  • B contains a one-to-many linked List of C entities

Database-wise there are 5 tables. The 3 tables for the respective entities and 2 separate linking tables, one containing the links between A and C (A_to_C), and another one containing the links between B and C (B_to_C). In my repository I'm trying to retrieve all C entities from a specific A record, meaning the C entities from A itself and the C entities which are linked through B.

In traditional SQL this could be done using something like:

select C.*
from A
left join A_to_C on A_to_C.A_ID = A.ID
left join B_to_C on B_to_C.B_ID = A.B_ID
inner join C on C.ID = A_to_C.C_ID OR C.ID = B_to_C.C_ID
where A.ID = '1';

or (starting from C)

select C.*
from C
left join A_to_C on A_to_C.C_ID = C.ID
left join B_to_C on B_to_C.C_ID = C.ID
inner join A on A.B_ID = B_to_C.B_ID OR
                A.ID = A_to_C.A_ID
where A.ID = '1';

There's no link to the table for B in these SQL examples because A contains the ID for B and it's also the one used in B_to_C so I don't really need it. I also know these aren't strictly the same but they produce the same result when I'm only interested in C.

I'm really struggling with how to do this in either CriteriaBuilder (preferably) or JPQL though. I'm still relatively new to jpa so I'm hoping someone here could help me with this.

I believe that, to even have a chance of succeeding with vanilla JPQL , you would need to convert the association to a bidirectional one (or a unidirectional many-to-one) by adding the @ManyToOne side. You could then start the query from C . The @OneToMany side, should you want to retain it, becomes the inverse side of the association in such a scenario, though:

@Entity
public class C {

    @ManyToOne
    @JoinTable
    private B b;

    @ManyToOne
    @JoinTable
    private A a;
}

@Entity
public class B {

    @ManyToOne
    @JoinTable
    private A a;

    @OneToMany(mappedBy = "b")
    private List<C> cs;
}

@Entity
public class A {

    @OneToMany(mappedBy = "a")
    private List<B> bs;


    @OneToMany(mappedBy = "a")
    private List<C> cs;
}

Once you do that, the JPQL query becomes sth like:

SELECT c FROM C c
LEFT JOIN c.a a
LEFT JOIN c.b b
LEFT JOIN b.a a2
WHERE a.id = :id OR a2.id = :id

If you're not OK with making the 'one' side of the association the inverse side, then you're out of luck. The easiest solution is to use a native query.

Eclipselink JPQL Extensions include an ON clause, so perhaps you could combine ON with MEMBER OF :

SELECT c FROM C c
LEFT JOIN A a ON c MEMBER OF a.cs
LEFT JOIN B b ON c MEMBER OF b.cs
LEFT JOIN A a2 ON b MEMBER OF a2.cs
WHERE a.id = :id OR a2.id = :id

I highly doubt it will work, though, and even if it does, I'd be wary of the generated SQL as it might be suboptimal.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM