javahibernatecrud-repository

CrudRepository - Hibernate - Overwrite existing model with manual set id


I have a problem which I try to figure out since many hours now.

I must save a model with manual set id in the database using CrudRepository and Hibernate.

But the manual set of the id is ignored always.

Is it somehow possible, to force

CrudRepository.save(Model m)

to persist the given Model with UPDATE?

The queries always results in INSERT statements, without using the id.

The reason I must do this manually is, that the identifier is not the database ID - it is a ID generated outside as UUID which is unique over multiple databases with this model-entry. This model is shared as serialized objects via hazelcast-cluster.

Following an example: The database already contains a Model-Entry with the id 1:

id    identifier_field_with_unique_constraint     a_changing_number
1     THIS_IS_THE_UNIQUE_STRING                   10

Now I need to update it. I create a new Model version

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
}

The id Field is tagged with @Id and @GeneratedValue annotation (for the case that the id is null and the id should be generated)

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)

I even tried to changed this field only to an @Id field without @GeneratedValue and generate the ID always on my own. It had no effect, it always used INSERT statements, never UPDATE.

What am I doing wrong?

Is there another identifier for the CrudRepository that declares the model as an existing one, other than the id?

I'm happy for any help.


Solution

  • CrudRepository has only save method but it acts for both insert as well as update.

    1. When you do save on entity with empty id it will do a save.
    2. When you do save on entity with existing id it will do an update that means that after you used findById for example and changed something in your object, you can call save on this object and it will actually do an update because after findById you get an object with populated id that exist in your DB.

    In your case you are fetching the records based on a field (unique) But records will update only when the model object has a existing primary key value

    In your code there should be presentModalOpt instead of 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);
        
    }
    

    See the default implementation -

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