【问题标题】:Filter by partials primary keys for Jpa Query按 Jpa Query 的部分主键过滤
【发布时间】:2021-10-27 07:14:27
【问题描述】:

我有一个包含复合主键 EmployeeId (name,location,status) 的 Employee(Employee 表)实体类。我只需要按选定的主键(名称、位置)进行过滤。

@Entity
@Table(name = "EMPLOYEE")
class Employee {

 @EmbeddedId
 EmployeeId id;
 
 @Column(name = "DESC")
 String desc;
}

public class EmployeeId implements Serializable {
  @Column(name = "name")
  private String name;

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

  @Column(name = "status")
  private String status;
}

但我不能使用以下,因为我没有状态值:

interface EmployeeJpaRepository  extends JpaRepository<Employee, EmployeeId > {
  List<Employee> findAllByIdIn(Set<EmployeeId > employeeId);
} 

我可以用这个:

interface EmployeeJpaRepository  extends JpaRepository<Employee, EmployeeId > {
  List<Employee> findAllByIdNameAndIdPlanLocation(String name, String location);
} 

这是每次我需要查询的时候。性能太差了。

有没有更好的基于性能的方法?

【问题讨论】:

    标签: java spring hibernate jpa spring-data-jpa


    【解决方案1】:

    Spring Data Jpa Specifications 还有另一种有趣的实现方式。
    首先使用JpaSpecificationExecutor&lt;Employee&gt; 扩展您的存储库,如下所示:

    public interface EmployeeRepository
        extends JpaRepository<Employee, EmployeeId>, JpaSpecificationExecutor<Employee> {}
    

    然后,假设您在地图中有员工姓名和位置(任何可迭代都可以):

    Map<String, String> nameLocations = Map.of("Bob", "Nowhere", "Jules", "Here");
    
    Specification<Employee> employeeSpec = Specification.where((root, query,
        builder) -> builder.or(nameLocations.entrySet().stream()
            .map(nameLocation -> builder.and(
                builder.equal(root.get("id").get("name"), nameLocation.getKey()),
                builder.equal(root.get("id").get("location"), nameLocation.getValue())))
            .toArray(Predicate[]::new)));
    
    List<Employees> employeesByNameAndLocation = employeeRepository.findAll(employeeSpec);
    

    这将通过生成以下单个查询为您提供位置为“Nowhere”的名为“Bob”的员工和位置为“Here”的名为“Jules”的员工: select employee0_.location as location1_0_, employee0_.name as name2_0_, employee0_.status as status3_0_, employee0_.desc as desc4_0_ from EMPLOYEE employee0_ where employee0_.name='Bob' and employee0_.location='Nowhere' or employee0_.name='Jules' and employee0_.location='Here'

    如果您不喜欢规范,您也可以使用像 @Pranay Srivastava 建议的嵌入式对象:

    @Embeddable
    @Immutable
    public class PartialEmployeeId implements Serializable {
      @Column(name = "name", insertable = false, updatable = false)
      private String name;
    
      @Column(name = "location", insertable = false, updatable = false)
      private String location;
    
      
    
      public PartialEmployeeId(String name, String location) {
        this.name = name;
        this.location = location;
      }
    
      public PartialEmployeeId() {}
    }
    

    注意insertableupdatable 标志以防止重复的列映射。然后将其嵌入到您的员工中

    @Entity
    @Table(name = "EMPLOYEE")
    public class Employee {
    
      @EmbeddedId
      EmployeeId id;
    
      @Embedded
      PartialEmployeeId partialId;
    
      @Column(name = "DESC")
      String desc;
    }
    

    ,使用来自 Spring 数据 JPA 的派生查询更新您的存储库:

    public interface EmployeeRepository
        extends JpaRepository<Employee, EmployeeId>{
      List<Employee> findByPartialIdIn(List<PartialEmployeeId> partialEmployeeIds);
    }
    

    并像这样使用它:

    PartialEmployeeId nameAndLocation1 = new PartialEmployeeId("Bob", "Nowhere");
    PartialEmployeeId nameAndLocation2 = new PartialEmployeeId("Jules", "Here");
    List<Employees> employeesByNameAndLocation = employeeRepository.findByPartialIdIn(List.of(nameAndLocation1, nameAndLocation2));
    

    这会生成与规范相同的单个 SQL 查询。

    【讨论】:

    【解决方案2】:

    您可以考虑多种不同的方法:

    1. 制作 2 个复合主键 使用名称和位置创建复合主键。 另一个具有所有属性(名称、位置和状态)。

    2. 编写您正在使用的各自数据库供应商(MySQL、PostgreSQL 等)的自定义查询。在 Repository 类中调用的方法上方使用 @Query 注解。

    例子:

    @Query("Select e from EmployeeId where e.name like %?1% and e.location like %?1%")
        List<Employee> findAllByIdNameAndIdPlanLocation(String name, String location);
    

    我不确定他们是否会按照您的要求提高性能。不过你也可以考虑一下。

    更新

    有关该主题的更多报道,请参阅链接。 Composite Primary Keys in JPA Spring JPA @Embedded and @EmbeddedId

    【讨论】:

    • 感谢 Pranay,第二种方法我也可以使用 jpa 实现,但这对于 {name,location} 列表无效。您能否解释一下第一种方法(两个复合主键)?特别是注释
    • 可以参考这个链接:baeldung.com/jpa-composite-primary-keys
    • 我们应该有两个 EmbeddedId(一个包含所有键,另一个只有两个键)?
    • JPA 可用于有效地映射复合键并通过派生查询对其进行查询。 Embeddable 注解可用于表示复合主键,EmbeddedId 注解可用于在实体中插入复合键。
    • 谢谢 Pranay 让我在这里尝试更新
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-12
    • 2021-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多