bestsource

스프링 데이터에서 JPA 엔티티를 아름답게 업데이트하는 방법은 무엇입니까?

bestsource 2023. 8. 2. 09:17
반응형

스프링 데이터에서 JPA 엔티티를 아름답게 업데이트하는 방법은 무엇입니까?

그래서 저는 Spring Data와 함께 JPA에 대한 다양한 튜토리얼을 살펴보았는데, 이 튜토리얼은 여러 번 다르게 수행되어 왔으며 올바른 접근 방식이 무엇인지 잘 모르겠습니다.

다음 엔티티가 있다고 가정합니다.

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

이제 고객이 웹 UI에서 자신의 이름을 변경하려고 한다고 가정합니다. 그러면 컨트롤러 작업이 수행됩니다. 여기에는 이전 ID와 새 이름을 가진 업데이트된 DTO가 포함됩니다.

이제 업데이트된 DTO를 데이터베이스에 저장해야 합니다.

유감스럽게도 현재 기존 고객을 업데이트할 방법이 없습니다(DB에서 항목을 삭제하고 새 자동 생성 ID로 새 고객을 만드는 것 외에는).

하지만 이것이 실현 가능하지 않기 때문에 (특히 그러한 실체가 잠재적으로 수백 개의 관계를 가질 수 있다는 것을 고려하면) - 그래서 제 마음에 두 가지 간단한 해결책이 있습니다.

  1. Customer 클래스의 id에 대한 설정자를 만듭니다. 따라서 id 설정을 허용한 다음 해당 리포지토리를 통해 Customer 개체를 저장합니다.

또는

  1. 생성자에 id 필드를 추가하고 고객을 업데이트할 때마다 항상 이전 id를 사용하여 새 개체를 생성하지만 다른 필드의 새 값(이 경우에는 이름만)

그래서 제 질문은 이것을 어떻게 하는지에 대한 일반적인 규칙이 있는지에 대한 것입니다.혹시 제가 설명한 두 가지 방법의 단점은 무엇인가요?

T getById(ID id)2.7 이후에는 더 이상 사용되지 않습니다.대신 사용하십시오.아이디어는 동일합니다. 참조만 반환합니다.그러나 "getById"가 명확하지 않을 수 있고 "getReferenceById"가 덜 모호할 수 있기 때문에 더 자명합니다.

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

@Tanjim Rahman 답변보다 훨씬 더 좋습니다. 스프링 데이터 JPA를 사용할 수 있습니다. 방법을 사용하십시오.

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

더 나은 이유는getOne(ID id) 참조(프록시) 개체만 가져오고 DB에서 가져오지 않습니다.이 참조에서 원하는 항목을 설정할 수 있습니다.save()예상대로 SQL UPDATE 문만 사용할 수 있습니다.당신이 전화할 때 비교해서요.find()@Tanjim Rahmans answer spring data와 같이 JPA는 SQL SELECT를 수행하여 방금 업데이트할 때 필요하지 않은 엔티티를 DB에서 물리적으로 가져옵니다.

Spring Data에서는 ID가 있는 경우 업데이트 쿼리를 정의합니다.

  @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, @Param("name") String name);

  }

일부 솔루션은 Spring 데이터를 사용하고 대신 JPA oldchool(업데이트가 손실된 방식으로도)을 수행한다고 주장합니다.

간단한 JPA 업데이트..

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

JpaRepository에 메서드가 있습니다.

getOne

그것은 현재로서는 을 위해 권장되지 않습니다.

getById

따라서 올바른 접근법은

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

이것은 jpa 질문이라기보다는 객체 초기화 질문에 가깝습니다. 두 가지 방법이 모두 작동하며 두 가지 방법을 동시에 사용할 수 있습니다. 일반적으로 인스턴스화 전에 데이터 멤버 값이 준비되면 생성자 매개 변수를 사용합니다. 인스턴스화 후에 이 값을 업데이트할 수 있다면 설정자가 있어야 합니다.

엔티티가 아닌 DTO와 직접 작업해야 하는 경우 기존 고객 인스턴스를 검색하고 DTO에서 업데이트된 필드를 해당 인스턴스에 매핑해야 합니다.

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

이제 고객이 웹 UI에서 자신의 이름을 변경하려고 한다고 가정합니다. 그러면 컨트롤러 작업이 수행됩니다. 여기에는 이전 ID와 새 이름을 가진 업데이트된 DTO가 포함됩니다.

일반적으로 워크플로는 다음과 같습니다.

  1. 사용자가 서버에서 자신의 데이터를 요청하여 UI에서 가져옵니다.
  2. 사용자가 자신의 데이터를 수정하여 이미 존재하는 ID를 가진 서버로 다시 보냅니다.
  3. 서버에서 사용자별로 업데이트된 데이터로 DTO를 획득하고, DB에서 ID로 검색하여(그렇지 않으면 예외 던지기) DTO를 변환합니다. -> 모든 주어진 데이터, 외부 키 등을 가진 엔티티...
  4. 그런 다음 그냥 병합하거나 Spring Data를 사용하여 save()를 호출하면 병합됩니다(이 스레드 참조).

추신: 이 작업을 수행하면 필연적으로 선택 및 업데이트라는 두 가지 쿼리가 발생합니다.다시 한 번, 단일 필드를 업데이트하려는 경우에도 2개의 쿼리가 있습니다.그러나 엔티티 클래스 위에 Hibernate의 고유한 @DynamicUpdate 주석을 사용하는 경우 업데이트 문에 모든 필드를 포함하지 않고 실제로 변경된 필드만 포함하는 데 도움이 됩니다.

추신: 첫 번째 select 문에 대한 비용을 지불하지 않고 Spring Data의 @Modifying 쿼리를 사용하는 것을 선호한다면 수정 가능한 엔티티와 관련된 L2C 캐시 영역이 손실될 것에 대비하십시오. 네이티브 업데이트 쿼리의 상황은 더욱 심각합니다(이 스레드 참조). 물론 이러한 쿼리를 수동으로 작성하고 테스트하여 향후 지원할 준비가 되어 있습니다.

이 문제가 발생했습니다!
운 좋게도, 나는 가지 방법을 결정하고 몇 가지는 이해하지만 나머지는 명확하지 않습니다.
만약 당신이 알고 있다면 누군가가 토론하거나 지지하기를 바랍니다.

  1. 리포지토리 사용확장 JPA.저장(엔티티)
    예:
    List<Person> person = this.PersonRepository.findById(0) person.setName("Neo"); This.PersonReository.save(person);
    이 블록 코드는 ID가 = 0인 레코드의 새 이름을 업데이트했습니다.
  2. javax 또는 spring 프레임워크에서 @Transactional을 사용합니다.
    클래스 또는 지정된 함수에 @Transactional을 붙입니다. 둘 다 괜찮습니다.
    저는 어디선가 이 주석이 함수 흐름 끝에 "커밋" 동작을 수행한다는 것을 읽었습니다.따라서 엔티티에서 수정한 모든 항목이 데이터베이스로 업데이트됩니다.

언급URL : https://stackoverflow.com/questions/39741102/how-to-beautifully-update-a-jpa-entity-in-spring-data

반응형