BT

Using Hibernate to Support Custom Domain Object Fields

Posted by Enterra Inc. on Nov 05, 2007 |

Introduction

When developing corporate-level business applications (Enterprise Scale) customers often requires implementing support for extensibility of the application object model not modifying the system source code. Use of extensible domain model allows for development of new functionality without additional effort and overheads:

  1. the application will be used for a more lengthy period
  2. the system workflow can be modified over time when changing external factors
  3. "setting" the application to fit specifics of an enterprise where it has been deployed.

The most simple and cost effective way to achieve the required functionality would be implementing extensible business entities in an application with the support of custom fields.

What is a "Custom Field"?

What is a custom field and how the end user benefit from this? A custom field is an attribute of an object which has not been created by the system developer at the development stage but that has been added by the system user into the object when actually using the system not introducing any changes into the source code of the application.

Which functionality may be demanded?

Let's try to figure out this based on an example of a CRM application. Let's say we have an object "Client". Theoretically this object can have any number of various attributes: several email addresses, many phone numbers, addresses etc. One of these can be used by Sales Department of one company however will be totally ignored by another organization. To enter all possible attributes into the object that may (may not) be used by the end users is wasteful and unjustified.

In this case if would be better to allow the user (or administrator) of the system to create the attributes that are necessary for sales managers in a specific organization. For example, administrator can create an attribute "work phone" if this field is actually required or "home address" and etc. Further these fields can be used in the application for example for filtering and searching of data.

Brief Description

While implementing the Enterra CRM project the customer set forth the task to support Custom Filed in the application as: "System administrator should be able to create/delete custom fields without the need to restart the system".

Hibernate 3.0 framework was used to develop the back end. This factor (technological constraint) was the key that was taken into consideration to implement the requirement.

Implementation

In this chapter we will provide key moments of the implementation considering specifics of using Hibernate as the framework.

Environment

The implementation demo variant from below was developed using:

  1. JDK 1.5
  2. Hibernate 3.2.0 framework;
  3. MySQL 4.1.

Limitations

To make it simpler we will not be using Hibernate EntityManager, Hibernate Annotations. Mapping of persistent objects will be built on xml file mapping. Moreover, it is worth mentioning that when using Hibernate Annotations the sample implementation demo will not be functional since is it based on management by xml file mapping.

Task definition

We will have to implement a mechanism allowing for creating/deleting custom fields in real time avoiding the application restart, add a value into it and make sure the value is present in the application database. Besides we will have to make sure that the custom field can be used in queries.

Solution

Domain Model

We will first need a business entity class which we will experiment with. Let is be Contact class. There will be two persistent fields: id and name.

However besides these permanent and unchangeable fields the class should be some sort of construction to store values of custom fields. Map would be an ideal construction for this.

Let's create a base class for all business entities supporting custom fields - CustomizableEntity, that contains Map CustomProperties to work with custom fields:

package com.enterra.customfieldsdemo.domain;

import java.util.Map;
import java.util.HashMap;

public abstract class CustomizableEntity {

private Map customProperties;

public Map getCustomProperties() {
if (customProperties == null)
customProperties = new HashMap();
return customProperties;
}
public void setCustomProperties(Map customProperties) {
this.customProperties = customProperties;
}

public Object getValueOfCustomField(String name) {
return getCustomProperties().get(name);
}

public void setValueOfCustomField(String name, Object value) {
getCustomProperties().put(name, value);
}

}

Listing 1 - base class CustomizableEntity

Inherit our class Contact from this base class:

package com.enterra.customfieldsdemo.domain;

import com.enterra.customfieldsdemo.domain.CustomizableEntity;

public class Contact extends CustomizableEntity {

private int id;
private String name;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

}

Listing 2 - Class Contact inherited from CustomizableEntity.

We should not forget about the mapping file for this class:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping auto-import="true" default-access="property" default-cascade="none" default-lazy="true">

<class abstract="false" name="com.enterra.customfieldsdemo.domain.Contact" table="tbl_contact">

<id column="fld_id" name="id">
<generator class="native"/>
</id>

<property name="name" column="fld_name" type="string"/>
<dynamic-component insert="true" name="customProperties" optimistic-lock="true" unique="false" update="true">
</dynamic-component>
</class>
</hibernate-mapping>

