I think you all may be interested in
http://opensource.atlassian.com/project ... key=HB-700
The quick version:
Many Hibernate users use hbm2java to create their basic persistent POJOs from Hibernate mappings. Something like this:
Code:
<hibernate-mapping>
<class name="my.Parent">
<meta attribute="generated-class">my.ParentDTO</meta>
<id name="id" type="long" unsaved-value="null" >
<generator class="native"/>
</id>
<set name="children" inverse="true" cascade="all-delete-orphan">
<key column="parent_id"/>
<one-to-many class="my.Child"/>
</set>
</class>
<class name="my.Child">
<meta attribute="generated-class">my.ChildDTO</meta>
<id name="id" type="long" unsaved-value="null" >
<generator class="native"/>
</id>
<discriminator/>
<many-to-one name="parent" column="parent_id" not-null="true"
class="my.Parent"/>
<subclass name="my.SpecialChild"
discriminator-value="SPEC">
<meta attribute="generated-class">my.SpecialChildDTO</meta>
</subclass>
</class>
</hibernate-mapping>
Which can be used with this hbm2java.config.xml:
Code:
<codegen>
<generate
package="data"
renderer="net.sf.hibernate.tool.hbm2java.BasicRenderer"/>
</codegen>
To produce this code (some boilerplate omitted):
Code:
package data;
...
/** @author Hibernate CodeGenerator */
abstract public class ParentDTO implements java.io.Serializable {
/** identifier field */
private Long id;
/** persistent field */
private Set children;
/** full constructor */
public ParentDTO(Set children) {
this.children = children;
}
...
}
/** @author Hibernate CodeGenerator */
abstract public class ChildDTO implements java.io.Serializable {
/** identifier field */
private Long id;
/** persistent field */
private my.Parent parent;
/** full constructor */
public ChildDTO(my.Parent parent) {
this.parent = parent;
}
...
public my.Parent getParent() {
return this.parent;
}
public void setParent(my.Parent parent) {
this.parent = parent;
}
...
}
/** @author Hibernate CodeGenerator */
abstract public class SpecialChildDTO extends Child implements java.io.Serializable {
/** full constructor */
public SpecialChildDTO(my.Parent parent) {
super(parent);
}
/** default constructor */
public SpecialChildDTO() {
}
public String toString() {
return new ToStringBuilder(this)
.append("id", getId())
.toString();
}
}
Then you implement my.Parent extends data.ParentDTO, my.Child extends data.Child, and my.SpecialChild extends data.SpecialChildDTO. You put all kinds of business behavior and domain knowledge into the my.Parent, my.Child, and my.SpecialChild subclasses. When you revise your data model, you regenerate the DTO superclasses and leave the my.* subclasses mostly alone. And all is well.
Except you can't actually use the DTO superclasses *as* data transfer objects, since they contain all kinds of references to my.* classes which contain your private business logic. i.e. SpecialChildDTO extends my.Child so you can't send out a SpecialChildDTO without also exposing your my.Child business subclass!
So I have submitted a patch to hbm2java to allow you to use this config.xml:
Code:
<codegen>
<generate
package="dto"
renderer="net.sf.hibernate.tool.hbm2java.BasicRenderer">
<param name="use-generated-classes-only">true</param>
</generate>
</codegen>
Which, when run against the *exact* same mapping given above, produces *this* code:
Code:
package dto;
...
/** @author Hibernate CodeGenerator */
public class ParentDTO implements Serializable {
/** identifier field */
private Long id;
/** persistent field */
private Set children;
/** full constructor */
public ParentDTO(Set children) {
this.children = children;
}
}
/** @author Hibernate CodeGenerator */
public class ChildDTO implements Serializable {
/** identifier field */
private Long id;
/** persistent field */
private dto.ParentDTO parent;
/** full constructor */
public ChildDTO(dto.ParentDTO parent) {
this.parent = parent;
}
...
public dto.ParentDTO getParent() {
return this.parent;
}
public void setParent(dto.ParentDTO parent) {
this.parent = parent;
}
...
}
/** @author Hibernate CodeGenerator */
public class SpecialChildDTO extends dto.ChildDTO implements Serializable {
/** full constructor */
public SpecialChildDTO(dto.ParentDTO parent) {
super(parent);
}
...
}
This new hierarchy maps exactly to the DTO classes in the other hierarchy -- same property names, same field names, and so forth. And it is possible to write a generic DTOMapper that uses reflection to convert any graph of objects from one hierarchy into a graph of objects from the other hierarchy, without losing any information. (I will be writing such a mapper within the next few days.)
This, I think, is a pretty good solution to the evolving-DTO problem, which we also have in our project.
What do you all think? If you like it, please consider voting for HB-700 in JIRA :-)
Cheers!
Rob