it-source

충돌 업데이트 시 삽입 일부 필드를 사용하여 Postgres가 있는 Spring Data/Hibernate 저장 엔티티

criticalcode 2023. 7. 15. 10:15
반응형

충돌 업데이트 시 삽입 일부 필드를 사용하여 Postgres가 있는 Spring Data/Hibernate 저장 엔티티

JpaRepository.save 메서드를 사용하여 저장하고 Postgres의 Sequence generator를 사용하여 ID를 자동으로 생성하는 Spring 도메인 개체가 있습니다.

@SequenceGenerator(initialValue = 1, name = "device_metric_gen", sequenceName = "device_metric_seq")
public class DeviceMetric extends BaseTimeModel {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "device_metric_gen")
    @Column(nullable = false, updatable = false)
    private Long id;
///// extra fields

내 사용 사례는 다음 작업을 수행해야 합니다.upsert의 통이아 save작업(ID가 있는 경우 업데이트할 것으로 알고 있음).세 개의 열 조합(복합 고유)이 있거나 새 행을 작성하는 경우 기존 행을 업데이트하려고 합니다.이는 다음과 유사합니다.

INSERT INTO customers (name, email)
VALUES
   (
      'Microsoft',
      'hotline@microsoft.com'
   ) 
ON CONFLICT (name) 
DO
      UPDATE
     SET email = EXCLUDED.email || ';' || customers.email;

Spring-data에서 동일한 것을 달성하는 한 가지 방법은 다음과 같습니다.

  1. 다음과 같은 사용자 지정 저장 작업을 서비스 계층에 작성합니다.
  2. 행이 있는 경우 세 개의 열에 대한 a를 구합니까?
  3. 현재 개체에 동일한 ID를 설정하고 리포지토리를 수행합니다.저장
  4. 행이 없으면 일반 리포지토리를 수행합니다. 저장

위 접근법의 문제는 다음과 같습니다.insert 제는이 .select그리고 나서.save두의 데이터베이스 호출을 , 두 개 데 이 터 수 하 는 동 호 일 포 한 은 있 출 니 습 의 다 수 달 될 성 해 그 스 레 에 트 스 반 면 의 행 베 호 이 스 을 출 ▁post ▁which ▁calls g ▁whereas ▁can ▁be▁by 두 니 습 ▁makes ▁achieved ▁database 있 ▁sameinsert on conflict기능을 제공합니다.Spring Data에서 이를 구현하는 방법에 대한 조언이 있습니까?

한 가지 방법은 네이티브 쿼리를 작성하는 것입니다.insert into values (all fields here)해당 객체는 25개 정도의 필드를 가지고 있기 때문에 저는 동일한 것을 달성할 수 있는 다른 더 나은 방법을 찾고 있습니다.

@JBizet이 언급했듯이, 당신은 데이터를 읽고 발견되면 업데이트하고 그렇지 않으면 삽입하는 것으로 당신의 질문에 답했습니다.스프링 데이터를 사용하여 이를 수행할 수 있는 방법은 다음과 같습니다.Optional.

를 합니다.findByField1AndField2AndField3의 신의방에 .DeviceMetricRepository.

public interface DeviceMetricRepository extends JpaRepository<DeviceMetric, UUID> {
    Optional<DeviceMetric> findByField1AndField2AndField3(String field1, String field2, String field3);
}

서비스 메서드에서 리포지토리를 사용합니다.

    @RequiredArgsConstructor
    public class DeviceMetricService {
        private final DeviceMetricRepository repo;
        DeviceMetric save(String email, String phoneNumber) {
            DeviceMetric deviceMetric = repo.findByField1AndField2AndField3("field1", "field", "field3")
                .orElse(new DeviceMetric()); // create new object in a way that makes sense for you
            deviceMetric.setEmail(email);
           deviceMetric.setPhoneNumber(phoneNumber);
        return repo.save(deviceMetric);
    }
}

관찰 가능성에 대한 조언:이는 시스템의 높은 처리량 사용 사례라고 언급했습니다.어떤 접근 방식을 취하든 이 저장 주위에 타이머를 설치하는 것을 고려합니다.이렇게 하면 객관적인 방식으로 수행한 모든 조정에 대해 초기 성능을 측정할 수 있습니다.이 실험을 살펴보고 필요에 따라 다른 솔루션으로 전환할 준비를 하십시오.이 세 개의 열을 항상 함께 읽는 경우 인덱스가 있는지 확인합니다.이러한 기능을 사용하면 업데이트/삽입 여부를 확인할 수 있습니다.

명명된 쿼리를 사용하여 후보 키를 기준으로 행을 가져오는 것이 좋습니다.행이 있으면 해당 행을 업데이트하고, 그렇지 않으면 새 행을 만듭니다.이 두 작업은 모두 저장 방법을 사용하여 수행할 수 있습니다.

@NamedQuery(name="getCustomerByNameAndEmail", query="select a from Customers a where a.name = :name and a.email = :email");

또한 도면요소에 대한 @UniqueColumns() 주석을 사용하여 이러한 열이 함께 그룹화될 때 항상 고유성을 유지하도록 할 수 있습니다.

Optional<Customers> customer = customerRepo.getCustomersByNameAndEmail(name, email);

저장소에 위의 방법을 구현합니다.쿼리를 호출하고 이름과 전자 메일을 매개 변수로 전달합니다.행이 없으면 Optional.empty()를 반환해야 합니다.

Customers c;
if (customer.isPresent()) {
    c = customer.get();
    c.setEmail("newemail@gmail.com");
    c.setPhone("9420420420");
    customerRepo.save(c);
} else {
    c = new Customer(0, "name", "email", "5451515478");
    customerRepo.save(c);
}

ID를 0으로 전달하면 JPA가 시퀀스 생성기에 따라 생성된 ID로 새 행을 삽입합니다.

숫자를 ID로 사용하는 것은 권장하지 않지만, 가능하면 기본 키에 임의로 생성된 UUID를 사용하면 고유성을 보장하고 시퀀스 생성기에서 발생할 수 있는 예기치 않은 동작을 방지할 수 있습니다.

스프링 JPA를 사용하면 깨끗한 자바 코드로 이를 구현하는 것이 매우 간단합니다.Spring Data JPA 방법 사용T getOne(ID id)DB 자체를 쿼리하는 것이 아니라 DB 객체(프록시)에 대한 참조를 사용하는 것입니다.따라서 엔티티를 업데이트/저장할 때 일회성 작업을 수행합니다.

객체를 수정할 수 있도록 Spring에서 제공하는@Transactional주석 - 메서드가 트랜잭션을 시작하고 메서드 자체가 런타임을 종료할 때만 종료함을 선언하는 메서드 수준 주석입니다.

다음을 수행해야 합니다.

  • jpa 트랜잭션 시작
  • getOne을 통해 DB 참조 가져오기
  • DB 참조 수정
  • 그것을 데이터베이스에 저장합니다.
  • 거래를 종료합니다.

실제 코드를 잘 볼 수 없기 때문에 가능한 한 많이 추상화할 것입니다.

@Transactional
public void saveOrUpdate(DeviceMetric metric) {
    DeviceMetric deviceMetric = metricRepository.getOne(metric.getId());
    //modify it
    deviceMetric.setName("Hello World!");
    metricRepository.save(metric);
}

까다로운 부분은 getOne을 DB의 SELECT로 생각하지 않는 것입니다.'저장' 메서드가 있을 때까지 데이터베이스가 호출되지 않습니다.

언급URL : https://stackoverflow.com/questions/58352334/spring-data-hibernate-save-entity-with-postgres-using-insert-on-conflict-updat

반응형