Saving detached entity does not save null columns

I have a problem when saving columns that were set to null. I’ll give a simple example. Let’s start with the following entity classes. I’ll skip a thing or two to keep this post short.

@MappedSuperclass
public abstract class AbstractEntity implements Serializable {
  @Id
  private Long id;
  ...
}
@MappedSuperclass
public abstract class AbstractElement extends AbstractEntity {
  ...
  @Column(length = 80)
  private String description;
  ...
}
@Entity
@Table(name = "my_element")
public class MyElement extends AbstractElement {
  ...
  @Column(precision = 38, scale = 18)
  private Double height;
  ...
}

I add to this a repository:

@Repository
public interface MyElementRepository extends JpaRepository<MyElement, Long> {}

DTOs are defined in a service package:

public abstract class AbstractDto implements Serializable {
  private Long id;
  ...
}
public abstract class AbstractElementDto extends AbstractDto {
  ...
  private String description;
  ...
}
public class MyElementDto extends AbstractElementDto {
  ...
  private Double height;
  ...
}

Mapping between DTOs and entities is done using Dozer. A DTO is modified from a Java FX UI. A service has been defined between UI and persistence to save modified entities.

@Service
@Transactional
public class MyElementService {
  @Autowired MyElementRepository myElementRepository;

  ...

  public List<MyElementDto> save(List<MyElementDto> dtosToSave) {
    List<MyElementDto> results = Collections.emptyList();

    if (dtosToSave != null && !dtosToSave.empty()) {
      Iterable<MyElement> entities = convertDtosWithDozer(dtosToSave);
      List<MyElement> savedEntities = myElementRepository.saveAll(entities);
      results = convertEntitiesWithDozer(savedEntities);
    }
    return results;
}

The problem is that if I set description to null in the interface, the change is not stored in the database. It’ll work fine with any non null values. Same problem with the height column.

Now, let’s define another entity:

@Entity
@Table(name = "my_other_element")
public class MyElement extends AbstractEntity {
  ...
  @Column(length = 80)
  private String description;
  ...
}

Equivalent repository, dto ans service are also defined. Now, what troubles me here is that description can be set to null in MyOtherEntity. The major difference between MyEntity and MyOtherEntity: an extra level of @MappedSuperClass. It’s important to mention that these columns are nullable in the database.

I’m aware that entities converted with Dozer are detached. But why can description be set to null in one case but not in the other? If I load a copy of my first entity from my database and set its description to null, it’ll work because I modified an attached entity.

For fun, I ran debugger into org.hibernate.type.TypeHelper.replace(Object[] original, Object[] target, Type[] types, SharedSessionContractImplementor session, Object owner, Map copyCache). The interesting part is that original contains non null columns of the entity to be saved, when I want to save a detached copy of MyEntity. This means that setting description to null is ignored.

Any ideas what’s going on? How can a null value be saved in MyEntity? Will I have to load it first? I thought JPA was taking care of this.

Regards

Francois

UPDATE 2020-04-23
Let me add some more information. Part of the problem seems to be related to bytecode enhancement on the entities. When deactivated, entity saving works as requested. But due to bytecode enhancement, null columns on detached entities are ignored during comparison with columns from proxied entities, meaning null column values are not saved.

Also, when debugging deep into Hibernate code, I noticed that null value is indeed set to the attached entity to be saved. But the field $$_hibernate_tracker never included the column name that was set to null.

Francois