Listing 3 - Mapping Class Contact.

Please note that properties id and name are done as all ordinary properties, however for customProperties we use a tag <dynamic-component>. Documentation on Hibernate 3.2.0GA says that the point of a dynamic-component is:

"The semantics of a <dynamic-component> mapping are identical to <component>. 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. Even better, you can access (and change) Hibernate's configuration-time metamodel via the Configuration object."

Based on this regulation from Hibernate documentation we will be building this function mechanism.

HibernateUtil and hibernate.cfg.xml

After we are defined with the domain model of our application we have to create necessary conditions for Hibernate framework functioning. For this we have to create a configuration file hibernate.cfg.xml and class to work with the core Hibernate functions.

<?xml version='1.0' encoding='utf-8'?>

<!DOCTYPE hibernate-configuration

PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"

"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<session-factory>

<property name="show_sql">true</property>
<property name="dialect">
org.hibernate.dialect.MySQLDialect</property>
<property name="cglib.use_reflection_optimizer">true</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/custom_fields_test</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.c3p0.max_size">50</property>
<property name="hibernate.c3p0.min_size">0</property>
<property name="hibernate.c3p0.timeout">120</property>
<property name="hibernate.c3p0.max_statements">100</property>
<property name="hibernate.c3p0.idle_test_period">0</property>
<property name="hibernate.c3p0.acquire_increment">2</property>
<property name="hibernate.jdbc.batch_size">20</property>
<property name="hibernate.hbm2ddl.auto">update</property>
</session-factory>
</hibernate-configuration>

Listing 4 - Hibernate configuration file.

The file hibernate.cfg.xml does not contain anything noticeable except for this string:

<property name="hibernate.hbm2ddl.auto">update</property>

Listing 5 - using auto-update.

Later we will explain in details on its purpose and tell more how we can go without it. There are several ways to implement class HibernateUtil. Our implementation will differ a bit from well known due to changes into Hibernate configuration.

package com.enterra.customfieldsdemo;

import org.hibernate.*;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.tool.hbm2ddl.SchemaUpdate;
import org.hibernate.cfg.Configuration;
import com.enterra.customfieldsdemo.domain.Contact;

public class HibernateUtil {

private static HibernateUtil instance;
private Configuration configuration;
private SessionFactory sessionFactory;
private Session session;

public synchronized static HibernateUtil getInstance() {
if (instance == null) {
instance = new HibernateUtil();
}
return instance;
}

private synchronized SessionFactory getSessionFactory() {
if (sessionFactory == null) {
sessionFactory = getConfiguration().buildSessionFactory();
}
return sessionFactory;
}

public synchronized Session getCurrentSession() {
if (session == null) {
session = getSessionFactory().openSession();
session.setFlushMode(FlushMode.COMMIT);
System.out.println("session opened.");
}
return session;
}

private synchronized Configuration getConfiguration() {
if (configuration == null) {
System.out.print("configuring Hibernate ... ");
try {
configuration = new Configuration().configure();
configuration.addClass(Contact.class);
System.out.println("ok");
} catch (HibernateException e) {
System.out.println("failure");
e.printStackTrace();
}
}
return configuration;
}
public void reset() {
Session session = getCurrentSession();
if (session != null) {
session.flush();
if (session.isOpen()) {
System.out.print("closing session ... ");
session.close();
System.out.println("ok");
}
}
SessionFactory sf = getSessionFactory();
if (sf != null) {
System.out.print("closing session factory ... ");
sf.close();
System.out.println("ok");
}
this.configuration = null;
this.sessionFactory = null;
this.session = null;
}

public PersistentClass getClassMapping(Class entityClass){
return getConfiguration().getClassMapping(entityClass.getName());
}
}

Listing 6 - HibernateUtils class.

Alongside with usual methods like getCurrentSession(), getConfiguration(), which is necessary for regular work of the application based on Hibernate, we also have implemented such methods as: reset() and getClassMapping(Class entityClass). In the method getConfiguration(), we configure Hibernate and add class Contact into the configuration.

Method reset() has been used to close all used by Hibernate resources and clearing all of its settings:

