【问题标题】:Spring Data JPA Interface and class based projection not working for DISTINCT field of Embedded keySpring Data JPA 接口和基于类的投影不适用于嵌入式键的 DISTINCT 字段
【发布时间】:2019-09-26 23:37:27
【问题描述】:

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

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

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

我正在尝试使用 基于接口和基于类的 Spring 数据 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 预测。

【问题讨论】:

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


    【解决方案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();
      }
    

    【讨论】:

      【解决方案2】:

      这最终对我有用。我在查询中使用了 列别名,与投影中的 getter 方法名称相匹配(别名 = 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 起作用。更早,在不使用列别名的情况下,getId()、getName()、getDepartment() 中的每一个都为 null。使用别名可以解决这个问题。

      感谢@Ho Wai Chan 为我指明了正确的方向。

      【讨论】:

        猜你喜欢
        • 2021-06-19
        • 1970-01-01
        • 2019-02-26
        • 2020-02-13
        • 2018-12-16
        • 2020-01-10
        • 1970-01-01
        • 2015-05-01
        • 1970-01-01
        相关资源
        最近更新 更多