Answer depends on engineering pipeline. I use hibernate-tools 4.3.5.Final and Ant task hibernatetool with notable configurations:
<hbm2hbmxml/>
for HBM files generation;
<hbm2java jdk5="true" ejb3="true"/>
to create Java classes from HBM. Solution is in HBM contents.
Is there any way to specify in the file reveng.xml?
No. hbm2hbmxml is configured by reveng.xml. Péter's answer suggests to specify desired type in <column/>
, but OneToMany has no column. Relationship may explicitly be defined as following
<table name="MY_TABLE">
<foreign-key constraint-name="FK_TO_MY_TABLE_IN_OTHER_TABLE">
<set property="otherTables"/>
</foreign-key>
</table>
Notice <set/>
element. There is no <list/>
or other collection in hibernate-reverse-engineering-3.0 namespace. I have not found a way to change collection type in reveng.xml or via Ant task parameters, so every relationship in generated HBM is <set>
element (in hibernate-mapping-3.0 namespace).
Is there a way to change collection type?
Yes. HBM file must contain desired type element (<list>
, <bag>
, <map>
, etc.) instead of <set>
. By default Hibernate tools use templates in JAR folders hbm and pojo. Each of those may be overridden by files in subfolders of folder specified in templatepath attribute of hibernatetool Ant task. You need to provide custom FreeMaker templates in hbm folder with following modifications.
In the end of persistentclass.hbm.ftl replace
<#if c2h.getTag(property)!="version" && c2h.getTag(property)!="timestamp">
with (notice template name starts with list, desired collection type, and processed tag is set)
<#if c2h.getTag(property) == "set">
<#include "list.hbm.ftl"/>
<#elseif c2h.getTag(property)!="version" && c2h.getTag(property)!="timestamp">
In list.hbm.ftl remove following line (because set has no index property)
<#assign indexValue = value.getIndex()>
and replace mandatory element
<list-index>
<#foreach column in indexValue.columnIterator>
<#include "column.hbm.ftl">
</#foreach>
</list-index>
with (put whatever you like instead of ID
, it is not present in generated Java code)
<list-index column="ID"/>
List implies order. I suggest to add OrderBy
annotation with related entity ID field name. Add to list.hbm.ftl after <#include "meta.hbm.ftl">
<meta attribute="scope-get">@javax.persistence.OrderBy("id") public</meta>
maggu suggests to add extra-import meta attribute to have package name mentioned in class file only once, but in my experience it breaks build with undefined symbol.
Notice actual scope after annotation. There is no way to put it on separate line (whitespace gets trimmed).
Alternative to scope hack is adding specific annotation in pojo build stage (one with hbm2java) together with other annotations, e.g., add to Ejb3PropertyGetAnnotation.ftl within <#elseif c2h.isCollection(property)>
block following line
<#if property.value.type.returnedClass.simpleName == "List">@javax.persistence.OrderBy("id")</#if>
<list>
and <bag>
lead to new ArrayList<>(0)
as default field value, but list is exposed as useful java.util.List, while bag is only java.util.Collection. It should be possible to change this (also for set) via collection-type attribute, but it accepts only org.hibernate.usertype.UserCollectionType implementations, not JDK classes.
In my project I had to replace Set with List only for certain relationships, so my list.hbm.ftl contains <#if property.name == "otherTables">
. You can adjust condition as needed.