Pregunta Conversión de SQL Left Outer Join Query a JPA Criteria


Tengo la siguiente consulta SQL nativa que intento convertir a los criterios de JPA:

select et.* from t_empl_tx et, t_dept d 
where et.assigned_dept = d.dept (+)
  and et.employee_id = :employee_id
  and (et.start_date >= d.dept_est_date and
       et.start_date <= d.dept_close_date or
       et.start_date is null or
       d.dept is null)

(Tenga en cuenta que (+) es más o menos equivalente a una combinación externa izquierda en este caso. Sí, sé que denota la tabla OPCIONAL, etc., etc.).

Aquí está mi intento por el código:

EntityManager entityManager = getEntityManager();
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<EmployeeTransaction> criteriaQuery = criteriaBuilder.createQuery(EmployeeTransaction.class);
Root<EmployeeTransaction> root = criteriaQuery.from(EmployeeTransaction.class);

    // this line bombs!
Join<EmployeeTransaction, Department> join = 
    root.join(EmployeeTransaction_.assignedDepartment).join(Department_.id).join(DepartmentCompositeId_.department, JoinType.LEFT);

List<Predicate> predicates = new ArrayList<>();

predicates.add(criteriaBuilder.equal(root.get(EmployeeTransaction_.id).get(EmployeeTransactionCompositeId_.employeeId), employeeId));
predicates.add(criteriaBuilder.or(
    criteriaBuilder.and(
    criteriaBuilder.greaterThanOrEqualTo(root.<Date>get(EmployeeTransaction_.requestedStartDate), join.get(Department_.id).<Date>get(DepartmentCompositeId_.departmentCreationDate)),
    criteriaBuilder.lessThanOrEqualTo(root.<Date>get(EmployeeTransaction_.requestedStartDate), join.<Date>get(Department_.departmentCloseDate))
    ),
    criteriaBuilder.isNull(root.get(EmployeeTransaction_.requestedStartDate)),
    criteriaBuilder.isNull(join.get(Department_.id).get(DepartmentCompositeId_.departmentCreationDate))
));

criteriaQuery.select(root).where(predicates.toArray(new Predicate[]{}));

TypedQuery<EmployeeTransaction> query = entityManager.createQuery(criteriaQuery);
List<EmployeeTransaction> result = query.getResultList();

Este problema parece ser que estoy intentando unirme a una columna de cadena, assigedDepartment, a un solo campo de una ID compuesta. Esto es perfectamente legal en SQL, pero no tan fácil en el código.

Una opción es convertir a una serie de subconsultas, lo que parece matar por completo el punto de la combinación externa izquierda.

¿Alguien puede señalar lo que estoy haciendo mal?

Jason


10
2018-02-14 12:35


origen


Respuestas:


Debe publicar sus entidades para que las respuestas puedan ser más específicas.

Sin embargo, voy a intentarlo.

Si estoy en lo cierto, puede reescribir la consulta:

select et.* 
from t_empl_tx et
    left join t_dept d on et.assigned_dept = d.dept
where 
    et.employee_id = :employee_id
    and (
        et.start_date >= d.dept_est_date 
        and et.start_date <= d.dept_close_date
        or et.start_date is null 
        or d.dept is null)

Entonces, en breve, debes mover el JoinType.LEFT a assignedDepartment unirse:

EntityManager entityManager = getEntityManager();
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<EmployeeTransaction> criteriaQuery = criteriaBuilder.createQuery(EmployeeTransaction.class);

Root<EmployeeTransaction> root = criteriaQuery.from(EmployeeTransaction.class);
Join<EmployeeTransaction, Department> department =  root.join(EmployeeTransaction_.assignedDepartment, JoinType.LEFT);
Path<Date> employeeTransactionRequestedStartDate = root.get(EmployeeTransaction_.requestedStartDate);
Path<DepartmentCompositeId> departmentId = department.get(Department_.id);
Path<Date> departmentCreationDate = departmentId.get(DepartmentCompositeId_.departmentCreationDate)
Path<Date> departmentCloseDate = departmentId.get(DepartmentCompositeId_.departmentCloseDate)

criteriaQuery.select(root).where(
    criteriaBuilder.equal(root.get(EmployeeTransaction_.id).get(EmployeeTransactionCompositeId_.employeeId), employeeId),
    criteriaBuilder.or(
        criteriaBuilder.and(
            criteriaBuilder.greaterThanOrEqualTo(employeeTransactionRequestedStartDate, departmentCreationDate)),
            criteriaBuilder.lessThanOrEqualTo(employeeTransactionRequestedStartDate, departmentCloseDate)
        ),
        criteriaBuilder.isNull(employeeTransactionRequestedStartDate),
        criteriaBuilder.isNull(departmentCreationDate)
    )
);

TypedQuery<EmployeeTransaction> query = entityManager.createQuery(criteriaQuery);
List<EmployeeTransaction> result = query.getResultList();

2
2018-02-21 12:06