【问题标题】:Getting "Duplicate key value violates unique constraint" with Postgres使用 Postgres 获取“重复键值违反唯一约束”
【发布时间】:2020-04-08 01:40:31
【问题描述】:

我正在开发 Springboot 应用程序。


问题陈述:
我正在使用 sequence 来生成 id。 当我第一次点击控制器时,没有异常,数据成功保存到数据库中。
但是当我第二次点击控制器时,我得到了:

**duplicate key violates unique constraint, Detail: 
Key(id)=(100) already exits.** <br/>

但是 id = 100 之前已经插入了 10 条记录。

我有以下 POJO:

package com.example.demo;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "student_tbl", schema = "user")
public class Student {

@Id
private Integer id;
private String name;
private String className;

public String getName() {
    return name;
}

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

public String getClassName() {
    return className;
}

public void setClassName(String className) {
    this.className = className;
}

public Integer getId() {
    return id;
}

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

}

学生资料库

package com.example.demo;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface StudentRepository extends JpaRepository<Student, Integer> {

@Query(value = "select nextval('user.student_id')", nativeQuery = true)
Integer createId();

}

坚持学生

package com.example.demo;

import java.util.List;
import java.util.stream.IntStream;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PersistStudent {

@Autowired
private StudentRepository studentRepository;

@PersistenceContext
EntityManager entityManager;

@Transactional
public void persistStudent(List<Student> students) {

    try {

        IntStream.range(0, students.size()).forEach(index -> {
            if (students.size() == index || index % 100 == 0) {
                entityManager.flush();
                entityManager.clear();
            }

            entityManager.persist(students.get(index));
        });
    } catch (Exception e) {
        System.out.println("Exception:: " + e);
    }
}

}

控制器

package com.example.demo;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class StudentContoller {

@Autowired
private PersistStudent persistStudent;

@Autowired
private StudentRepository studentRepo;

@GetMapping(value = "/save")
public void saveStudent() {

    List<Student> students = new ArrayList<Student>();
    Student stu = new Student();
    stu.setClassName("VII");
    stu.setName("John Doe");
    stu.setId(studentRepo.createId());

    students.add(stu);

    persistStudent.persistStudent(students);
}

}

请帮忙。

【问题讨论】:

  • 如果只有 JPA 提供了一种基于序列自动生成 ID 的方法。哦等等...docs.jboss.org/hibernate/orm/current/userguide/html_single/…
  • 请显示该表的DDL和该表的pk序列的DDL。
  • @JBNizet:其实分配id是有逻辑的,即if(id !=null){generate id from sequence},否则根本不生成id。
  • 这是一个糟糕的主意:现在每次序列生成一个之前选择的 ID 而不从序列中获取它时,你都会得到一个异常。这可能正是这里发生的事情,以及您问这个问题的原因:您生成了一个 ID 却没有从序列中获取它。所以当然如果序列生成相同的 ID,你会得到一个异常。
  • 但是序列怎么会生成已经生成的id。该序列将始终生成一个新的 id。例如,如果最后生成的 id 是 101,那么 next 将是 102。我看不到这里发生的错误。

标签: java spring postgresql spring-boot jpa


【解决方案1】:

您可以通过简单地使用 @Generated 注释字段 Student.id 来避免管理实体 ID 的全部开销。有了这个,您可以简单地将 id 字段留空,并在插入时将唯一的 id 分配给实体。我还建议使用 JpaRepository 中的方法进行创建/读取/更新/删除操作。

代码将变为:

@Entity
@Table(name = "student_tbl", schema = "user")
public class Student {

@Id
@Generated
private Integer id;
private String name;
private String className;

....

@Service
public class PersistStudent {

@Autowired
private StudentRepository studentRepository;

@Transactional
public void persistStudent(List<Student> students) {
    studentRepository.saveAll(students);
}
}

....

@RestController
public class StudentContoller {

@Autowired
private PersistStudent persistStudent;

@GetMapping(value = "/save")
public void saveStudent() {

    List<Student> students = new ArrayList<Student>();
    Student stu = new Student();
    stu.setClassName("VII");
    stu.setName("John Doe");
    students.add(stu);

    persistStudent.persistStudent(students);
}

}

【讨论】:

  • 感谢您的回复:我已经尝试过这种方法(studentRepository.saveAll(students);),但是将批量数据保存到表中需要很长时间。
【解决方案2】:

希望您的序列运行良好,然后您无需致电stu.setId(studentRepo.createId());

只需在您的 id 字段中添加以下注释:

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "user_id")
  private int id;

【讨论】:

  • 感谢回答,但是:其实是有分配id的逻辑,即if(id !=null){generate id from sequence},否则根本不生成id跨度>
  • 这个逻辑的用例是什么?
  • 用例是:如果有新学生来了,那么只有我们会生成Id,否则不会。
  • 如果一个学生不是新的,那么你也会做一个插入?
  • 是的,我们将插入。如果学生年龄较大,那么它将有一个 id。所以不需要生成新的id。
猜你喜欢
  • 2020-05-28
  • 2012-06-20
  • 2012-03-03
  • 2019-03-20
  • 2022-11-04
  • 2021-04-10
  • 1970-01-01
  • 2022-01-13
相关资源
最近更新 更多