public void reset() {
Session session = getCurrentSession();
if (session != null) {
session.flush();
if (session.isOpen()) {
System.out.print("closing session ... ");
session.close();
System.out.println("ok");
}
}
SessionFactory sf = getSessionFactory();
if (sf != null) {
System.out.print("closing session factory ... "); sf.close();
System.out.println("ok");
}
this.configuration = null;
this.sessionFactory = null;
this.session = null;
}

Listing 7 - method reset()

Method getClassMapping(Class entityClass) returns object PersistentClass, that contains full information on mapping the related entity. In particular the manipulations with the object PersistentClass allow modifying the set of attributes of the entity class in the run-time.

public PersistentClass getClassMapping(Class entityClass){
return
getConfiguration().getClassMapping(entityClass.getName());
}

Listing 8 - method getClassMapping(Class entityClass).

Manipulations with mapping

Once we have the business entity class (Contact) available and the main class to interact with Hibernate we can start working. We can create and save samples of the Contact class. We can even place some data into our Map customProperties, however we should be aware that this data (stored in Map customProperties) are not saved to the DB.

To have the data saved we should provide for the mechanism of creating custom fields in our classs and make it the way Hibernate knows how to work with them.

To provide for class mapping manipulation we should create some interface. Let's call it CustomizableEntityManager. Its name should reflect the purpose of the interface managing a business entity, its contents and attributes:

package com.enterra.customfieldsdemo;

import org.hibernate.mapping.Component;

public interface CustomizableEntityManager {
public static String CUSTOM_COMPONENT_NAME = "customProperties";

void addCustomField(String name);

void removeCustomField(String name);

Component getCustomProperties();

Class getEntityClass();
}

Listing 9 - Interface CustomizableEntityManager

The main methods for the interface are: void addCustomField(String name) and void removeCustomField(String name). These should created and remove our custom field in the mapping of the corresponding class.

Below is the way to implement the interface:

package com.enterra.customfieldsdemo;

import org.hibernate.cfg.Configuration;
import org.hibernate.mapping.*;
import java.util.Iterator;

public class CustomizableEntityManagerImpl implements CustomizableEntityManager {
private Component customProperties;
private Class entityClass;

public CustomizableEntityManagerImpl(Class entityClass) {
this.entityClass = entityClass;
}

public Class getEntityClass() {
return entityClass;
}

public Component getCustomProperties() {
if (customProperties == null) {
Property property = getPersistentClass().getProperty(CUSTOM_COMPONENT_NAME);
customProperties = (Component) property.getValue();
}
return customProperties;
}

public void addCustomField(String name) {
SimpleValue simpleValue = new SimpleValue();
simpleValue.addColumn(new Column("fld_" + name));
simpleValue.setTypeName(String.class.getName());

PersistentClass persistentClass = getPersistentClass();
simpleValue.setTable(persistentClass.getTable());

Property property = new Property();
property.setName(name);
property.setValue(simpleValue);
getCustomProperties().addProperty(property);

updateMapping();
}

public void removeCustomField(String name) {
Iterator propertyIterator = customProperties.getPropertyIterator();

while (propertyIterator.hasNext()) {
Property property = (Property) propertyIterator.next();
if (property.getName().equals(name)) {
propertyIterator.remove();
updateMapping();
return;
}
}
}

private synchronized void updateMapping() {
MappingManager.updateClassMapping(this);
HibernateUtil.getInstance().reset();
// updateDBSchema();
}

private PersistentClass getPersistentClass() {
return HibernateUtil.getInstance().getClassMapping(this.entityClass);
}
}

Listing 10 - implementing interface CustomizableEntityManager

First of all we should point out that when creating class CustomizableEntityManager we specify the business entity class the manager will operate. This class is passed as a parameter to designer CustomizableEntityManager:

private Class entityClass;

public CustomizableEntityManagerImpl(Class entityClass) {
this.entityClass = entityClass;
}

public Class getEntityClass() {
return entityClass;
}

Listing 11 - class designer CustomizableEntityManagerImpl

Now we should get more interested in how to implement method void addCustomField(String name):

