org.postgresql.util.PSQLException: ОШИБКА: повторяющееся значение ключа нарушает уникальное ограничение «_pkey»

#java #spring #postgresql #hibernate #one-to-many

#java #весна #postgresql #спящий режим #один ко многим

Вопрос:

Я получаю эту ошибку, когда пытаюсь обновить два объекта с помощью отношения один ко многим.

Это первый класс сущности:

 package com.example.demo.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.SelectBeforeUpdate;

@Entity
@Table(name = "tags", schema = "public")
@NoArgsConstructor
@SelectBeforeUpdate
@Data
public class Tag {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(unique = true, nullable = false)
  private Long id;

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

  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "product_id", nullable = false)
  private Product product;

  public Tag(String tag, Product product) {
    this.tag = tag;
    this.product = product;
  }
}
 

Это второй класс объектов:

 package com.example.demo.entity;

import com.example.demo.dto.ProductDto;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;

@Entity
@NoArgsConstructor
@Table(name = "products", schema = "public")
@Data
public class Product {

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

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

  @Column(name = "price")
  private double price;

  @Column(name = "added")
  private Date added;

  @OneToMany(mappedBy = "product", cascade = CascadeType.MERGE, orphanRemoval = true)
  @Fetch(value = FetchMode.SELECT)
  private List<Tag> tags;

  public Product(final ProductDto productDto) {
    this.id = productDto.getId();
    this.name = productDto.getName();
    this.price = productDto.getPrice();
    this.added = new Date(productDto.getAdded());
    this.tags = convertTagDtoToTag(productDto.getTags());
  }

  public List<Tag> convertTagDtoToTag(final List<String> tagDtos) {
    return tagDtos
        .stream()
        .map(t -> new Tag(t, this))
        .collect(Collectors.toList());
  }
}
 

Этот метод, в котором я пытаюсь обновить:

   public Product updateProduct(final ProductDto productDto) {
    final Product product = this.productRepository.findById(productDto.getId()).map(p -> {
      p.setName(productDto.getName());
      p.setPrice(productDto.getPrice());
      p.setAdded(new Date(productDto.getAdded()));
      p.getTags().clear();
      p.getTags().addAll(p.convertTagDtoToTag(productDto.getTags()));

      return p;
    }).orElse(null);

    return this.productRepository.save(product);
  }
 

И я получаю эту ошибку:

 org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "tags_pkey"
  Detail: Key (id)=(1) already exists.
 

Я пишу этот метод, но я думаю, что есть лучший способ.

   public Product updateProduct(final ProductDto productDto) {
    final Product product = this.productRepository.findById(productDto.getId()).map(p -> {
      p.setName(productDto.getName());
      p.setPrice(productDto.getPrice());
      p.setAdded(new Date(productDto.getAdded()));
      updateTag(p.getTags(), p.convertTagDtoToTag(productDto.getTags()));

      return p;
    }).orElse(null);

    return this.productRepository.save(product);
  }

  private void updateTag(List<Tag> serverTags, List<Tag> frontedTags) {
    for (int i = 0; i < serverTags.size(); i  ) {
      serverTags.get(i).setTag(frontedTags.get(i).getTag());
    }
  }
 

Также у меня есть файл data.sql

 INSERT INTO products (id, name, price, added) values (1, 'First product', 100, '2000-09-01');

INSERT INTO products (id, name, price, added)
values (2, 'Second product', 200, '2010-10-11');

INSERT INTO products (id, name, price, added)
values (3, 'Third product', 300, '2010-11-04');

INSERT INTO tags (id, tag, product_id)
values (1, 'tag1', 1);

INSERT INTO tags (id, tag, product_id)
values (2, 'tag2', 1);

INSERT INTO tags (id, tag, product_id)
values (3, 'tag3', 1);

INSERT INTO tags (id, tag, product_id)
values (4, 'tag4', 2);

INSERT INTO tags (id, tag, product_id)
values (5, 'tag5', 3);
 

Похоже, что ваш пост в основном состоит из кода; пожалуйста, добавьте еще несколько деталей.
Похоже, что ваш пост в основном состоит из кода; пожалуйста, добавьте еще несколько деталей.
Похоже, что ваш пост в основном состоит из кода; пожалуйста, добавьте еще несколько деталей.
Похоже, что ваш пост в основном состоит из кода; пожалуйста, добавьте еще несколько деталей.

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

1. Первое, что я вижу, вы не должны очищать свои теги сущностей. Просто зациклите его и обновите входящими данными (но не трогайте идентификаторы). Если вы хотите добавить новые теги — просто создайте новые и добавьте их к существующим тегам. Во-вторых, это может быть проблемой стратегии генерации идентификаторов. Вероятно, Hibernate не видит существующий идентификатор (1) и пытается начать свою собственную последовательность с начала, которое тоже равно 1… Иногда это случается, особенно если вы заполняете таблицы образцами данных вручную или с помощью таких инструментов, как liquibase.

2. У меня есть файл data.sql

Ответ №1:

Пожалуйста, попробуйте вставить, получив таким образом значение следующего идентификатора:

 INSERT INTO products (id, name, price, added)
values (
       SELECT setval(pg_get_serial_sequence('products', 'id'), coalesce(max(id) 1, 1), false) FROM products, 
       'Second product', 
       200, 
       '2010-10-11'
       );