【问题标题】:JPA-Repository save: Proceed saving after insert Constraint violationJPA-Repository 保存:插入约束冲突后继续保存
【发布时间】:2020-02-12 12:43:31
【问题描述】:

我正在使用 JPA 存储库将简单的数据对象保存到数据库中。为了避免重复,我在多个字段上创建了唯一约束。如果现在应该保存根据唯一字段/约束的副本,我想捕获异常,记录对象,应用程序应该继续并保存下一个对象。但是在这里我总是得到这个异常:“org.hibernate.AssertionFailure: null id in de.test.PeopleDBO 条目(发生异常后不要刷新会话)”。

一般来说,我了解 hibernate 在做什么,但我如何才能恢复会话或启动新会话以继续保存下一个数据对象。请看下面的代码:

PeopleDBO.java

@Entity
@Data
@Table(
        name = "PEOPLE", 
        uniqueConstraints = {@UniqueConstraint(columnNames = {"firstname", "lastname"}})
public class PeopleDBO {

    public PeopleDBO(String firstname, String lastname) {
        this.firstname = firstname;
        this.lastname = lastname;
    }

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

    private String firstname;

    private String lastname;

}

测试:

public void should_save_people_and_ignore_constraint_violation(){
    final List<PeopleDBO> peopleList = Arrays.asList(
        new PeopleDBO("Georg","Smith"),
        new PeopleDBO("Georg","Smith"),
        new PeopleDBO("Paul","Smith")
    );

    peopleList.forEach(p -> {
        try {
            peopleRepository.save(p);
        } catch (DataIntegrityViolationException e) {
            log.error("Could not save due to constraint violation: {}",p);
        }
    }

    Assertions.assertThat(peopleRepository.count()).isEqualTo(2);
}

问题是,如果保存第二个人,就会违反唯一约束。错误日志发生,下一次调用 peopleRepository.save() 时抛出上述异常:

“org.hibernate.AssertionFailure: de.test.PeopleDBO 条目中的空 id(发生异常后不刷新 Session)”

我怎样才能避免这种行为?如何清理会话或开始新会话?

提前非常感谢 d.

------ 编辑/新想法------ 我刚刚尝试了一些事情,发现我可以实现一个 PeopleRepositoryImpl,如下所示:

@Service
public class PeopleRepositoryImpl {

    final private PeopleRepository peopleRepository;

    public PeopleRepositoryImpl(PeopleRepository peopleRepository) {
        this.peopleRepository = peopleRepository;
    }

    @Transactional
    public PeopleDBO save(PeopleDBO people){
        return peopleRepository.save(people);
    }
}

这在我的测试中运行良好。 ...你怎么看?

【问题讨论】:

    标签: java hibernate jpa spring-data-jpa unique-constraint


    【解决方案1】:

    单笔交易

    原因是所有插入都发生在一个事务中。由于这个事务是原子的,它要么完全成功,要么失败,中间没有任何东西。

    最干净的解决方案是在尝试插入之前检查 People 是否存在:

    public interface PeopleRespository {
    
        boolean existsByLastnameAndFirstname(String lastname, String firstname);
    }
    

    然后:

    if (!peopleRepository.existsByLastnameAndFirstname(p.getLastname, p.getFirstname)) {
        peopleRepository.save(p);
    }
    

    每人一次交易

    另一种选择确实是为每个人开始新的交易。但我不确定它是否会更有效,因为创建交易需要额外的成本。

    【讨论】:

    • 我想这个答案可能会出现:) ...我的意图是避免额外的查找请求。
    • 为什么要避免查找请求?表现 ?如果您在 (lastname, firstname) 列上有一个正确的索引,如果您定义了一个唯一键,通常就是这种情况,您应该不会注意到显着差异。
    • 是的,这是有道理的。我只是想,由于唯一约束,数据库无论如何都会执行此检查,因此我可以保存性能以获取额外的 findByFirstnameAndLastname。
    • 请注意,如果您使用existsByLastnameAndFirstname 而不是findByLastnameAndFirstname,则查询只会查看索引,不会获取实际数据。
    • 不客气。如果这回答了您的问题,请随时接受和/或投票。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多