public void addCustomField(String name) {
SimpleValue simpleValue = new SimpleValue();
simpleValue.addColumn(new Column("fld_" + name));
simpleValue.setTypeName(String.class.getName());

PersistentClass persistentClass = getPersistentClass();
simpleValue.setTable(persistentClass.getTable());

Property property = new Property();
property.setName(name);
property.setValue(simpleValue);
getCustomProperties().addProperty(property);

updateMapping();
}

Listing 12 - creating custom field.

As we can see from the implementation, Hibernate offers more options in working with properties of persistent objects and their representation in the DB. As per the essence of the method:

1) We create class SimpleValue that allow us to denote how the value of this custom field will be stored in the DB in which field and table of the DB:

SimpleValue simpleValue = new SimpleValue();
simpleValue.addColumn(new Column("fld_" + name));
simpleValue.setTypeName(String.class.getName());

PersistentClass persistentClass = getPersistentClass();
simpleValue.setTable(persistentClass.getTable());

Listing 13 - creating new column of the table.

2) We create a property of the persistent object and add a dynamic component into it (!), that we have planned to be used for this purpose:

Property property = new Property()
property.setName(name)
property.setValue(simpleValue)
getCustomProperties().addProperty(property)

Listing 14 - creating object property.

3) And finally we should make our application perform certain changes in the xml files and update the Hibernate configuration. This can be done via method updateMapping();

It is necessary to clarify the purpose of another two get-methods which have been used in the code above. The first method is getCustomProperties():

public Component getCustomProperties() {
if (customProperties == null) {
Property property = getPersistentClass().getProperty(CUSTOM_COMPONENT_NAME);
customProperties = (Component) property.getValue();
}
return customProperties;
}

Listing 15 - getting CustomProperties as Component.

This method finds and returns object Component corresponding to the tag in the mapping of our business entity.

The second method is updateMapping():

private synchronized void updateMapping() {
MappingManager.updateClassMapping(this);
HibernateUtil.getInstance().reset();
// updateDBSchema();
}

Listing 16 - method updateMapping().

The method is in charge for storing the updated mapping of the persistent class and updates the configuration status of Hibernate to make further changes that we make valid when the changes take effect.

By the way we should get back to the string:

<property name="hibernate.hbm2ddl.auto">update</property>

of the Hibernate configuration. If this string was missing we would have to launch executing updates of the DB schema using hibernate utilities. However using the setting allows us to avoid this.

Saving mapping

Modifications to mapping made in run-time do not save by themselves into the corresponding xml mapping file and to make the changes to get activated at next launch of the application we need to manually save changes to the corresponding mapping file.

To do this we will be using class MappingManager the main purpose of which is to save mapping of the designated business entity to its xml mapping file:

package com.enterra.customfieldsdemo;

import com.enterra.customfieldsdemo.domain.CustomizableEntity;
import org.hibernate.Session;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Property;
import org.hibernate.type.Type;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.util.Iterator;

public class MappingManager {
public static void updateClassMapping(CustomizableEntityManager entityManager) {
try {
Session session = HibernateUtil.getInstance().getCurrentSession();
Class<? extends CustomizableEntity> entityClass = entityManager.getEntityClass();
String file = entityClass.getResource(entityClass.getSimpleName() + ".hbm.xml").getPath();

Document document = XMLUtil.loadDocument(file);
NodeList componentTags = document.getElementsByTagName("dynamic-component");
Node node = componentTags.item(0);
XMLUtil.removeChildren(node);

Iterator propertyIterator = entityManager.getCustomProperties().getPropertyIterator();
while (propertyIterator.hasNext()) {
Property property = (Property) propertyIterator.next();
Element element = createPropertyElement(document, property);
node.appendChild(element);
}

XMLUtil.saveDocument(document, file);
} catch (Exception e) {
e.printStackTrace();
}
}

private static Element createPropertyElement(Document document, Property property) {
Element element = document.createElement("property");
Type type = property.getType();

element.setAttribute("name", property.getName());
element.setAttribute("column", ((Column)
property.getColumnIterator().next()).getName());
element.setAttribute("type",
type.getReturnedClass().getName());
element.setAttribute("not-null", String.valueOf(false));

return element;
}
}

Listing 17 - the utility to update mapping of the persistent class.

