Dynamic-component migration

Hi all,

I am trying to migrate from hibernate 4 to hibernate 5.
My xml mapping contains dynamic-component tags.
We were initializing the configuration of these tags during the start of the application by adding elements to the XML DOM. The idea is to read a list of columns from the database to configure a list of extra columns.
But the dynamic-component tag seems to have disappeared from the hibernate 5 documentation.
Can I still use it ?
What could be a good replacement ?

Best regards,
Xavier.

Can you provide an example? I don’t understand what tags you are talking about.

Sorry, the tag name was invisible.
I am referring to HIBERNATE - Relational Persistence for Idiomatic Java

9.5. Dynamic components

You can also map a property of type Map:

 <dynamic-component name="userAttributes">
     <property name="foo" column="FOO" type="string"/>
     <property name="bar" column="BAR" type="integer"/>
     <many-to-one name="baz" class="Baz" column="BAZ_ID"/>
 </dynamic-component>

The semantics of a mapping are identical to . The advantage of this kind of mapping is the ability to determine the actual properties of the bean at deployment time just by editing the mapping document. Runtime manipulation of the mapping document is also possible, using a DOM parser. You can also access, and change, Hibernate’s configuration-time metamodel via the Configuration object.

The old HBM mappings should work the same. We didn’t add it to the docs because HBM mappings are deprecated in favor of JPA.

As for a replacement, show me your mappings, Java classes and DB table involved, and I will try to find you an equivalent with JPA.

Hi Vlad,

The idea is to be able to add some extra attributes (java types) to standard objects. Each of our customer can customize standard class by adding columns to standard table and declaring these extra columns in a meta-data table.
These meta-data are injected in the xml mapping.
This allows customization without touching the code and only adding columns and defining them in the database.

Here is an extract of one mapping :

	<class name="PContact" table="CUSTOMER_CONTACT" >
		<id column="id" name="id" type="java.lang.Integer">
			...
		</id>
		
		<property column="EXTERNALID" name="externalId" length="30" type="java.lang.String">
			<meta attribute="use-in-tostring">true</meta>
			<meta attribute="field-description">ID from external datasource</meta>
		</property>
		<property column="FIRSTNAME" length="100" name="firstName" type="java.lang.String">
			<meta attribute="field-description">first name</meta>
		</property>
		<property column="LASTNAME" length="100" name="lastName" type="java.lang.String">
			<meta attribute="use-in-tostring">true</meta>
			<meta attribute="field-description">last name</meta>
		</property>

		<dynamic-component name="additionalAttributes" />

	</class>

An extract of the associated class :

public class PContact  implements java.io.Serializable {

  /** storage for id : contact identifier */
    private Integer id;
     /** storage for externalId : ID from external datasource */
    private String externalId;
      /** storage for firstName : first name */
    private String firstName;
     /** storage for lastName : last name */
    private String lastName;

 /** storage for additionalAttributes */
     private Map additionalAttributes;

...

/**
     * getter for additionalAttributes
     */
    public Map getAdditionalAttributes() {
        return this.additionalAttributes;
    }
    /**
     * setter for additionalAttributes
     */  
	public void setAdditionalAttributes(Map argadditionalAttributes) {
		this.additionalAttributes = argadditionalAttributes;
    }

}

And finally an extract of the code altering the xml mapping during application start :

InputStream xmlInputStream = classLoader.getResourceAsStream(mappingFile);
Document doc = xmlHelper.createSAXReader(errorHandler, entityResolver).read(new InputSource(xmlInputStream));
// iterate through child elements of root 
for (Iterator<Element> itr = root.elementIterator("class"); itr.hasNext();) {
	Element classElt = itr.next();
	for (Iterator<Element> itr2 = classElt.elementIterator("dynamic-component"); itr2.hasNext();) {
		Element dynElt = itr2.next();
		String dname = null;
		for (Iterator<Attribute> itrC = dynElt.attributeIterator(); itrC.hasNext();) {
			Attribute attribute = itrC.next();
			if ("name".equals(attribute.getName())) {
				dname = attribute.getValue();
				break;
			}
		}
		if ("additionalAttributes".equals(dname)) {
			res = stmt.executeQuery("SELECT PARAMETERNAME,JAVATYPE,SQLVALUE FROM XXX");
			while (res.next()) {
				String className = res.getString(1);
				String type = res.getString(2);
				String sql = res.getString(3);

				dynElt.addElement("property").addAttribute("name", className).addAttribute("column", sql)
				.addAttribute("type", fixSqlType(type));
			}
			res.close();	
		}
	}
}

HbmBinder.bindRoot(new XmlDocumentImpl(doc, new OriginImpl("type", "name")), cfg.createMappings(), Collections.emptyMap(),Collections.EMPTY_SET);

Regards,
Xavier.

You can do something similar with the @ManyToAny mapping. Check out this User Guide section for more details.

I see one big difference : the additional attributes are stored using several extra tables instead of the same table.

Just take a look at this test in Hibernate ORM.

The Glarch entity mapping uses dynamic-component and the test works just fine.

So, you can still use it. In 6.0 or 7.0, HBM will be changed so that we can provide an extension to the JPA orm.xml. However, meanwhile, this mapping should work.

That’s a good thing. I just need to find a (new) way to modify mapping at boot time since it looks like the XML way is over.

Instead of HBMBinder, there is the ModelBinder class.

Thanks, in fact I didn’t need the ModelBinder class. I am now altering the hbm.xml files before giving them to MetadataSources.

How did you achieve this with annotations.Is there a way without using xml files