Vous êtes sur la page 1sur 17

InfoQ: Using Hibernate to Support Custom Domain Object Fields Page 1 of 17

Article

Using Hibernate to Support Custom Domain


Object Fields
Posted by Enterra Inc. on Nov 05, 2007 11:06 AM
Community Java Topics Data Access Tags Hibernate

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:

RelatedVendorContent

Hibernate without Database Bottlenecks

Webcast: Applying lean thinking to the governance of software development

Gaining Control of Changing Requirements Webcast

The Agile Business Analyst: Skills and Techniques needed for Agile

Continuous Application Performance eKit - Articles, Discussion Panel, Whitepapers, Demos

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".

http://www.infoq.com/articles/hibernate-custom-fields 11/30/2008
InfoQ: Using Hibernate to Support Custom Domain Object Fields Page 2 of 17

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:

InfoQ.com and all content copyright © 2006-2009 C4Media Inc. InfoQ.com hosted at Contegix, the best
ISP we've ever worked with. Privacy policy

http://www.infoq.com/articles/hibernate-custom-fields 11/30/2008
InfoQ: Using Hibernate to Support Custom Domain Object Fields Page 3 of 17

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-
<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>

http://www.infoq.com/articles/hibernate-custom-fields 11/30/2008
InfoQ: Using Hibernate to Support Custom Domain Object Fields Page 4 of 17

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.

http://www.infoq.com/articles/hibernate-custom-fields 11/30/2008
InfoQ: Using Hibernate to Support Custom Domain Object Fields Page 5 of 17

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){

http://www.infoq.com/articles/hibernate-custom-fields 11/30/2008
InfoQ: Using Hibernate to Support Custom Domain Object Fields Page 6 of 17

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:

http://www.infoq.com/articles/hibernate-custom-fields 11/30/2008
InfoQ: Using Hibernate to Support Custom Domain Object Fields Page 7 of 17

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:

http://www.infoq.com/articles/hibernate-custom-fields 11/30/2008
InfoQ: Using Hibernate to Support Custom Domain Object Fields Page 8 of 17

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:

http://www.infoq.com/articles/hibernate-custom-fields 11/30/2008
InfoQ: Using Hibernate to Support Custom Domain Object Fields Page 9 of 17

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():

http://www.infoq.com/articles/hibernate-custom-fields 11/30/2008
InfoQ: Using Hibernate to Support Custom Domain Object Fields Page 10 of 17

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:

http://www.infoq.com/articles/hibernate-custom-fields 11/30/2008
InfoQ: Using Hibernate to Support Custom Domain Object Fields Page 11 of 17

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:

http://www.infoq.com/articles/hibernate-custom-fields 11/30/2008
InfoQ: Using Hibernate to Support Custom Domain Object Fields Page 12 of 17

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:

http://www.infoq.com/articles/hibernate-custom-fields 11/30/2008
InfoQ: Using Hibernate to Support Custom Domain Object Fields Page 13 of 17

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:

http://www.infoq.com/articles/hibernate-custom-fields 11/30/2008
InfoQ: Using Hibernate to Support Custom Domain Object Fields Page 14 of 17

+--------+---------------------+----------------------+
| 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

http://www.infoq.com/articles/hibernate-custom-fields 11/30/2008
InfoQ: Using Hibernate to Support Custom Domain Object Fields Page 15 of 17

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.

Bookmark digg+, reddit+, del.icio.us+, dzone+, facebook+

10 comments Reply

Worse Than Failure by Greg Helton Posted Nov 6, 2007 1:39 PM


Re: Worse Than Failure by Jeppe Cramon Posted Nov 7, 2007 2:26 AM
Is Listing 3 broken? by Hiroki Kondo Posted Nov 6, 2007 1:45 PM
Re: Is Listing 3 broken? by Diana Plesa Posted Nov 7, 2007 5:17 AM
Existing content by Benny Michielsen Posted Nov 12, 2007 4:48 AM
Solution with Annotations? by Tom Bostelmann Posted Dec 29, 2007 1:35 PM
Re: Solution with Annotations? by Tom Bostelmann Posted Dec 29, 2007 9:50 PM
When using oracle iam getting exception by Arif Mohd Posted Feb 19, 2008 6:32 AM
Re: When using oracle iam getting exception by Ken Nguyen Posted Mar 13, 2008 9:29 AM
Can't remove the custom fields,Why? by nakamula mata Posted Sep 2, 2008 1:59 AM

Sort by date descending

Worse Than Failure

Nov 6, 2007 1:39 PM by Greg Helton

I've seen this anti-pattern before.


Reply

Is Listing 3 broken?

Nov 6, 2007 1:45 PM by Hiroki Kondo

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.
Reply

Re: Worse Than Failure

Nov 7, 2007 2:26 AM 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 ;)
Reply

Re: Is Listing 3 broken?

Nov 7, 2007 5:17 AM by Diana Plesa

http://www.infoq.com/articles/hibernate-custom-fields 11/30/2008
InfoQ: Using Hibernate to Support Custom Domain Object Fields Page 16 of 17

Hi Hiroki
This has been now fixed
Reply

Existing content

Nov 12, 2007 4:48 AM by Benny Michielsen

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

Solution with Annotations?

Dec 29, 2007 1:35 PM by Tom Bostelmann

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

Re: Solution with Annotations?

Dec 29, 2007 9:50 PM by Tom Bostelmann

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


Reply

When using oracle iam getting exception

Feb 19, 2008 6:32 AM 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)

http://www.infoq.com/articles/hibernate-custom-fields 11/30/2008
InfoQ: Using Hibernate to Support Custom Domain Object Fields Page 17 of 17

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>
Reply

Re: When using oracle iam getting exception

Mar 13, 2008 9:29 AM 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
Reply

Can't remove the custom fields,Why?

Sep 2, 2008 1:59 AM by nakamula mata

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?
Reply

http://www.infoq.com/articles/hibernate-custom-fields 11/30/2008

Vous aimerez peut-être aussi