当前位置: 首页 > 知识库问答 >
问题:

JPA-存储库保存:插入约束冲突后继续保存

巫新知
2023-03-14

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

一般来说,我知道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条目中的null id(发生异常后不刷新会话)"

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

提前非常感谢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);
    }
}

这在我的测试中效果很好。。。你怎么认为?

共有1个答案

王高超
2023-03-14

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

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

public interface PeopleRespository {

    boolean existsByLastnameAndFirstname(String lastname, String firstname);
}

然后:

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

一个替代方法是为每个人开始一个新的交易。但我不确定这样会更有效率,因为创建交易需要额外的成本。

 类似资料:
  • 我有以下课程: DBEntity.java 使用者JAVA 用户服务。JAVA UserRepository.java

  • 我有一个数据已经保存在我的数据库基于我的存储库和服务。我想保存另一个数据与邮递员只改变播放器id。但它不是创建一个新的实体数据。它更新现有的实体数据。我的问题是如何更新一个数据由我的服务当它找到一个现有的id。但当它找到一个新的id将保存一个新的数据到数据库。 这是我的回购: 这是我的服务:

  • 无论如何,如果用户名已经被使用,存储库只需进行更新,因为指定的标识符不是空的。这不是我想要的行为,我需要它抛出类似重复条目异常的东西。有什么办法可以预防吗?我必须自己做吗?例如:

  • 我有下表,目前我可以更新用户对象来添加或删除角色,但我不能在创建新用户时向其添加角色(用户的角色添加到UI上的同一表单上),因为中间表User_Role希望用户的ID为Obj,所以在插入时,我自然得到一个User.user_id=?。 有什么常见的方法可以解决这个问题吗?我在考虑也许尝试在保存后获取插入用户的ID,然后调用更新函数。我不想这样做,因为我担心性能,特别是在未来可能发生批量上传的时候。

  • 这很可能通过Nexus配置得到解决。 我们使用maven进行hadoop开发。Nexus被配置为所有存储库的镜像,存储库被添加到Nexus公共组中。(参见Nexus中有没有更好的配置存储库的方法?) 我发现hadoop-core工件版本1.0.4显示来自spring-roo-repositoryhttp://spring-roo-repository.springsource.org/releas

  • 我正在尝试创建一个文字游戏,它可以对一个单词进行加密,并根据用户输入的内容将字符移位一定的数字,解密加密,并检查该单词是否为回文。问题是我不知道如何保持输入,所以在我加密或检查它是否为回文后,程序结束,因此我无法解密加密的内容。 例如,如果用户输入单词“hello”并选择加密该单词,密钥为3,则应显示“khoor”。然后,我希望能够通过将“khor”解密回“hello”来继续,但程序结束。 此外,