Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating an entity with updatable=false set the field to null

I'm using SpringBoot with JPA/Hibernate and I'd like to add auditing.

I have created an abstract entity with the following :

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

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

The application

I'm developping a RESTful API with a simple CRUD on entities. My resources(controller) are using the save() method on jpa repositories for create and update

The problem :

When I'm calling repository.save() on an Entity to update it, the "createdDate" is ignored (that's normal) but the entity which is returned by the method has its"createdDate" set to null.

Then the entity is put in the cache and the next get will have its createdDate value to null also.

How can I update an entity without allowing the modification of the field createdDate and using JpaCrudRepository ?

Update

Repository definition :

public interface ModelRepository extends JpaRepository<Model, Long> {

REST Controller :

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);
}

The Json returned by the endpoint contains "createdDate: null"

Update 2

To fix this issue I have created a service like this :

    @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;
}

To make this work, i have also removed the "updatable = false" from createdDate field to prevent the result from having a null date after the save.

I don't understand the purpose of having "updatable = false" if we have to handle it manually after.

like image 717
John D Avatar asked Oct 29 '22 12:10

John D


1 Answers

If we consider just plain Hibernate for a moment, it is perfectly legal to specify updatable=false on a mapping but obviously the value must be initialized somewhere for Hibernate to know what value to place in the column at insertion.

Your solution by setting the value yourself is exactly what users of plain Hibernate must do.

But I notice your model uses a @CreatedDate annotation, which from my brief googling appears to be something that Spring Data exposes and has nothing to do with Hibernate. That to me implies that something may not be properly configured here.

I've never used this from Spring data, but you can find another post here. Perhaps it can help highlight where your configuration may be off and why the @CreatedDate annotation isn't initializing the field as you intended.

UPDATE

In situations where an external source supplies you with state that you need to apply atop of an existing entity such as the case of your update method, you first need to retrieve the entity and overlay your input accordingly.

// 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() );
}

Hope that helps.

like image 120
Naros Avatar answered Nov 15 '22 10:11

Naros