On our project, we define the hibernate mapping in the Javadoc and use XDoclet to turn that into HBM mapping files.
For a given property we might have something like the following:
Code:
/**
* Australian Registered Business Number (for foreign owned enitities)
*
* @hibernate:property column="t_arbn" not-null="true"
* @hibernate:meta name="clas.optional" value ="true"
* @hibernate:meta name="clas.minimum" value="11"
* @hibernate:meta name="clas.maximum" value="11"
* @hibernate:meta name="clas.expression" value="[0-9]{3} [0-9]{3} [0-9]{3}"
*/
public java.lang.String getArbn() {
return arbn;
}
The back-end framework accesses the validation information for a property by asking Hibernate for it (HB loads the metadata up when it does all the other pre-processing during SessionFactory build time) with code like this:
Code:
public Property getProperty(PersistentClass pClass, String propertyName)
throws PersistenceLayerException {
try {
Property property = pClass.getProperty(propertyName);
if( property == null ){
throw ExceptionUtil.createPersistenceLayerException(log, res, "metadata.get_property.no_property", "the class `{0}` does not contain a mapped property named `{1}`", new Object[]{pClass.getName(), propertyName});
}
return property;
}
catch( HibernateException he ){
throw ExceptionUtil.createPersistenceLayerException(log, res, "metadata.get_property.hb_error", "while getting the property", he);
}
}
We have a strict separation of layers in our app, so we define a "grammar" for the "property specification" that the front-end uses when asking for validation information from the back end (this actually doesn't happen often, because we do most of our validation on the back-end). The property specification looks something like "Entity.arbn".
The code to find the right class and then call the getProperty() method above is:
Code:
/**
* [TODO: unqualified classnames should probably be cached in a map or
* something, rather than iterating across all the mappings each time]
*/
public PersistentClass getUnqualifiedClassMapping(String uqClassname)
throws PersistenceLayerException {
Validate.notNull(uqClassname, "param uqClassname may not be null");
try {
PersistentClass result = null;
for( Iterator i=hibernateConfig.getClassMappings(); i.hasNext(); ){
PersistentClass iClass = (PersistentClass) i.next();
String iUQClassname =
StringTools.sliceRemainder(iClass.getName(), ".");
if( uqClassname.equals(iUQClassname) ){
if( result != null ){
throw handleError("the class `"+iClass.getName()+"` has the same unqualified classname as `"+result.getName()+"`, this should not be possible because it is now impossible to tell which class you mean when you say `"+uqClassname+"`", null);
}
result = iClass;
}
}
if( result == null ){
throw handleError("the unqualified classname `"+uqClassname+"` could not be found in the domain model", null);
}
return result;
}
catch( Exception e ){
throw handleError("while getting the class mappings", e);
}
}
...
public String getSingleColMapping(String attributeSpec)
throws PersistenceLayerException {
PersistentClass mappedClass =
getUnqualifiedClassMapping(
DomainGrammar.getClassnameFromPropertySpec(attributeSpec) );
String propertyName =
DomainGrammar.getPropertyNameFromPropertySpec(attributeSpec);
Iterator i;
if (propertyName.equals("id")) {
i = mappedClass.getIdentifier().getColumnIterator();
} else {
i = getProperty(mappedClass, propertyName).getColumnIterator();
}
String value = ((net.sf.hibernate.mapping.Column)i.next()).getName();
if (i.hasNext()) {
throw ExceptionUtil.createPersistenceLayerException(log, res, "getSingleColMapping", "requested a single col, but has multiple cols");
}
return value;
}
The only real wrinkle to this scheme is that XDoclet doesn't support the meta attribute - if you define the javadoc as above then run it through XDoclet, the meta attributes won't propogate through to the HBM mapping file.
You can use the patch attached to XDoclet jira issue XDT-819 to enable support for the meta attribute. Building XDoclet is basic, just don't try to do "dist" build - only build the core and modules from the root. And only do that once, then while you're messing with the hibernate module XDT files just do a build from the root of the Hibernate module.
This is not a "best practice" guide, it's what we do on our project.
If this info is useful to you, it might be useful to others - I'm sure they'd appreciate it if you made a wiki topic out of it :)