Hibernate 6 migration and dynamic-component or dynamic fields

246 Views Asked by At

I migrate a Spring 5/Hibernate 5 application to Jakarta EE and have an issue with the Hibernate 6 migration.

Since Hibernate 4 I used the hbm.xml and the <dynamic-component/> for adding custom fields to entities at the start of the application.

With Hibernate 6 I get the following warning:

WARN  org.hibernate.orm.deprecation - HHH90000028: Support for `<hibernate-mappings/>` is deprecated [INPUT_STREAM : D:\Workspace\IntelliJ\App-JakartaEE\build-target\app-3.1.00\WEB-INF\classes\generated.hbm.xml]; migrate to orm.xml or mapping.xml, or enable `hibernate.transform_hbm_xml.enabled` for on the fly transformation

Now I tried to migrate from hbm.xml to orm.xml/mapping.xml, but I don't find an equivalent to <dynamic-component/>. I had a look on the hibernate transformation triggered by the setting hibernate.transform_hbm_xml.enabled. There I found this:

HbmXmlTransformer

else if ( hbmAttributeMapping instanceof JaxbHbmDynamicComponentType ) {
                final String name = ( (JaxbHbmDynamicComponentType) hbmAttributeMapping ).getName();
                handleUnsupported(
                        "<dynamic-component/> mappings not supported for transformation [name=%s]",
                        name
                );
            }

So the transformation of <dynamic-component/> is not supported. Now I search for a alternative way to add custom fields to the entities at the start of the application. In the best case represented as Map like <dynamic-component/> and similar schema.

Sources

hbm.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="de.user.app.domain">

    <class name="de.user.app.domain.CustomFields" 
        abstract="true" lazy="false" optimistic-lock="version">
        <id name="id" type="java.lang.String" length="36">
            <generator class="uuid2" />
        </id>
        <version name="version" />
        
    </class>

<union-subclass name="de.user.app.domain.AddressCustomFields" extends="de.user.app.domain.CustomFields" lazy="false">
 <dynamic-component name="fields">
  <property name="notes" type="java.lang.String"  lazy="false" length="60" not-null="false" />
  <property name="distance" type="java.lang.Double"  lazy="false" length="20" not-null="false" />
 </dynamic-component>
</union-subclass>

<union-subclass name="de.user.app.domain.MeasurementCustomFields" extends="de.user.app.domain.CustomFields" lazy="false">
 <dynamic-component name="fields">
  <property name="name" type="java.lang.String"  lazy="false" length="60" not-null="false" />
  <property name="volume" type="java.lang.Double"  lazy="false" length="20" not-null="false" />
 </dynamic-component>
</union-subclass>

...
</hibernate-mapping>

CustomFields:

public abstract class CustomFields implements Map<String, Object> {

    private static final long serialVersionUID = 1L;

    private String id;

    private int version;

    private Map<String, Object> fields;

    @Override
    public int size() {
        if (fields == null) {
            return 0;
        } else {
            return fields.size();
        }
    }

    @Override
    public boolean isEmpty() {
        if (fields == null) {
            return true;
        } else {
            return fields.isEmpty();
        }
    }

    @Override
    public boolean containsKey(Object key) {
        if (fields == null) {
            return false;
        } else {
            return fields.containsKey(key);
        }
    }

    @Override
    public boolean containsValue(Object value) {
        if (fields == null) {
            return false;
        } else {
            return fields.containsValue(value);
        }
    }

    @Override
    public Object get(Object key) {
        if (fields == null) {
            return null;
        } else {
            return fields.get(key);
        }
    }

    @Override
    public Object put(String key, Object value) {
        if (fields == null) {
            fields = new HashMap<>();
        }
        return fields.put(key, value);
    }

    @Override
    public Object remove(Object key) {
        if (fields == null) {
            return null;
        } else {
            return fields.remove(key);
        }
    }

    @Override
    public void putAll(Map<? extends String, ? extends Object> m) {
        if (fields == null) {
            fields = new HashMap<>();
        }
        fields.putAll(m);
    }

    @Override
    public void clear() {
        if (fields != null) {
            fields.clear();
        }
    }

    @Override
    public Set<String> keySet() {
        if (fields == null) {
            return null;
        } else {
            return fields.keySet();
        }
    }

    @Override
    public Collection<Object> values() {
        if (fields == null) {
            return null;
        } else {
            return fields.values();
        }
    }

    @Override
    public Set<Entry<String, Object>> entrySet() {
        if (fields == null) {
            return null;
        } else {
            return fields.entrySet();
        }
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder(17, 37).append(id).toHashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }

        if (!(obj instanceof CustomFields)) {
            return false;
        }

        CustomFields rhs = (CustomFields) obj;

        return new EqualsBuilder().append(id, rhs.getId()).isEquals();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this).append("fields", fields).toString();
    }
    
    public Map<String, Object> getFields() {
        return fields;
    }

    public void setFields(Map<String, Object> customFields) {
        this.fields = customFields;
    }

    @Override
    public String getId() {
        return id;
    }

    @Override
    public void setId(String id) {
        this.id = id;
    }

    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }

}
0

There are 0 best solutions below