Search code examples
javaspringhibernatejpabyte-code-enhancement

Hibernate: Dirty Field Tracking


I am a bit confused right now. I'll try to explain my concern as easy as possible:

I've an Spring Boot app which, of course, has entities. These entities get updated via an thymeleaf form. In this form only some relevant fields are changeable. For example the name of the entity. Other fields like, created, createdby or lastUsedBy are not controlled / changed by this form.

Now heres the problem: If we now change the entity, all other fields are set to null, because they are not in the request. One approach would be to add for these missing fields <input type="hidden"/>inputs. But this not very elegant and error prone. Than I've come to the conclusion, that hibernate should only update those fields which have been changed. So this has to be done via DirtyTracking. I've currently another app, which uses OpenJPA with the openJPA Enhancer and in this app the update only updates the changed fields. My assumption was that the Hibernate enhancer would solve my issue. But even with dirty tracking enabled all fields are updated and information gets lost. I've managed to get it working when I add the @DynamicUpdateannotation to the given entity, but this can't be the right way right?

I double checked that the entities are enhanced and also debugged the whole save process of spring/hibernate. Am I missing something here? Why is hibernate also updating all non-dirty fields?

EDIT

I've checked further and got to this point: The code is from the AbstractEntityTuplizer

public void setPropertyValues(Object entity, Object[] values) throws HibernateException {
    boolean setAll = !entityMetamodel.hasLazyProperties();

    for ( int j = 0; j < entityMetamodel.getPropertySpan(); j++ ) {
        if ( setAll || values[j] != LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
            setters[j].set( entity, values[j], getFactory() );
        }
    }
}

The values object array only has a few values prefilled, only the changed / dirty ones. For example values[5] and values[6]. But ALL setters are called and the values are set to null if they have not been in the values paraemter of the function.. Looks like an bug to me.


Solution

  • I've found another solution which suits my needs better: The problem was not Hibernate after all but Spring MVC.

    Some more code before:

    @PutMapping("/{oid}/edit")
    public String update(@ModelAttribute("oid") @Valid T entity,
                         BindingResult result,
                         Model model,
                         HttpServletRequest request)
    

    The crucial thing was the @ModelAttribute("oid") here. Before I had not the "oid" in it. So Spring was using the model which was posted from the form, which was obviously not complete filled. Only with the form inputs. With adding the "oid" to the annotation, Spring MVC and Spring Data fetches the entity BEFORE applying the form fields. So than we have an complete Entity, which we can save.

    This solution comes close to the second one from @Vlad but omits the cumbersome, manual mapping of changed fields.

    Maybe I had to provide more information for others to get the whole picture for my issue! Otherwise, thank you!