【问题标题】:Updating an entity with updatable=false set the field to null使用 updatable=false 更新实体将字段设置为 null
【发布时间】:2017-06-20 18:07:45
【问题描述】:

我正在使用带有 JPA/Hibernate 的 SpringBoot,我想添加审计。

我创建了一个具有以下内容的抽象实体:

@CreatedDate
@NotNull
@Column(columnDefinition = "DATETIME(3)", updatable = false)
private ZonedDateTime createdDate;

@LastModifiedDate
@Column(columnDefinition = "DATETIME(3)")
private ZonedDateTime lastModifiedDate;

应用程序

我正在开发一个带有简单 CRUD 实体的 RESTful API。 我的资源(控制器)正在使用 jpa 存储库上的 save() 方法进行创建和更新

问题:

当我在实体上调用 repository.save() 来更新它时,“createdDate”被忽略(这是正常的),但该方法返回的实体将其“createdDate”设置为 null。

然后将实体放入缓存中,下一次获取的 createdDate 值也会为 null。

如何在不允许修改字段 createdDate 和使用 JpaCrudRepository 的情况下更新实体?

更新

存储库定义:

public interface ModelRepository extends JpaRepository<Model, Long> {

REST 控制器:

public ResponseEntity<Model> createModel(@RequestBody Model model) throws URISyntaxException {
        Model result = modelRepository.save(model);
        return ResponseEntity.created(new URI("/api/v1/models/" + result.getId()))
            .body(result);
}

端点返回的Json包含“createdDate: null”

更新 2

为了解决这个问题,我创建了这样的服务:

    @Override
    public Model save(Model model) {
        Model result = modelRepository.save(model);
        return result;
    }

    @Override
    public Model update(Long id, Model model) {
        Model existingModel = modelRepository.findOne(id);
        if (existingModel == null) {
            return null;
        }
        // Non updatable fields
        model.setId(id);
        model.setCreatedDate(existingModel.getCreatedDate());
        Model result = modelRepository.save(model);
        return result;
}

为了完成这项工作,我还从 createdDate 字段中删除了“updatable = false”,以防止结果在保存后出现空日期。

如果我们之后必须手动处理,我不明白设置“updatable = false”的目的。

【问题讨论】:

  • 能否显示保存实体的代码?

标签: hibernate jpa spring-boot


【解决方案1】:

如果我们暂时只考虑普通的 Hibernate,在映射上指定 updatable=false 是完全合法的,但显然必须在某个地方初始化该值,以便 Hibernate 知道在插入时在列中放置什么值。

您自己设置值的解决方案正是普通 Hibernate 用户必须做的。

但我注意到您的模型使用了 @CreatedDate 注释,从我简短的谷歌搜索来看,这似乎是 Spring Data 公开的内容,与 Hibernate 无关。这对我来说意味着这里可能没有正确配置某些东西。

我从未在 Spring 数据中使用过这个,但你可以找到另一篇帖子 here。也许它可以帮助突出您的配置可能在哪里关闭以及为什么 @CreatedDate 注释没有按照您的预期初始化该字段。

更新

在外部源向您提供需要在现有实体之上应用的状态的情况下,例如您的 update 方法的情况,您首先需要检索实体并相应地覆盖您的输入。

// Some controller or business method
public Object updateModel(Model input) {
  final Model model = modelRepository.findOne( input.getId() );
  if ( model != null ) {
    // replicate business case fields
    // sometimes this can be done using frameworks like ObjectMapper, etc.
    // this is just a simple case.
    model.setField1( input.getField1() );
    model.setField2( input.getField2() );
    return modelRepository.update( model );
  }
  throw new UnknownObjectException( "No Model found with id: " + input.getId() );
}

希望对您有所帮助。

【讨论】:

  • @CreatedDate 据我了解工作正常。它是一个完全创建的实体(未检索到)与可更新的 false 的合并,它不会更新(从 DB 中选择)其原始值。我可以理解为什么它没有完成,但我真的不知道这里最好的方法是什么以及人们做什么。检索对象并更新除不可更新之外的每个字段?或者检索对象并强制(如在我的示例中)新对象的不可更新字段?或者最后,在保存之后,通过 entityManager 重新获取(刷新)实体?
  • 您的帖子显示呼叫save 而不是update,因此我感到困惑。我会更新我的答案。
  • JpaRepository 上只有“save”方法,这就是我使用 save() 的原因。但是我们同意这样做,我们不能使用“updatable=false”,因为一旦我们坚持,问题仍然存在。如果可更新设置为 false,则该字段将为空。我真的不喜欢仅仅因为我不想更新字段而必须为每个实体和完整的映射做一个更新方法。审计和只读创建日期这样简单的事情怎么会产生如此多的复杂性
  • 这就是为什么我提到使用控制器或业务方法来处理创建与更新记录的想法。您不一定必须将该概念推送到存储库级别,尽管从技术上讲它是有意义的。当您的/updateModel URI 被调用时,您调用存储库,获取模型,覆盖更改,然后调用存储库的保存方法。这应该会给你带来你想要的结果。
【解决方案2】:

我可以通过刷新和清除 entityManager 然后重新选择数据来解决这个问题。

entityManager.flush(); entityManager.clear();

【讨论】:

    【解决方案3】:

    也许@CreatedDate (org.springframework.data.annotation.CreatedDate) 是错误的注解?我的小型 spring-boot JPA/Hibernate CRUD 服务器在创建日期列上使用注释 @CreationTimestamp (org.hibernate.annotations.CreationTimestamp):

    @CreationTimestamp
    @Column(name = "CREATED_DATE", nullable = false, updatable = false, columnDefinition = "TIMESTAMP")
    private Date created;
    

    请求以空创建日期到达。调用 repo.save() 方法后,魔法发生了,生成的域对象具有非空的创建日期。我不摆弄实体管理器,不刷新,nada。

    我注意到,尽管在列上注释了“可更新 = false”,但更改现有实体上的创建日期值并调用 repo.save() 方法会毫无怨言地保持该更改。这让我很惊讶。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-03-28
      • 2015-12-21
      • 1970-01-01
      • 2022-07-05
      • 2012-05-09
      • 2017-08-31
      • 1970-01-01
      相关资源
      最近更新 更多