【问题标题】:CrudRepository - Hibernate - Overwrite existing model with manual set idCrudRepository - Hibernate - 用手动设置 id 覆盖现有模型
【发布时间】:2021-05-31 03:26:27
【问题描述】:

我有一个问题,我已经尝试了好几个小时了。

我必须使用 CrudRepository 和 Hibernate 在数据库中保存具有手动设置 ID 的模型。

但是手动设置的id总是被忽略。

是否有可能,强制

CrudRepository.save(模型 m)

使用 UPDATE 持久化给定的模型?

查询总是产生 INSERT 语句,而不使用 id。

我必须手动执行此操作的原因是,标识符不是数据库 ID - 它是作为 UUID 在外部生成的 ID,在具有此模型条目的多个数据库中是唯一的。该模型通过 hazelcast-cluster 作为序列化对象共享。

以下示例: 数据库已经包含一个 ID 为 1 的模型条目:

id    identifier_field_with_unique_constraint     a_changing_number
1     THIS_IS_THE_UNIQUE_STRING                   10

现在我需要更新它。我创建了一个新的模型版本

Model m = new Model();
m.setIdentifierFieldWithUniqueConstraint(THIS_IS_THE_UNIQUE_STRING);
m.setAChangingNumberField(20);
saveMe(m);

void saveMe(Model m) {
    Optional<Model> presentModalOpt = modelCrudRepo.findByIdentField(THIS_IS_THE_UNIQUE_STRING)
    if(presentModalOpt.isPresent()) {
        // The unique value in my identifier field exists in the database already
        // so use that id for the new model, so it will be overwritten
        m.setId(modalOpt.get().getId());
    } else {
        m.setId(null);
    }
    // This call will now do an INSERT, instead of UPDATE, 
    // even though the id is set in the model AND the id exists in the database!
    modelCrudRepo.save(m);
    // ConstraintViolationException for the unique identifier field.
    // It would be a duplicate now, which is not allowed, because it uses INSERT instead of UPDATE
}

id字段打上@Id和@GeneratedValue注解(对于id为null,需要生成id的情况)

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)

我什至尝试将此字段仅更改为没有@GeneratedValue 的@Id 字段,并始终自行生成ID。它没有效果,它总是使用 INSERT 语句,从不 UPDATE。

我做错了什么?

除了 id 之外,CrudRepository 是否有另一个标识符将模型声明为现有模型?

我很乐意提供任何帮助。

【问题讨论】:

    标签: java hibernate crud-repository


    【解决方案1】:

    CrudRepository 仅具有保存方法,但它既可以用于插入,也可以用于更新。

    1. 当您在 ID 为空的实体上保存时,它会进行保存。
    2. 当您使用现有 ID 保存实体时,它会进行更新 这意味着在您使用 findById 并更改之后 你的对象中的某些东西,你可以在这个对象上调用 save 它 实际上会进行更新,因为在 findById 之后你会得到一个对象 使用您的数据库中存在的填充 ID。

    在您的情况下,您是基于字段(唯一)获取记录,但只有在模型对象具有现有主键值时才会更新记录

    在您的代码中应该有 presentModalOpt 而不是 modalOpt

    void saveMe(Model m) {
        Optional<Model> presentModalOpt = modelCrudRepo.findByIdentField(THIS_IS_THE_UNIQUE_STRING)
        if(presentModalOpt.isPresent()) {  // should be presentModalOpt instead of modalOpt
     
        } else {
            m.setId(null);
        }
        modelCrudRepo.save(m);
        
    }
    

    查看默认实现 -

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
     */
    @Transactional
    public <S extends T> S save(S entity) {
    
         if (entityInformation.isNew(entity)) {
              em.persist(entity);
              return entity;
         } else {
              return em.merge(entity);
         }
    }
    

    【讨论】:

    • 感谢您的回复。是的,它必须是 presentModalOpt,它是 presentModalOpt。当我在这里复制它时,那只是一个写入错误。嗯,这正是我的知识。但是我设置了主键值(这是 id 字段,它有 @Id 注释),并且无论如何它都会插入它......在我看来它必须是一个更新,但它不是。
    • +1 啊,你说得对!问题不是存储库。这是坚持的命令。我在没有 Flush 的情况下对对象进行持久化。调用 save() 后 CrudRepository 不会自动刷新。我不得不使用 JPARepository 和 saveAndFlush 方法。否则我的 findByIdentField() 方法永远找不到现有的条目。
    • 问题出在条件语句中,该语句始终为 false,并且 id 初始化为 null。我认为不需要使用 null if(presentModalOpt.isPresent()) { } else { m.setId(null); 进行 id 初始化} 如果 id 存在,它将更新,否则插入。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-27
    • 2012-03-15
    • 2019-08-11
    • 2019-05-25
    • 1970-01-01
    • 2020-01-27
    • 1970-01-01
    相关资源
    最近更新 更多