About Me

My photo
Bangalore, Karnataka, India

Thursday, May 13, 2010

Peristing java.util.Properties :hibernate

Hibernate gives you option to store only map by default. If you need to store java.util.Properties object to database writing a UserType around PersistenceMap will not help unless your domain object has a setter method which accepts Map as argument. The solution I took is to write a tupilizer like below.

-----------------------------------------------------
import java.lang.reflect.Method;
import java.util.Properties;
import java.util.Set;
import org.hibernate.HibernateException;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.mapping.Map;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.property.Setter;
import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.tuple.entity.PojoEntityTuplizer;
/**
* Class to convert the PersistentMap to Properties in setter.
* Implementation is backed by PojoEntityTuplizer and only if
* property type is Properties value to set is Map this will convert
* and return the value.
*
* @author krishna.arani
*/
public class PropertyTuplizer extends PojoEntityTuplizer {
public PropertyTuplizer(EntityMetamodel entityMetamodel,
PersistentClass mappedEntity) {
super(entityMetamodel, mappedEntity);
}
@Override
protected Setter buildPropertySetter(Property mappedProperty,
PersistentClass mappedEntity) {
final Setter setter = super.buildPropertySetter(mappedProperty,
mappedEntity);
if (!(mappedProperty.getValue() instanceof Map)) {
return setter;
}
return new Setter() {
private static final long serialVersionUID = 1L;
@Override
public Method getMethod() {
return setter.getMethod();
}
@Override
public String getMethodName() {
return setter.getMethodName();
}
@Override
public void set(Object target, Object value,
SessionFactoryImplementor sessionFactoryImplementor)
throws HibernateException {
Object convertedValue = value;
if (value instanceof java.util.Map) {
Class parameterType[] = getMethod().getParameterTypes();
// if required to convert from map
if (Properties.class.isAssignableFrom(parameterType[0])
&& !(value instanceof Properties)) {
Properties props = new Properties();
java.util.Map castedValue = (java.util.Map) value;
Set entries = ((java.util.Map) value)
.entrySet();
for (java.util.Map.Entry entry : entries) {
props.put(entry.getKey(), entry.getValue());
}
convertedValue = props;
}
}
setter.set(target, convertedValue, sessionFactoryImplementor);
}
};
}
}

------------------------------------
The idea is to override the setter method so that it converts map to Properties before invoking the set on the domain object.

Here is the sample of class and hbm Site with connection parameters as java.util.Properties.




Class Site{
String SiteId;
java.util.Properties connectionParameters;

public String getSiteId(){
return siteId;
}

public void setSiteId(String siteId){
this.siteId = siteId;
}

public java.util.Properties getConnectionParameters(){
return connectionPerameters;
}

public void setConnectionParameters(java.util.Properties props){
this. connectionPerameters = props;
}

}

-----------------------
hbm:


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >

<hibernate-mapping>

<class name="Site" >

<tuplizer class="PropertyTuplizer"
entity-mode="pojo" />

<id name="siteId" type="java.lang.String" column="siteid" />

<map name="connectionParameters">
<key column="siteid" />
<map-key type="java.lang.String" column="propertyName" />
<element type="java.lang.String" column="propertyValue" />
</map>

</class>

</hibernate-mapping>