I have fixed this. Please create a file in your project root called hbm.template with the following content:
Code:
<%
import org.apache.log4j.Logger
import com.objectgen.core.*
import com.objectgen.types.*
import com.objectgen.codegen.hibernate.*
// Configure MySQL specifics by setting database='mysql'
def database = ''
log = Logger.getLogger("com.objectgen.codegen.hibernate.hbm-template")
collectionTypes = [ 'set', 'list', 'bag', 'idbag', 'map' ]
// Handle subclasses with Table per class hierarchy.
// Find all Persistent subclasses in the same package.
subclasses = []
for(sub in c.packageData.classifiers) {
if(sub.stereotype?.name == 'Persistent' && sub.superClass == c) {
subclasses.add(sub)
}
}
%><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated by HiberObjects. DO NOT EDIT! --><% /* Feel free to edit this template, but not the generated xml file. */ %>
<hibernate-mapping>
<class name='${className(c)}' table='${tagParamValue(c, '@hibernate', 'table', tableName(c.name))}'>
<%
// Generate <version> if a tag @hibernate.version is designed.
version = TagUtils.findTag(c, '@hibernate.version')
if(version != null) {
%> <version${tagParameters(version)}/>
<%
}
// First, generate id or composite-id.
for(var in c.variables) {
String propertyName = propertyName(var)
if(var.stereotype?.name == 'id') {
if(var.attributeType) {
generatorClass = tagParamValue(var, '@hibernate', 'generator.class', 'native')
%> <id name='${propertyName}' type='${javaType2Hibernate(var.type)}' column='${idColumn(c, var)}'>
<generator class='${generatorClass}' />
</id>
<% } else {
%> <composite-id name='${propertyName}' class='${var.type.fullName}'><%
for(compositeVar in var.type.variables) {
if(!compositeVar.isStatic() && !compositeVar.isFinal()) { %>
<key-property name='${propertyName(compositeVar)}'/><%
}
} %>
</composite-id>
<%
}
}
}
if(!subclasses.empty) {
%> <discriminator column='${c.name.toLowerCase()}_type' type='string'/>
<%
}
// Iterate through all variables in the class 2 times,
// first for the elements in <natural_id>, then for the rest.
for(boolean natural_id in [true,false])
{
for(var in c.variables) {
if(natural_id != (var.stereotype?.name == 'natural id'))
continue
if(var.isStatic() || var.isFinal())
continue
// Skip <<id>>, it is already generated.
if(var.stereotype?.name == 'id')
continue
// Don't generate relations to non-persistent classes.
if(!variableIsPersistent(var))
continue
String propertyName = propertyName(var)
element = null
inverse = var.inverse
inverseMultiplicity = (inverse != null ? inverse.multiplicity : var.inverseMultiplicity)
inverseSingle = (inverseMultiplicity == '1' || inverseMultiplicity == '0..1')
hibernateTag = null
collectionRefType = null
childElements = []
childIndex = [:]
attributes = [:]
columns = []
naturalId = findNaturalId(var.type)
Variable varId = findId(var.type)
Variable compositeId = null
if(varId != null && !varId.type.attributeType) {
compositeId = varId
}
attributes['name'] = propertyName
if(var.attributeType) {
// The variable is an attribute.
element = 'property'
if(database == 'mysql' && (var.type.name == 'boolean' || var.type.name == 'Boolean') ) {
attributes['type'] = javaType2Hibernate(var.type)
childIndex.put('column', childElements.size())
sqlTypeParam = new TagParameter('column.sql-type', 'boolean')
childElements.add( [ sqlTypeParam ] )
} else {
attributes['type'] = javaType2Hibernate(var.type)
attributes['column'] = mixedCase2UnderScore(var.name)
}
hibernateTag = TagUtils.findTag(var, '@hibernate.property')
if(hibernateTag == null)
hibernateTag = TagUtils.findTag(var, '@hibernate')
debug 'attribute: ' + var
} else if(!mapAsSet(var)) {
// The variable is a single association.
attributes['class'] = className(var.type)
singleTypes = [ 'many-to-one', 'one-to-one', 'component' ]
for(s in singleTypes ) {
hibernateTag = TagUtils.findTag(var, '@hibernate.' + s)
if(hibernateTag != null) {
element = s
break
}
}
if(hibernateTag == null) {
hibernateTag = TagUtils.findTag(var, '@hibernate')
}
if(element == null) {
// Map 1 to 1 associations to a many-to-one and a one-to-one relation.
if(inverseSingle && (c.name >= var.type.name))
element = 'one-to-one'
else
element = 'many-to-one'
}
if(element == 'one-to-one' && inverse != null)
attributes['property-ref'] = inverse.name
if(element != 'one-to-one' && element != 'component' && var.multiplicity == var.ONE) {
attributes['not-null'] = 'true'
attributes['cascade'] = 'save-update'
}
if(element == 'many-to-one') {
String col = (naturalId != null ? naturalId.name : 'id')
attributes['foreign-key'] = 'fk_' + mixedCase2UnderScore(baseClassName(c.name) + '_' + var.name) + '_' + col
if(findColumnNameTag(var) != null) {
// The <column> elements are generated by tags. Do nothing here.
} else if(compositeId != null) {
for(compositeVar in compositeId.type.variables) {
columns.add( mixedCase2UnderScore(propertyName(compositeVar)) )
}
} else {
columns.add( mixedCase2UnderScore(propertyName) + '_' + col )
}
if(inverseSingle) {
attributes['unique'] = 'true'
}
if(naturalId != null) {
attributes['property-ref'] = naturalId.name
}
}
/* TODO Generate cascade=all in the inverse end of 1 multiplicity?
if(element == 'many-to-one' || element == 'one-to-one')
attributes['cascade'] = 'all'
*/
debug 'single association: hibernateTag=' + hibernateTag + ', element=' + element
} else {
// The variable is a multiple association.
// Generate a <set>
// The user can generate <list>, <bag> etc by designing a tag like "@hibernate.list".
element = 'set'
String generateCollectionsAsSet = c.codeGenerator.getOption(PersistentFactory.GENERATE_COLLECTIONS_AS_SET)
debug 'generateCollectionsAsSet=' + generateCollectionsAsSet
if(generateCollectionsAsSet != null && generateCollectionsAsSet == 'false') {
element = 'list'
}
for(s in collectionTypes ) {
hibernateTag = TagUtils.findTag(var, '@hibernate.' + s)
if(hibernateTag != null) {
element = s
break
}
}
if(hibernateTag == null) {
hibernateTag = TagUtils.findTag(var, '@hibernate')
}
debug 'multiple association: hibernateTag=' + hibernateTag + ', element=' + element
// See if this is a bidirectional association.
debug "inverse=${inverse}, inverseMultiplicity=${inverseMultiplicity}, inverseSingle=${inverseSingle}"
if(inverseSingle) {
collectionRefType = 'one-to-many'
if(inverse != null) {
attributes['inverse'] = 'true'
}
debug "${var} aggregate=${var.aggregate}, ${inverse}"
if(var.aggregate) {
if(inverseMultiplicity == '1') {
attributes['cascade'] = 'all,delete-orphan'
} else {
attributes['cascade'] = 'all'
}
}
}
else {
collectionRefType = 'many-to-many'
if(inverse != null) {
attributes['inverse'] = (c.name >= var.type.name)
}
relation = null
if(var.associationName != null) {
relation = var.associationName
} else if(inverse != null && c.name >= var.type.name) {
relation = baseClassName(var.type.name) + '_' + baseClassName(c.name)
} else {
relation = baseClassName(c.name) + '_' + baseClassName(var.type.name)
}
attributes['table'] = tableName(relation)
}
}
// Collect parameters to the @hibernate tag.
// These may override the default attributes set earlier.
if(hibernateTag != null) {
debug 'hibernateTag=' + hibernateTag
for(param in hibernateTag.parameters) {
if(param.name.startsWith('column.')) {
attributes.remove('column')
}
ix = param.name.indexOf(".")
if(ix < 0) {
attributes[param.name] = param.value
}
else {
// Join duplicate elements in the list.
elementName = param.name.substring(0, ix)
if(childIndex.containsKey(elementName)) {
index = childIndex.get(elementName)
childElements.get(index).add(param)
debug 'add to <' + elementName + '>: childElements=' + childElements
} else {
childIndex.put(elementName, childElements.size())
childElements.add( [ param ] )
debug 'new element <' + elementName + '>: childElements='+ childElements
}
}
}
}
// Generate mapping for the variable.
// Generate <natural-id> around 1 property if it has that stereotype.
// TODO Support multiple elements in the same <natural-id>
if(natural_id) {
%> <natural-id>
<% }
%> <${element} <%
for(key in attributes.keySet()) {
%> ${key}='${attributes[key]}'<%
}
%>>
<% for(paramList in childElements) {
param1 = paramList.get(0)
ix = param1.name.indexOf(".")
elementName = param1.name.substring(0, ix)
if(elementName != collectionRefType) {
debug 'generate <' + elementName + '> parameters: ' + paramList
%> <${elementName}<%
for(param in paramList) {
attrName = param.name.substring(ix+1)
%> ${attrName}='${param.value}'<%
}
%>/>
<%
}
}
for(column in columns) {
%> <column name='${column}'/>
<%
}
// For map, set, list and bag, generate <key column='...'>
if(['map', 'set', 'list', 'bag'].contains(element) && !childIndex.containsKey('key')) {
def fk = 'fk_' + mixedCase2UnderScore(baseClassName(c.name) + '_' + baseClassName(var.type.name)) + '_key'
def idCol = idColumn(c, null)
if(inverse != null) {
def inverseNaturalId = findNaturalId(c)
String col = (inverseNaturalId != null ? inverseNaturalId.name : 'id')
idCol = mixedCase2UnderScore(inverse.name) + '_' + col
fk = 'fk_' + mixedCase2UnderScore(baseClassName(var.type.name) + '_' + inverse.name) + '_key'
}
%> <key column='${idCol}' foreign-key='${fk}'/>
<% }
// For map and list, generate <index>
if((element == 'map' || element == 'list') && !childIndex.containsKey('index')) {
%> <index column='${indexColumn(var)}'<%
if(element == 'map') {
%> type='string'<%
}
%> />
<% }
// For multiple associations, generate <many-to-many> unless <one-to-many>,
// <element>, <many-to-one> or <composite-element> has already been generated.
foundChild = findMapValue(childIndex, ['one-to-many', 'element', 'composite-element', 'many-to-one', 'many-to-any'])
if(!var.attributeType && mapAsSet(var) && foundChild == null) {
debug 'Generate collectionRefType=' + collectionRefType
%> <${collectionRefType} class='${className(var.type)}'<%
if(collectionRefType == 'many-to-many') {
def fk = 'fk_' + mixedCase2UnderScore(baseClassName(c.name) + '_' + propertyName)
%> foreign-key='${fk}'<%
String column = null
if(naturalId != null) {
column = naturalId.name
} else if(compositeId == null) {
column = relationColumn(c,var,inverse)
}
if(column != null) {
%> column='${column}'<%
}
if(naturalId != null) {
%> property-ref='${naturalId.name}'<%
}
}
// Generate the @hibernate tag parameters.
for(paramList in childElements) {
param1 = paramList.get(0)
ix = param1.name.indexOf(".")
elementName = param1.name.substring(0, ix)
if(elementName == collectionRefType) {
debug 'generate ' + elementName + ' parameters: ' + paramList
for(param in paramList) {
attrName = param.name.substring(ix+1)
%> ${attrName}='${param.value}'<%
}
}
}
if(compositeId != null && collectionRefType != 'one-to-many') {
%> ><%
for(compositeVar in compositeId.type.variables) { %>
<column name='${propertyName(compositeVar)}'/><%
}
%>
</${collectionRefType}>
<%
} else {
%> />
<%
}
}
%> </${element}>
<%
if(natural_id) {
%> </natural-id>
<% }
} // end for(var)
} // end for(natural_id)
// Handle subclasses with Table per class hierarchy.
// The subclasses will not get their own mapping files.
for(sub in subclasses) {
%> <subclass name="${sub.fullName}" discriminator-value="${sub.name}">
<%
for(var in sub.variables) {
if(var.stereotype?.name == 'id')
continue
else if(var.isStatic() || var.isFinal())
continue
else if(!variableIsPersistent(var))
continue
else if(var.attributeType) {
%> <property name='${propertyName(var)}' type='${javaType2Hibernate(var.type)}' column='${mixedCase2UnderScore(var.name)}'>
</property>
<%
}
}
%> </subclass>
<%
}
%> </class>
</hibernate-mapping><%
void debug(s) {
if(log != null) log.debug(s)
}
boolean variableIsPersistent(var) {
if(var.type instanceof Classifier) {
Classifier varType = (Classifier) var.type
if(!varType.attributeType &&
(varType.stereotype == null ||
varType.stereotype.name != 'Persistent') )
return false
}
return true
}
Object findMapValue(map, keyList) {
for(key in keyList) {
value = map.get(key)
if(value != null)
return value
}
return null;
}
String tagParamValue(taggedValue, tagName, paramName, defaultValue) {
param = TagUtils.findTagParameter(taggedValue, tagName, paramName)
return (param != null ? param.value : defaultValue)
}
String tagParamValue(tag, paramName, defaultValue) {
param = TagUtils.findTagParameter(tag, paramName)
return (param != null ? param.value : defaultValue)
}
String tagParameters(tag) {
buf = new StringBuffer()
for(param in tag.parameters) {
buf.append(" ").append(param.name).append("=").append("\'").append(param.value).append("\'")
}
return buf
}
/** For a class name "VolumePriceType", return "volum_price_type_id" etc. */
String idColumn(c, id) {
if(id == null) {
id = findId(c)
}
if(id != null) {
idColumn = findColumnNameTag(id)
if(idColumn != null) {
return idColumn
}
}
s = baseClassName(c.name)
return mixedCase2UnderScore(s) + '_id'
}
String columnName(var) {
column = findColumnNameTag(var)
if(column != null) {
return column
} else {
return mixedCase2UnderScore(var.name)
}
}
String findColumnNameTag(var) {
column = tagParamValue(var, '@hibernate', 'column', null)
if(column == null)
column = tagParamValue(var, '@hibernate', 'column.name', null)
return column
}
String className(c) {
return c.fullName
}
String baseClassName(String s) {
// Can put special handling, for example remove prefix "H"
// if(s[0] == 'H')
// s = s[1..-1]
return s
}
// Convert "MyTable" to "tb_my_table"
String tableName(s) {
s = baseClassName(s)
buf = new StringBuffer('tb')
mixedCase2UnderScore(s, buf)
return buf
}
String indexColumn(var) {
return mixedCase2UnderScore(var.name) + '_ix'
}
String relationColumn(c, var, inverse) {
return mixedCase2UnderScore(var.name) + '_id'
}
String inverseRelationColumn(c, var, inverse) {
if(inverse != null)
return mixedCase2UnderScore(inverse.name) + '_id'
else
return relationColumn(c, var, inverse)
}
String javaType2Hibernate(type) {
return HibernateTypes.instance.javaType2hibernateType(type.name)
}
/** Convert from "MixedCase" to "mixed_case" etc. */
String mixedCase2UnderScore(String s, StringBuffer buf) {
for(char ch in s) {
if(buf.size() > 0 && Character.isUpperCase(ch)) {
if(buf[-1] != '_')
buf.append('_')
}
buf.append(Character.toLowerCase(ch))
}
return buf
}
String mixedCase2UnderScore(String s) {
return mixedCase2UnderScore(s, new StringBuffer())
}
// Find any variable with stereotype <<natural id>> in a class.
Variable findNaturalId(type) {
return findVariableStereotype(type, 'natural id')
}
// Find any variable with stereotype <<id>> in a class.
Variable findId(type) {
return findVariableStereotype(type, 'id')
}
Variable findVariableStereotype(cl, stereotype) {
if(cl instanceof Classifier && !cl.attributeType) {
for(var in cl.variables) {
if(var.stereotype != null && var.stereotype.name == stereotype) {
return var
}
}
}
return null
}
// Determine if a relation shall be mapped to a <set>
boolean mapAsSet(var) {
if(!var.single)
return true
for(s in collectionTypes ) {
hibernateTag = TagUtils.findTag(var, '@hibernate.' + s)
if(hibernateTag != null)
return true
}
return false
}
// Convert variable name to a correct property name
String propertyName(var) {
String varName = var.name
if(varName.length() >= 2) {
if(Character.isUpperCase( (char)varName[1] ) )
return varName[0].toUpperCase() + varName[1..-1]
else
return varName[0].toLowerCase() + varName[1..-1]
}
return varName
}
%>
Then set the inverse multiplicity of the association to 1 to test it.
This hbm.template will be part of the next release, and then you can remove it from your project again.
About JPA: Yes, Hibernate supports JPA. Then you can get rid of those XML files. And to try it in HiberObjects, just open Project Properties go to HiberObjects > Persistence and select Platform "Java Persistence API". Then all the code will be regenerated to work with annotations instead of XML files.
Regards,
Lars