Spring Data JPA Интерфейс и проекция на основе классов не работают для поля DISTINCT встроенного ключа - PullRequest
0 голосов
/ 09 мая 2019

Ниже приведена структура моей сущности. EmployeeKey - это EmbeddedId (составной ключ) внутри Employee.

Это собственный запрос, который я хочу реализовать с помощью Spring-Data.

select DISTINCT(ID), NAME, DEPARTMENT from EMPLOYEE;

Я пытаюсь использовать Проекции JPA для данных на основе интерфейса и на основе классов , но ни один из подходов не работает. Проекции на основе интерфейса дают список прокси, которые я не могу отменить прокси. Использование DISTINCT в конструкторе проекций на основе классов невозможно, AFAIK.

@Entity
@Table(name = "EMPLOYEE")
public class Employee {
   @EmbeddedId
   private EmployeeKey key;

   @Column(name = "NAME")
   private String name;

   @Column(name = "DEPARTMENT")
   private String department;

   @Coulmn(name = "AGE")
   private Integer age;

   //Getter/Setters/Constructors
}

@Embeddable
public class EmployeeKey {
   @Column(name = "ID")
   String id;

   @Column(name = "REGNO")
   String regNo;

   //Getter/Setters/Constructors
}

@Repository
public interface EmployeRepository extends JpaRepository<Employee, EmployeeKey>{
   @Query(value = "select distinct(emp.key.id), emp.name, emp.department from Employee emp")
   List<EmployeeInterfaceProjection> findUsingInterfaceProjection();

   @Query(value = "select distinct(emp.key.id) as empId, emp.name as empName, emp.department as empDepartment from Employee emp")
   List<EmployeeClassProjection1> findUsingClassProjection1();

   @Query(value = "select new com.path.to.EmployeeClassProjection2(emp.key, emp.name, emp.department) from Employee emp")
   List<EmployeeClassProjection2> findUsingClassProjection2();

   @Query(value = "select distinct(emp.key.id) as empId, emp.name as empName, emp.department as empDepartment from Employee emp")
   List<Object[]> findUsingObjectProjection();
}

public interface EmployeeInterfaceProjection{
   EmployeeKeyInterfaceProjection getKey();
   String getName();
   String getDepartment();

   interface EmployeeKeyInterfaceProjection{
      String getId();
   }
}

public class EmployeeClassProjection1{
   private String empId;
   private String empName;
   private String empDepartment;

   //Getters/Setters, Constructors, Hashcode, Equals
}

public class EmployeeClassProjection2{
   private EmployeeKey key;
   private String name;
   private String department;

   //Getters/Setters, Constructors, Hashcode, Equals
}

ПРОБЛЕМЫ, КАСАЮЩИЕСЯ В КАЖДОМ ПОДХОДЕ

findUsingInterfaceProjection ()

Это дает список , который я не могу отменить, чтобы получить значение . Hibernate.unproxy / initialize тоже не помогает. При потоковой передаче по прокси и вызову getter: getKey (), getName (), getDepartment () выдает null для каждого.

findUsingClassProjection1 ()

Это дает "Не найден конвертер, способный конвертировать из типа [org.springframework.data.jpa.repository.query. AbstractJpaQuery $ TupleConverter $ TupleBackedMap ] в тип [com.path.to.EmployeeClassProjection1] " ошибка.

Я знаю, что для решения этой проблемы мне нужно использовать параметризованный конструктор непосредственно в запросе. Но это запрещает мне использовать DISTINCT в конструкторе.

findUsingClassProjection2 ()

Этот подход на самом деле получает данные, а не прокси, но мне нужна отдельная фильтрация. Не удается использовать DISTINCT внутри конструктора в запросе.

findUsingObjectProjection ()

Это отлично работает, но выглядит как очень грубый подход. Я надеялся использовать проекции JPA.

Ответы [ 2 ]

1 голос
/ 09 мая 2019

Это то, что, наконец, сработало для меня.Я использовал псевдонимы столбцов в запросе , соответствующие именам методов получения в проекции (псевдоним = name , если проекция имеет getName () ). нет необходимости использовать внутренний интерфейс EmployeeKeyInterfaceProjection .

public interface EmployeeInterfaceProjection{
   String getId();
   String getName();
   String getDepartment();
}


@Query(value = "select distinct(emp.key.id) as id, emp.name as name, emp.department as department from Employee emp")
List<EmployeeInterfaceProjection> findUsingInterfaceProjection();

Внутренние проекции на основе интерфейса работают на AbstractJpaQuery $ TupleConverter $ TupleBackedMap .Более простой, без использования псевдонимов столбцов, я получил null для каждого из getId (), getName (), getDepartment ().Использование псевдонимов решает эту проблему.

Спасибо @Ho Wai Chan за указание мне в правильном направлении.

1 голос
/ 09 мая 2019
@Repository
public interface EmployeRepository extends JpaRepository<Employee, EmployeeKey>{
   @Query(value = "select distinct(emp.key.id) as id, emp.name, emp.department from Employee emp")
   List<EmployeeInterfaceProjection> findUsingInterfaceProjection();

   @Query(value = "select distinct(emp.key.id) as empId, emp.name as empName, emp.department as empDepartment from Employee emp")
   List<EmployeeClassProjection1> findUsingClassProjection1();

   @Query(value = "select new com.path.to.EmployeeClassProjection2(emp.key, emp.name, emp.department) from Employee emp")
   List<EmployeeClassProjection2> findUsingClassProjection2();

   @Query(value = "select distinct(emp.key.id) as empId, emp.name as empName, emp.department as empDepartment from Employee emp")
   List<Object[]> findUsingObjectProjection();
}

public interface EmployeeInterfaceProjection{
   String getId();
   String getName();
   String getDepartment();

}

public interface EmployeeClassProjection1{
   String getEmpId;
   String getEmpName;
   String getEmpDepartment;
}

, если вы хотите получить внутренний объект:

public interface dto {
    @Value("#{target.Object.Id}")
    Integer getId();
  }
...