Spring Data JPA接口和基于类的投影不适用于嵌入式密钥的DISTINCT字段

时间:2019-05-09 07:27:38

标签: jpa spring-data-jpa spring-data

以下是我的实体结构。 EmployeeKey是Employee中的EmbeddedId(复合键)。

这是我想使用Spring-Data实现的本机查询。

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

我正在尝试使用基于接口和基于类的Spring数据JPA投影,但是这些方法似乎都不起作用。基于接口的预测给出了我无法取消代理的代理列表。在基于类的投影的构造函数中不能使用 DISTINCT

@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()时,每个值都为空。

findUsingClassProjection1()

这给出了“找不到能够从类型[org.springframework.data.jpa.repository.query。 AbstractJpaQuery $ TupleConverter $ TupleBackedMap ]转换为[com.path.to.EmployeeClassProjection1]的转换器。 ]”错误。

我知道为了解决这个问题,我需要直接在查询中使用参数化的构造函数。但是这样做禁止我在构造函数中使用 DISTINCT

findUsingClassProjection2()

这种方法实际上是获取数据而不是代理,但我需要进行独特的过滤。无法在查询的构造函数内使用DISTINCT。

findUsingObjectProjection()

这很好,但是看起来很粗糙。我希望使用JPA预测。

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

这终于对我有用。我在查询中使用了列别名,以匹配投影中的getter方法名称(如果投影具有 getName(),则别名为 name )。 无需使用内部接口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 内部起作用。 Ealier,无需使用列别名,我对getId(),getName(),getDepartment()的每个属性都为null。使用别名可以解决此问题。

感谢@Ho Wai Chan指出正确的方向。