Как красиво обновить объект JPA в Spring Data?

Поэтому я просмотрел различные руководства по JPA с данными Spring, и это неоднократно было сделано во многих случаях, и я не совсем уверен, что такое правильный подход.

Предположим, что существует следующее:

package stackoverflowTest.dao;

import javax.persistence.*;

@Entity
@Table(name = "customers")
public class Customer {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private long id;

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

public Customer(String name) {
    this.name = name;
}

public Customer() {
}

public long getId() {
    return id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}
}

У нас также есть DTO, который извлекается на уровне сервиса, а затем передается на контроллер/клиентскую сторону.

package stackoverflowTest.dto;

public class CustomerDto {

private long id;
private String name;

public CustomerDto(long id, String name) {
    this.id = id;
    this.name = name;
}

public long getId() {
    return id;
}

public void setId(long id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}
}

Итак, теперь предположим, что Клиент хочет изменить свое имя в webui - тогда будет какое-то действие контроллера, где будет обновленный DTO со старым идентификатором и новым именем.

Теперь я должен сохранить этот обновленный DTO в базе данных.

К сожалению, в настоящее время нет способа обновить существующего клиента (кроме удаления записи в БД и создания нового Cusomter с новым автоматически сгенерированным идентификатором)

Однако, поскольку это не представляется возможным (особенно учитывая, что такая организация может иметь сотни отношений потенциально), - так что на мой взгляд идут два прямых решения:

  1. сделать установщик для идентификатора в классе Customer - и, таким образом, разрешить настройку идентификатора, а затем сохранить объект Customer через соответствующий репозиторий.

или же

  1. добавьте поле id в конструктор, и всякий раз, когда вы хотите обновить клиента, вы всегда создаете новый объект со старым id, но новые значения для других полей (в данном случае - только имя)

Итак, мой вопрос: существует ли общее правило, как это сделать? Возможно, какие-то недостатки двух методов, которые я объяснил?

Ответ 1

Еще лучше @Tanjim Rahman ответ вы можете использовать Spring Data JPA использовать метод T getOne(ID id)

Customer customerToUpdate = customerRepository.getOne(id);
customerToUpdate.setName(customerDto.getName);
customerRepository.save(customerToUpdate);

Лучше, потому что getOne(ID id) получает вам только ссылочный (прокси) объект и не извлекает его из БД. В этой ссылке вы можете указать, что хотите, и в save() он будет выполнять только инструкцию SQL UPDATE, как вы ожидаете. В сравнении, когда вы вызываете find() как в @Tanjim Rahmans, ответьте на весенние данные, JPA выполнит SQL SELECT, чтобы физически извлечь объект из БД, который вам не нужен, когда вы просто обновляете.

Ответ 2

Простое обновление JPA..

Customer customer = em.find(id, Customer.class); //Consider em as JPA EntityManager
customer.setName(customerDto.getName);
em.merge(customer);

Ответ 3

В Spring Data вы просто определяете запрос на обновление, если у вас есть идентификатор

  @Repository
  public interface CustomerRepository extends JpaRepository<Customer , Long> {

     @Query("update Customer c set c.name = :name WHERE c.id = :customerId")
     void setCustomerName(@Param("customerId") Long id);

  }

Некоторые решения утверждают, что используют данные Spring и вместо этого используют JPA oldschool (даже способом с потерянными обновлениями).

Ответ 4

Это вопрос об инициализации объекта больше, чем вопрос jpa, оба метода работают, и вы можете иметь оба из них одновременно, обычно, если значение элемента данных готово до создания экземпляра, то вы используете параметры конструктора, если это значение может быть обновленный после создания экземпляра, у вас должен быть сеттер.

Ответ 5

Если вам нужно работать с DTO, а не с объектами напрямую, вы должны получить существующий экземпляр клиента и сопоставить обновленные поля с DTO.

Customer entity = //load from DB
//map fields from DTO to entity

Ответ 6

Я везде вижу, что я не могу обновить одну и ту же запись с помощью jpa. он не поддерживается. я скажу вам одну вещь, что если вы просто возьмете скрытую переменную в html и привяжите ее к существующему полю id записи, а затем, когда вы разместите форму и получите запись по этому идентификатору, просто сохраните опубликованный объект записи и сохраните существующий запись.

поэтому ключ должен взять существующий идентификатор записи в скрытом поле

Ответ 7

  Итак, теперь предположим, что Клиент хочет изменить свое имя в Интернете - тогда будет какое-то действие контроллера, где будет обновлен DTO со старым идентификатором и новым именем.

Обычно у вас есть следующий рабочий процесс:

  1. Пользователь запрашивает свои данные с сервера и получает их в пользовательском интерфейсе;
  2. Пользователь исправляет свои данные и отправляет их обратно на сервер с уже имеющимся идентификатором;
  3. На сервере вы получаете DTO с обновленными данными пользователя, находите его в БД по идентификатору (в противном случае генерируете исключение) и преобразуете DTO → Entity со всеми данными, внешними ключами и т.д....
  4. Затем вы просто объединяете это или, если используете Spring Data, вызываете save(), что, в свою очередь, объединяет это (см. этот поток);

P.S. Эта операция неизбежно вызовет 2 запроса: выбрать и обновить. Опять же, 2 запроса, даже если вы хотите обновить одно поле. Однако если вы используете проприетарную @DynamicUpdate аннотацию Hibernate поверх класса сущности, это поможет вам не включать в оператор обновления все поля, а только те, которые действительно изменились.

P.S. Если вы не хотите платить за первый оператор select и предпочитаете использовать запрос Spring Data @Modifying, будьте готовы потерять область кэша L2C, связанную с изменяемой сущностью; еще хуже ситуация с собственными запросами на обновление (см. эту ветку), а также, конечно, будьте готовы написать эти запросы вручную, протестировать их и поддерживать в будущем.

Ответ 8

Я столкнулся с этой проблемой!
К счастью, я определяю 2 пути и понимаю некоторые вещи, но остальное не ясно.
Надеюсь, кто-нибудь обсудит или поддержит, если вы знаете.

  1. Используйте RepositoryExtendJPA.save(entity).
    Пример:
    List<Person> person = this.PersonRepository.findById(0) person.setName("Neo"); This.PersonReository.save(person);
    этот код блока обновил новое имя для записи с id = 0;
  2. Используйте @Transactional из Javax или Spring Framework.
    Пусть поместите @Transactional на ваш класс или указанную функцию, оба в порядке.
    Я где-то читал, что эта аннотация выполняет действие "commit" в конце вашего потока функций. Таким образом, все, что вы изменили в сущности, будет обновлено в базе данных.