提问者:小点点

按 JPA 查询的部分主键筛选


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

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

这是我每次需要查询的时候。对性能非常不利。

有什么更好的基于性能的方法吗?


共2个答案

匿名用户

还有另一种有趣的方法可以通过Spring Data Jpa规范来实现这一点。
首先使用 JpaSpecificationExecutor 扩展您的存储库

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);

这将为您提供名为“Bob”且位置为“Nowhere”的员工和名为“Jules”且位置“Here”的员工,方法是生成以下单个查询:选择employee0_.location作为location1_0_,employee 0_.name作为name2_0_,employee0_.desc为EMPLOYEE EMPLOYEE_0_的desc4_0_,其中EMPLOYEE.0_.name=“Bob”和EMPLOYEE-0_.location=“Nowhere”或employey0_.name=“Jules”和employee0.location=“Here”

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

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

注意可插入更新标志,以防止重复的列映射。然后将其嵌入您的员工

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

  @EmbeddedId
  EmployeeId id;

  @Embedded
  PartialEmployeeId partialId;

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

,使用来自Spring data 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查询。

匿名用户

您可以考虑不同的方法:

1.制作两个复合主键用名称和位置制作一个复合主键。另一个包含所有属性(名称、位置和状态)。

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

示例:

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

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

更新

有关该主题的更多报道,请参阅链接。JPA Spring JPA @Embedded@EmbeddedId中的复合主键