Crinteria Join marque ET incrustation de OU

Nov 06 2020

Mon problème est que lorsque je mets cb.or it do cb et .. cb est CriteriaBuilder Ma classe est:

public class User extends BaseEntity<User> {
public static final String CHILDREN_FIELD = "children";
public static final String PARENT_FIELD = "parent";

//Id column based in BaseEntity

OneToOne(fetch = LAZY)
JoinColumn(name = "parent_partner_id")
User parent;

OneToMany(mappedBy = "parent", fetch = LAZY,cascade = {})
private List<User> children;

Mon code de critères est:

Root<User> root = query.from(User.class);
Join<User, User> userJoin = root.join(CHILDREN_FIELD, JoinType.LEFT);
userJoin.on(
cb.or(
cb.equal(userJoin.get(ID_FIELD), root.get("id")))
);

Le code génère:

select
user0_.id as col_0_0_
from
user user0_
left outer join
user children2_
on user0_.id=children2_.parent_id
and (  <- i write OR in cb!! why it generates AND ?  
children2_.id=user0_.id
)

Comment puis-je le résoudre? Pourquoi mon code génère une instruction AND?

Réponses

1 AntonGalishnikov Nov 06 2020 at 09:18

C'est probablement parce que vous avez un seul argument dans votre appel de méthode ou

cb.or(cb.equal(userJoin.get(ID_FIELD), root.get("id"))));

devrait être

cb.or(arg1, arg2); // arg1 OR arg2
1 JLazar0 Nov 06 2020 at 09:48

Je suis désolé de vous dire qu'avec les critères API ce n'est pas possible aujourd'hui, je vais vous parler des différentes options:

1.- Utiliser la requête native

L'utilisation de la requête native n'est pas un problème car vous utilisez des clauses prises en charge par tous les gestionnaires de bases de données

List<User> result =  em.createNativeQuery("select * from user p "+
                        "left outer join user p1 on p.id = p1.parent_id or p.id = p1.id", 
                        "User.nativeConstructorMapping").getResultList();

Dans votre entité

@SqlResultSetMappings({
    @SqlResultSetMapping(name="User.nativeConstructorMapping",
    classes = {
    @ConstructorResult(
        targetClass = Users.class,
        columns = {
            @ColumnResult(name = "id", type = Long.class),
            @ColumnResult(name = "name", type = String.class)
            [...]
        })
    })
})

....

public User(Long id, String name, [...]) {
    super();
    this.id= id;
    this.name = name
    [...]
}

2.- Utilisez deux racines, au cas où la jointure interne vous servirait au lieu de la jointure gauche en ajoutant les conditions de la clause on dans le where

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root1 = query.from(User.class);
Root<User> root2 = query.from(User.class);

query.where(cb.or(
    cb.equal(root1.get(User_.id),root2.get(User_.parent).get(User_.id)),
    cb.equal(root1.get(User_.id),root2.get(User_.id)))
);

Comme je vous le disais, bien qu'il ne soit pas recommandé d'utiliser des requêtes natives car cela rompt avec la philosophie JPA, dans ce cas, comme vous n'utilisez les clauses d'aucune base de données, cela ne devrait pas poser de problème, je serais l'option que je voudrais choisir.