#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. Спасибо, Пранай, позвольте мне попробовать и обновить здесь