Crinteria Únete marca E inserta de OR

Nov 06 2020

Mi problema es que cuando configuro cb. O lo hace cb y .. cb es CriteriaBuilder Mi clase es:

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;

Mi código de criterios es:

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")))
);

El código genera:

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
)

¿Cómo puedo resolverlo? ¿Por qué mi código genera una declaración AND?

Respuestas

1 AntonGalishnikov Nov 06 2020 at 09:18

Probablemente se deba a que tiene un solo argumento en su llamada o método

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

debiera ser

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

Lamento decirte que con criterios de API hoy no es posible, te contaré diferentes opciones:

1.- Utilizar consulta nativa

El uso de consultas nativas no es un problema ya que está utilizando cláusulas compatibles con todos los administradores de bases de datos

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();

En tu entidad

@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.- Use dos raíces, en caso de que la combinación interna le sirva en lugar de la combinación izquierda agregando las condiciones de la cláusula on en el 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)))
);

Como te estaba diciendo, aunque no se recomienda usar consultas nativas porque rompe con la filosofía JPA, en este caso como no usas las cláusulas de ninguna DB no debería ser un problema, yo sería la opción que haría escoger.