【问题标题】:How to insert record into database using spring jpa when there is no conflict没有冲突时如何使用spring jpa将记录插入数据库
【发布时间】:2019-08-16 09:29:31
【问题描述】:

我有使用 spring-data-jpa 与 PostgreSQL 数据库连接的 Spring Boot 应用程序,这是我的实体,有近 40 个字段

现在为了将实体保存到数据库中,我只是使用studentRepository.save 方法

studentRepository.save(new StudentEntity());

DAO 实体

  @Table
  @Entity
  public class StudentEntity {

     @Id
     @Generate( using database sequenece)
     private long studentId;

     private String studentName;

     private String dept;

     private String age;
      ..
      ..
      }

存储库

 public interface StudentRepository implements JPARepository<Long, Student> {
   }

但是现在我有一个要求,如果表中有 namedept 的任何学生记录,我不应该插入新记录,我知道我可以使用 PostgreSQL ON CONFLICT 进行本机查询,但是如果我使用本机查询,我必须在查询中指定所有 40 个字段,并将其作为方法参数,这看起来很丑。

有什么方法可以让它更简单吗?

本机查询示例

@Query(value = "insert into Users (name, age, email, status) values (:name, :age, :email, :status)", nativeQuery = true)

 void insertUser(@Param("name") String name, @Param("age") Integer age, @Param("status") Integer status, @Param("email") String email);

【问题讨论】:

  • 声明这些属性的唯一索引,无条件插入,并捕获/翻译抛出的异常?
  • 我不确定先生您在解释什么@chrylis
  • 我们遇到了类似的问题,我不得不使用 MYSQL 数据库特有的“ON DUPLICATE KEY”功能。最初我们继续使用 @Query,但不可读。因此,我们搜索并发现JOOQ 是此类情况的合适替代方案。
  • 您能否为上述场景提供一个示例,以及如何集成该示例?

标签: java postgresql spring-boot spring-data-jpa


【解决方案1】:

使用数据库。在 2 个字段上创建 unique constraint 并尝试添加多个字段将被阻止。

类似

ALTER TABLE StudentEntity ADD CONSTRAINT UQ_NAME_DEPT UNIQUE (studentName,dept);

此唯一约束将防止插入重复的组合。

您还可以在 JPA 实体上定义一个约束来自动创建用于测试的索引

@Table(uniqueConstraints=@UniqueConstraint(columnNames = {"studentName", "dept"})

【讨论】:

  • 我会试试这个谢谢,插入重复记录时它会抛出任何异常吗?还是会忽略?
  • 在异常的情况下,弹簧会关闭连接部分,对吗?作为程序员,除了处理它,我们还需要做其他事情吗?
  • 我强烈建议您阅读您正在使用的技术(JPA 和 Spring)。不,您不需要做任何事情(如果您以正常方式使用瘦身!)。您也可以在存储库中使用save 方法,不需要单独的插入方法,也不需要专门的查询。
  • 唯一约束方法(相对于insert... on conflict do nothing)的一个缺点是休眠会将冲突记录为错误。因此,根据这种情况发生的频率,它可能值得也可能不值得您打扰。 (是的,您可以将记录器配置为忽略来自 Hibernate 的 SqlExceptionHelper 类的错误消息,但这可能会隐藏其他真正的错误)。
  • 约束开箱即用。 insert .. on conflict do nothing 需要编写自定义查询,并且不适用于所有数据库。
【解决方案2】:

如果你的首要任务是减少冗长,因为它是原生 sql,如果你把它们按正确的顺序排列,你可以避免字段的名称,但你必须把表的所有值,即使是 null:

@Query(value = "insert into Users values (:name, :age, :email, :status)", nativeQuery = true)
 void insertUser(@Param("name") String name, @Param("age") Integer age, @Param("status") Integer status, @Param("email") String email);

或者,如果 findByStudentNameAndDebt(studentName, dept) 方法没有返回任何结果,则可以保存,而不是使用本机插入。在 StudentRepository 中(这里不需要本地):

@Query("SELECT a FROM Student as a WHERE lower(a.studentName) = lower(:studentName) AND lower(a.dept) = lower(:dept)")
    public Student findByStudentNameAndDebt(@Param("studentName") final String studentName, @Param("dept") final String dept);

为您服务:

if(studentRepository.findByStudentNameAndDebt(studentName, dept)==null) {
   studentRepository.save(new StudentEntity());
}

【讨论】:

  • 这不是线程安全的。想象一下,如果两个线程同时启动:两者都会从 findByStudentNameAndDebt 获取 null,然后都将保存新实体。
【解决方案3】:

无需原生查询。您也可以通过编写面向对象的查询来做到这一点。首先检查namedept 的表中是否存在任何记录。 如果它返回 null,则进行保存。

StudentRepository.java

public interface StudentRepository implements JPARepository<Long, Student> {


@Query("FROM Student AS stu WHERE lower(stu.name) = lower(:studentName) AND lower(stu.dept) = lower(:dept)")
public Student findStudentByNameAndDept(@Param("studentName") String studentName, @Param("dept") String dept);

//if you have already duplicates in your db, then you can do the followings too..
@Query("FROM Student AS stu WHERE lower(stu.name) = lower(:studentName) AND lower(stu.dept) = lower(:dept)")
public List<Student> findAllStudentsByNameAndDept(@Param("studentName") String studentName, @Param("dept") String dept);

}

StudentService.java

if(studentRepository.findStudentByNameAndDept(name,dept)==null){
    //save
}else{
    //custom hanlding
    throw StudentExistsException("Student with the given name/dept exists");
}

OR
//if already duplicates exisited in db

if(studentRepository.findStudentByNameAndDept(name,dept).isEmpty()){
    //save
}else{
    //custom hanlding
    throw StudentExistsException("Student with the given name/dept exists");
}

【讨论】:

  • 这允许竞争条件发生,当两个线程通过 if 条件并同时进入保存块时。
【解决方案4】:

您可以使用轻量级查询来检查是否已经存在同名同部门的学生

public interface StudentRepository implements JPARepository<Long, Student> {

@Query("SELECT CASE WHEN COUNT(c) > 0 THEN true ELSE false END FROM Student s WHERE s.studentName= :studentName AND s.dept= :dept")
    public boolean existsByNameAndDepartment(@Param("studentName") String studentName, @Param("dept") String dept);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-16
    • 1970-01-01
    • 2017-12-17
    • 1970-01-01
    • 2015-09-10
    • 1970-01-01
    相关资源
    最近更新 更多