The class literally performs the following:

  1. Defines a location and loads xml mapping for the designated business entity into the DOM Document object for further manipulations with it;
  2. Finds the element of this document <dynamic-component>. In particular here we store the custom fields and its contents we change;
  3. Delete (!) all embedded elements from this element;
  4. For any persistent property contained in our component that is in charge for the custom fields storage, we create a specific document element and define attributes for the element from the corresponding property;
  5. Save this newly created mapping file.

When manipulating XML we use (as we can see from the code) class XMLUtil, that in general can be implemented in any way though it should correctly load and save the xml file.

Our implementation is given at the listing below:

package com.enterra.customfieldsdemo;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.dom.DOMSource;
import java.io.IOException;
import java.io.FileOutputStream;

public class XMLUtil {
public static void removeChildren(Node node) {
NodeList childNodes = node.getChildNodes();
int length = childNodes.getLength();
for (int i = length - 1; i > -1; i--)
node.removeChild(childNodes.item(i));
}

public static Document loadDocument(String file)
throws ParserConfigurationException, SAXException, IOException {

DocumentBuilderFactory factory =DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(file);
}

public static void saveDocument(Document dom, String file)
throws TransformerException, IOException {

TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();

transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, dom.getDoctype().getPublicId());
transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, dom.getDoctype().getSystemId());

DOMSource source = new DOMSource(dom);
StreamResult result = new StreamResult();

FileOutputStream outputStream = new FileOutputStream(file);
result.setOutputStream(outputStream);
transformer.transform(source, result);

outputStream.flush();
outputStream.close();
}
}

Listing 18 - XML manipulation utility.

Testing

Now when we have all the necessary working code at place we can write tests and see how everything works. The first test will create the custom field "email", create and save the object of the class Contact and define it the "email" property.

First let's take a look at the data table tbl_contact. It contains two fields: fld_id, fld_name. The code is provided below:

package com.enterra.customfieldsdemo.test;

import com.enterra.customfieldsdemo.HibernateUtil;
import com.enterra.customfieldsdemo.CustomizableEntityManager;
import com.enterra.customfieldsdemo.CustomizableEntityManagerImpl;
import com.enterra.customfieldsdemo.domain.Contact;
import org.hibernate.Session;
import org.hibernate.Transaction;
import java.io.Serializable;

public class TestCustomEntities {
private static final String TEST_FIELD_NAME = "email";
private static final String TEST_VALUE = "test@test.com";

public static void main(String[] args) {
HibernateUtil.getInstance().getCurrentSession();

CustomizableEntityManager contactEntityManager = new
CustomizableEntityManagerImpl(Contact.class);

contactEntityManager.addCustomField(TEST_FIELD_NAME);

Session session = HibernateUtil.getInstance().getCurrentSession();

Transaction tx = session.beginTransaction();
try {

Contact contact = new Contact();
contact.setName("Contact Name 1");
contact.setValueOfCustomField(TEST_FIELD_NAME, TEST_VALUE);
Serializable id = session.save(contact); tx.commit();

contact = (Contact) session.get(Contact.class, id);
Object value = contact.getValueOfCustomField(TEST_FIELD_NAME);
System.out.println("value = " + value);

} catch (Exception e) {
tx.rollback();
System.out.println("e = " + e);
}
}
}

Listing 19 - test to creation of the custom field.

This method is responsible for the following:

  1. Creation of CustomizableEntityManager for class Contact;
  2. Creation of new custom field named "email";
  3. Further in transaction we create a new contact and give the custom field value "test@test.com";
  4. Save Contact;
  5. Get the value of the cusom field "email"

As the result of the execution we can see the following:

configuring Hibernate ... ok
session opened.
closing session ... ok
closing session factory ... ok
configuring Hibernate ... ok
session opened.
Hibernate: insert into tbl_contact (fld_name, fld_email) values (?, ?)
value = test@test.com

Listing 20 - test result.

In the database the following record can be seen:

+--------+---------------------+----------------------+
| fld_id | fld_name | fld_email
|
+--------+---------------------+----------------------+
| 1 | Contact Name 1 | test@test.com |
+--------+---------------------+----------------------+

Listing 21 - DB result.

As we can see the new field has been created in the run-time and its value successfully saved.

The second test creates the query to the DB using the newly created field:

