Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

195
Vistas
Hibernate genera una combinación cruzada en lugar de una combinación externa izquierda para una relación opcional

Modelé una relación de base de datos en JPA así:

 @Entity @Data @NoArgsConstructor(access = AccessLevel.PROTECTED) // Required for JPA @AllArgsConstructor public class OuterEntity { @Id private String outerId; @JoinColumn(nullable = true) @ManyToOne(fetch = FetchType.LAZY, optional = true) private InnerEntity inner; }
 @Entity @Data @NoArgsConstructor(access = AccessLevel.PROTECTED) // Required for JPA @AllArgsConstructor public class InnerEntity { @Id private String innerId; @Column private boolean deleted; }

Y persistió algunos datos de prueba de la siguiente manera:

 InnerEntity inner = new InnerEntity("inner", false); OuterEntity outerWithInner = new OuterEntity("outerWithInner", inner); OuterEntity outerWithoutInner = new OuterEntity("outerWithoutInner", null); em.persist(inner); em.persist(outerWithInner); em.persist(outerWithoutInner);

Realizar una consulta muy simple utilizando la API de criterios con éxito da dos resultados:

 CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<OuterEntity> query = cb.createQuery(OuterEntity.class); query.from(OuterEntity.class); System.out.println(em.createQuery(query).getResultList()); // [OuterEntity(outerId=outerWithInner, inner=InnerEntity(innerId=inner, deleted=false)), OuterEntity(outerId=outerWithoutInner, inner=null)]

Pero una vez que agrego un filtro en la entidad inner como esta:

 CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<OuterEntity> query = cb.createQuery(OuterEntity.class); Root<OuterEntity> root = query.from(OuterEntity.class); query.where(cb.or(cb.isNull(root.get("inner")), cb.isFalse(root.get("inner").get("deleted"))));

hibernate genera una cross join con where outerentit0_.innerId_innerId=innerentit1_.innerId en el SQL resultante para acceder a la tabla de la entidad inner :

 select outerentit0_.outerId as outerid1_1_, outerentit0_.inner_innerId as inner_in2_1_ from OuterEntity outerentit0_ cross join InnerEntity innerentit1_ where outerentit0_.inner_innerId=innerentit1_.innerId and ( outerentit0_.inner_innerId is null or innerentit1_.deleted=0 )

Esto ahora solo devuelve una entidad:

 [OuterEntity(outerId=outerWithInner, inner=InnerEntity(innerId=inner, deleted=false))]

Que no es el resultado que deseo. Esperaba que todavía hubiera dos resultados.

Creo que esto sucede porque el filtro outerentit0_.inner_innerId=innerentit1_.innerId elimina la entidad externa que tiene null para su inner , porque usar = en nulos siempre se evalúa como falso. Tenga en cuenta que configuré explícitamente la columna de combinación en anulable y la relación de muchos a uno en opcional en las anotaciones JPA, por lo que esperaba que JPA manejara correctamente el caso nulo.

Si agrego root.join("inner", JoinType.LEFT); o root.join("inner", JoinType.LEFT); , obtengo el resultado correcto porque la consulta generada usa una left outer join :

 select outerentit0_.outerId as outerid1_1_, outerentit0_.inner_innerId as inner_in2_1_ from OuterEntity outerentit0_ left outer join InnerEntity innerentit1_ on outerentit0_.inner_innerId=innerentit1_.innerId where outerentit0_.inner_innerId is null or innerentit1_.deleted=0
 CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<OuterEntity> query = cb.createQuery(OuterEntity.class); Root<OuterEntity> root = query.from(OuterEntity.class); root.join("inner", JoinType.LEFT); cb.isFalse(root.get("inner").get("deleted")))); List<OuterEntity> result = em.createQuery(query).getResultList(); System.out.println(result); // [OuterEntity(outerId=outerWithInner, inner=InnerEntity(innerId=inner, deleted=false)), OuterEntity(outerId=outerWithoutInner, inner=null)]

Supongo que es una solución viable, pero estoy muy confundido por qué hibernate emitiría una cross join , lo cual es sutilmente incorrecto para mis expectativas, el peor tipo de error. ¿Qué hice para causar este comportamiento? ¿Estoy obligado a realizar siempre explícitamente una combinación externa izquierda cuando se trata de relaciones anulables como esta?

over 4 years ago · Santiago Trujillo
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda