Фильтр по частичным первичным ключам для запроса Jpa

#java #spring #hibernate #jpa #spring-data-jpa

Вопрос:

У меня есть класс объектов Employee (таблица 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);
} 
 

Это каждый раз, когда мне нужно запрашивать. Так плохо для производительности.

Есть ли лучший подход, основанный на производительности?

Ответ №1:

Есть еще один интересный способ добиться этого с помощью спецификаций Spring Data Jpa.
Начните с расширения вашего репозитория JpaSpecificationExecutor<Employee> такими :

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

Это даст вам сотрудников с именем «Боб» с местоположением «Нигде» и сотрудников с именем «Жюль» с местоположением «Здесь», создав следующий одиночный запрос :
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() {}
}
 

ОБРАТИТЕ insertable updatable внимание на флаги и, чтобы предотвратить повторное сопоставление столбцов. Затем внедрите его в своего сотрудника

 @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. Спасибо @Lookslikeitsnot … звучит неплохо … позвольте мне попробовать встроенную опцию

2. работает нормально … необходимо также добавить AttributeOverrides baeldung.com/jpa-embedded-embeddable для моего случая

Ответ №2:

Существуют различные подходы, которые вы можете рассмотреть:

1. Создайте 2 составных первичных ключа Создайте составной первичный ключ с именем и местоположением. Еще один со всеми атрибутами (имя, местоположение и статус).

2. Напишите пользовательский запрос соответствующего поставщика базы данных, который вы используете (MySQL, PostgreSQL и т. Д.). Используйте @Query аннотацию чуть выше метода, который вы вызываете в классе репозитория.

Пример:

 @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

Комментарии:

1. Спасибо, Пранай, 2-й подход, которого я могу достичь, также используя jpa, но это неэффективно для списка {name, location} . Не могли бы вы, пожалуйста, объяснить первый подход (два составных первичных ключа)? особенно аннотации

2. Вы можете перейти по этой ссылке: baeldung.com/jpa-composite-primary-keys

3. должны ли у нас быть два встроенных идентификатора (один содержит все ключи, другой только с двумя ключами)?

4. JPA можно использовать для эффективного сопоставления составных ключей и запроса их с помощью производных запросов. Встраиваемая аннотация может использоваться для представления составного первичного ключа, а аннотация EmbeddedId — для вставки составного ключа в объект.

5. Спасибо, Пранай, позвольте мне попробовать и обновить здесь