package com.enterra.customfieldsdemo.test;

import com.enterra.customfieldsdemo.HibernateUtil;
import com.enterra.customfieldsdemo.CustomizableEntityManager;
import com.enterra.customfieldsdemo.domain.Contact;
import org.hibernate.Session;
import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import java.util.List;

public class TestQueryCustomFields {
public static void main(String[] args) {
Session session = HibernateUtil.getInstance().getCurrentSession();
Criteria criteria = session.createCriteria(Contact.class);
criteria.add(Restrictions.eq(CustomizableEntityManager.CUSTOM_COMPONENT_NAME + ".email", "test@test.com"));
List list = criteria.list();
System.out.println("list.size() = " + list.size());
}
}

Listing 22 - Query test by the custom field.

Execution result:
configuring Hibernate ... ok
session opened.
Hibernate: select this_.fld_id as fld1_0_0_, this_.fld_name as fld2_0_0_,
this_.fld_email as fld3_0_0_ from tbl_contact this_ where this_.fld_email=?
list.size() = 1

Listing 23 - Query execution result.

As we can see the custom field that has been created using our technology can easily participate in queries to the DB.

Further Improvements

Obviously the implementation we spoke above is rather primitive. It does not reflect all the variety of options that pop up at actual implementation of this functionality. Yet, it shows the general working mechanism of the solution on the technological platform suggested.

It is also obvious that the requirement can be implemented using other mechanisms (e.g. code generation) which may be considered in other articles.

This implementation supports only String type as Custom Fields however in the real application built with this approach (Enterra CRM) a full support has been implemented as for all primitive types as well as object types (links to business objects) and collection fields.

To support custom fields in the part of the user interface the system of meta-descriptors for the custom fields has been implemented which used the user interface generation system. However the mechanism of the generator is a theme for a separate article.

Conclusion

As the result of the Enterra CRM team has created, approved and applied in practice the architecture of open object model based on ORM platform Hibernate which allowed for satisfying of the customer requests referring to the application settings under actual need of the end users in the run-time with no necessity to make changes to the source code of the application.

Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Tell us what you think

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Worse Than Failure by Greg Helton

I've seen this anti-pattern before.

Is Listing 3 broken? by Kondo Hiroki

Hi.
I find a doubtful point in Listing 3.
Where does <dynamic-component> tag start?
This is an essencial point in this article.
So I recommend as soon as even possible fix it.
I read this article using Firefox 2.

Re: Worse Than Failure by Jeppe Cramon

If you have experience and a better pattern then please share instead of just proclaiming that the proposed solution is an antipattern ;)

Re: Is Listing 3 broken? by Diana Baciu

Hi Hiroki
This has been now fixed

Existing content by Benny Michielsen

What happens with any existing content in the table that is being altered?

Solution with Annotations? by Tom Bostelmann

This solutions looks great for use in a pluggable architecture. Do you have a solution that utilizes annotations?

Re: Solution with Annotations? by Tom Bostelmann

Whoops. My apologies. I should have read the article more closely :P

When using oracle iam getting exception by Arif Mohd

Hi,
I tried the example, but getting the following exception.Also the new column is not created in Database.Every time i run a new row is inserted in DB(connection to DB is perfect)but actual functionality i.e, new column is not inserted.Iam using Oracle.

configuring Hibernate ... ok
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
log4j:WARN Please initialize the log4j system properly.
session opened.
java.net.ConnectException: Connection refused: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(Unknown Source)
at java.net.PlainSocketImpl.connectToAddress(Unknown Source)
at java.net.PlainSocketImpl.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at sun.net.NetworkClient.doConnect(Unknown Source)
at sun.net.www.http.HttpClient.openServer(Unknown Source)
at sun.net.www.http.HttpClient.openServer(Unknown Source)
at sun.net.www.http.HttpClient.<init>(Unknown Source)
at sun.net.www.http.HttpClient.New(Unknown Source)
at sun.net.www.http.HttpClient.New(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.connect(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
at org.apache.xerces.impl.XMLEntityManager.setupCurrentEntity(Unknown Source)
at org.apache.xerces.impl.XMLEntityManager.startEntity(Unknown Source)
at org.apache.xerces.impl.XMLEntityManager.startDTDEntity(Unknown Source)
at org.apache.xerces.impl.XMLDTDScannerImpl.setInputSource(Unknown Source)
at org.apache.xerces.impl.XMLDocumentScannerImpl$DTDDispatcher.dispatch(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)closing session ... ok
closing session factory ... ok
configuring Hibernate ...
at org.apache.xerces.parsers.DOMParser.parse(Unknown Source)
at org.apache.xerces.jaxp.DocumentBuilderImpl.parse(Unknown Source)
at javax.xml.parsers.DocumentBuilder.parse(Unknown Source)
at com.enterra.customfieldsdemo.XMLUtil.loadDocument(XMLUtil.java:40)
at com.enterra.customfieldsdemo.MappingManager.updateClassMapping(MappingManager.java:23)
at com.enterra.customfieldsdemo.CustomizableEntityManagerImpl.updateMapping(CustomizableEntityManagerImpl.java:57)
at com.enterra.customfieldsdemo.CustomizableEntityManagerImpl.addCustomField(CustomizableEntityManagerImpl.java:40)
at com.enterra.customfieldsdemo.test.TestCustomEntities.main(TestCustomEntities.java:21)
ok
session opened.
Hibernate: select hibernate_sequence.nextval from dual
Hibernate: insert into tbl_contact (fld_name, fld_id) values (?, ?)
value = test@test.com

</init>

Re: When using oracle iam getting exception by Ken Nguyen

Actually you can build this extension into your data model without going through the actual adding of new columns in your existing table by separating meta data and actual data store. It may make your manipulation on service layer slightly more complicated but it gives you a lot of flexibility

Can't remove the custom fields,Why? by mata nakamula

I have done the source code example,Now I can add custom fields successfully.
But I can't remove the added custom fields, the custom fields are still in the table of database.
Why? How can I remove the added custom fields in both hibernate mapping and table of database?

Maybe that it's not a flexiable approach to dynamic field. by zhanwang lin

Why not make it by database design, eg. to make the flexiable table relations.

Re: Maybe that it's not a flexiable approach to dynamic field. by Tom Cooper

Because you don't want the overhead of the joins that would create, or the obscurity in the data when you have pre-allocated flex fields. The idea is to have post-build additions to the schema and code that doesn't have that performance or coherence penalty, but are first-class columns.

Nice - but why edit the hbm xml? by Tom Cooper

You don't really need to update the XML again to do this. In our case there are dozens of hbm files included, and not each on the file system, so we cannot really rewrite these. Instead you could simply use the API as you do to update the configuration, and then just go ahead with that. You would need some extra "addendum" information from somewhere holding these additions so that each time you start up you can apply these edits before you create a session factory.

I tried that, creating more than one dynamic property, and had some trouble if I didn't add the properties to the component in a specific order, which surprised me, and I'm still figuring out. (Using Hibernate core 3.6.3) Otherwise its great.

Re: Nice - but why edit the hbm xml? by Tom Cooper

Just to clarify -- I didn't need to use the hbm2ddl auto feature, as my database extension was handled by a different process. If I needed to have Hibernate do this DDL update, then I can understand why you would need to update the XML and load that for Hibernate to handle it. That's just not my requirement, so I don't have to update the XML.

Re: Nice - but why edit the hbm xml? by Tom Cooper

Found my problem: I also need to add my column to the table, or the column won't get a unique number, and aliases won't be distinct. Did that and all is well.

is there a jpa version by mohammed rafeeq

This article is useful, but is there a more modern approached JPA version of this. Some leads by the aughor or by any viewers will be useful. My need is the same but very few use hbm files today as the industry has shifted to annotations/JPA and dont use hibernate directly

With Annotations by Vincenz Mössenböck

I know it says that this demo will not work with annotations, but does anyone has a idea how we could solve it with annotations? I have been looking for a week now but I just can t find a way.

Re: is there a jpa version by Petr Jeřábek

can you provide some info about JPA version of this feature?

thanks!

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

18 Discuss

Educational Content

General Feedback
Bugs
Advertising
Editorial
InfoQ.com and all content copyright © 2006-2014 C4Media Inc. InfoQ.com hosted at Contegix, the best ISP we've ever worked with.
Privacy policy
BT