-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 
Author Message
 Post subject: Working on hbm2java to implement association management
PostPosted: Thu Mar 22, 2007 8:41 pm 
Newbie

Joined: Wed Jan 14, 2004 4:44 pm
Posts: 13
Location: Wellington, New Zealand
I am working on hbm2java. I would like to implement association management for bidi mappings, and submit my work for inclusion in the tool if it is of interest. I need some help to find the name of the property at the opposite end of the association, details below.

Hibernate version:3.2.2, tools: 3.2.0 beta 9

Mapping documents:

From Car.hbm.xml:
<set name="wheels" inverse="true">
<key column="CAR_ID" />
<one-to-many class="Wheel" />
</set>

From Wheel.hbm.xml:
<many-to-one name="car" class="Car" column="CAR_ID" not-null="true" foreign-key="CAR_FK"/>

Desired output:

public void addWheel(Wheel wheel) {
if(wheel == null){
throw new IllegalArgumentException("Cannot add a null " + Wheel.class.getName());
}
if(wheel.getCar() != null){
wheel.getCar().removeWheel(wheel);
}
wheel.setCar(this);
getWheels().add(wheel);
}

public void removeWheel(Wheel wheel) {
getWheels().remove(wheel);
}

Achieved so far: (new template: PojoAssociationManagers.ftl added to Pojo.ftl)

<#if pojo.needsAssociationManagers()>
<#foreach property in pojo.getAllPropertiesIterator()>
<#if pojo.getMetaAttribAsBool(property, "gen-property", true)><#if c2h.isOneToMany(property)>
<#assign parameterName = pojo.getSingularNameFor(property)
parameterType = c2h.getOneToManyReferencedEntityType(property)
capitalizedParameterName = pojo.beanCapitalize(parameterName)
propertyName = pojo.getPropertyName(property)>
public void add${capitalizedParameterName}(${parameterType} ${parameterName}) {
if(${parameterName} == null){
throw new IllegalArgumentException("Cannot add a null " + ${parameterType}.class.getName());
}
if(${parameterName}.get{---Parent---}() != null){
${parameterName}.get{---Parent---}().remove${capitalizedParameterName}(${parameterName});
}
${parameterName}.set{---Parent---}(this);
get${propertyName}().add(${parameterName});
}

public void remove${capitalizedParameterName}(${parameterType} ${parameterName}) {
get${propertyName}().remove(${parameterName});
}
</#if></#if></#foreach></#if>

Output:

public void addWheel(org.example.domain.Wheel wheel) {
if(wheel == null){
throw new IllegalArgumentException("Cannot add a null " + org.example.domain.Wheel.class.getName());
}
if(wheel.get{---Parent---}() != null){
wheel.get{---Parent---}().removeWheel(wheel);
}
wheel.set{---Parent---}(this);
getWheels().add(wheel);
}

public void removeWheel(org.example.domain.Wheel wheel) {
getWheels().remove(wheel);
}

Outstanding:

I need to do two things:
1. Find the name of the property used in the "many" end of the association to reference the "one" end. In the output above, the placeholder ---Parent--- should be "Car". Note, of course, that the type of the entity owning the many end of the relationship is not the correct name here, we need the name of the property used by the entity at the "many" end.

2. (Less important) I would like to use the unqualified class name for the entity on the many side (i.e. Wheel rather than org.example.domain.Wheel)

If someone could help with these I would be most grateful :)

Cheers,

Russell

Added methods:
I have added the following methods to enable the templating above:

org.hibernate.tool.hbm2x.pojo.BasicPOJOClass:

public String getSingularNameFor(Property property){
String str = property.getName();
return ReverseEngineeringStrategyUtil.simpleSingularize(str);
}


org.hibernate.cfg.reveng.ReverseEngineeringStrategyUtil:
public static String simpleSingularize(String plural) {
String[][] suffixMap = {{"ies", "y"},{"uses", "us"},{"ses", "se"},{"x", "x"},{"s", ""}};
for(int i = 0; i < suffixMap.length; i++){
String suffix = suffixMap[i][0];
String replacement = suffixMap[i][1];
if(plural.endsWith(suffix)){
plural = plural.substring(0, (plural.length() - suffix.length()));
plural += replacement;
break;
}
}
return plural;
}

org.hibernate.tool.hbm2x.Cfg2HbmTool:

(Probably a bad name...)
public String getOneToManyReferencedEntityType(Property property){
if(!isOneToMany(property)){
throw new IllegalArgumentException(property.getName() + " is not one-to-many");
}
OneToMany oneToMany = null;
Value value = property.getValue();
if(value instanceof Collection) {
oneToMany = (OneToMany)((Collection)value).getElement();
} else {
oneToMany = (OneToMany)value;
}
return getClassName(oneToMany);
}


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 23, 2007 4:52 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
Sounds great you want to look into this.

You can see EntityPOJOClass.getOneToMappedBy which does *some* of this.

Note, make the generation of this logic dependent on a flag, e.g. "genbidirectional".

http://opensource.atlassian.com/project ... owse/HBX-5 is the relevant jira for this.

When you start contributing it then please do it via patches to the above jira.

thanks.

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 23, 2007 9:38 am 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
In the spirit of sharing, here's my association management freemarker template (PojoPropertyAccessors.ftl). It is a bit specific to my implementation, but it might lend a few tricks.

Code:
    // Property accessors
<#foreach property in pojo.getAllPropertiesIterator()>

<#if pojo.getMetaAttribAsBool(property, "gen-property", true)>
<#if pojo.hasFieldJavaDoc(property)>   
    /**       
${pojo.getFieldJavaDoc(property, 4)}
     */
</#if>
    <#include "Ejb3PropertyGetAnnotation.ftl"/>
    ${pojo.getPropertyGetModifiers(property)} ${pojo.getJavaTypeName(property, jdk5)} ${pojo.getGetterSignature(property)}() {
        return this.${property.name};
    }
   
    ${pojo.getPropertySetModifiers(property)} void set${pojo.getPropertyName(property)}(${pojo.getJavaTypeName(property, jdk5)} ${property.name}) {
        this.${property.name} = ${property.name};
    }
   <#if c2h.isOneToMany(property)>
      <#assign inversePojo = c2j.getPOJOClass(property.value.element.associatedClass)>
      <#assign inverseProperty = "">
      <#if property.name?starts_with(c2j.simplePluralize(inversePojo.declarationName)?uncap_first + "For")>
         <#assign potentialMatch = pojo.declarationName?uncap_first + "By" + (property.name)?substring((c2j.simplePluralize(inversePojo.declarationName) + "For")?length)>
      <#else>
         <#assign potentialMatch = pojo.declarationName?uncap_first>
      </#if>
      <#foreach field in inversePojo.getAllPropertiesIterator()>
         <#if potentialMatch = field.name>
            <#assign inverseProperty = field.name>
         </#if>
      </#foreach>
      <#assign addMethodName = "addTo" + property.name?cap_first>
      <#assign removeMethodName = "removeFrom" + property.name?cap_first>
      <#assign inverseClassName = pojo.importType(inversePojo.qualifiedDeclarationName)>
      <#assign inverseInstanceName = inverseClassName?uncap_first>
      <#if inverseProperty != ""><#assign inverseSetterName = "set" + inverseProperty?cap_first></#if>

   // <#if inverseProperty = "">Unidirectional<#else>Bidirectional</#if> collection management for ${property.name}

   /**
    * Add a ${inverseClassName} to the ${pojo.getPropertyName(property)} collection
    * @param ${inverseInstanceName} the instance of ${inverseClassName} to add to the ${pojo.getPropertyName(property)} collection
    * @return this ${pojo.declarationName} for method chaining
    */
   ${pojo.getPropertySetModifiers(property)} ${pojo.declarationName} ${addMethodName}(${inverseClassName} ${inverseInstanceName}) {
      if (${inverseInstanceName} != null) {
         if (${pojo.getGetterSignature(property)}() == null) {
            set${pojo.getPropertyName(property)}(${pojo.getFieldInitialization(property, jdk5)});
         }
         ${pojo.getGetterSignature(property)}().add(${inverseInstanceName});<#if inverseProperty != "">
         ${inverseInstanceName}.${inverseSetterName}(this);</#if>
      } else {
         throw new NullPointerException("Illegal attempt to add a null ${inverseClassName} to ${pojo.declarationName}");
      }
      return this;
   }
   
   /**
    * Remove a ${inverseClassName} from the ${pojo.getPropertyName(property)} collection
    * @param ${inverseInstanceName} the instance of ${inverseClassName} to remove from the ${pojo.getPropertyName(property)} collection
    * @return this ${pojo.declarationName} for method chaining
    */
   ${pojo.getPropertyGetModifiers(property)} ${pojo.declarationName} ${removeMethodName}(${inverseClassName} ${inverseInstanceName}) {
      if (${inverseInstanceName} != null) {
         if (${pojo.getGetterSignature(property)}() != null) {
            ${pojo.getGetterSignature(property)}().remove(${inverseInstanceName});<#if inverseProperty != "">
            ${inverseInstanceName}.${inverseSetterName}(null);</#if>
         }
      } else {
         throw new NullPointerException("Illegal attempt to remove a null ${inverseClassName} from ${pojo.declarationName}");
      }
      return this;
   }
   </#if>
</#if>
</#foreach>


For unidirectional associations, it generates:

Code:
   // Unidirectional collection management for academicEvaluationFlags

   /**
    * Add a AcademicEvaluationFlag to the AcademicEvaluationFlags collection
    * @param academicEvaluationFlag the instance of AcademicEvaluationFlag to add to the AcademicEvaluationFlags collection
    * @return this AcademicEvaluation for method chaining
    */
   public AcademicEvaluation addToAcademicEvaluationFlags(AcademicEvaluationFlag academicEvaluationFlag) {
      if (academicEvaluationFlag != null) {
         if (getAcademicEvaluationFlags() == null) {
            setAcademicEvaluationFlags(new HashSet<AcademicEvaluationFlag>(0));
         }
         getAcademicEvaluationFlags().add(academicEvaluationFlag);
      } else {
         throw new NullPointerException("Illegal attempt to add a null AcademicEvaluationFlag to AcademicEvaluation");
      }
      return this;
   }
   
   /**
    * Remove a AcademicEvaluationFlag from the AcademicEvaluationFlags collection
    * @param academicEvaluationFlag the instance of AcademicEvaluationFlag to remove from the AcademicEvaluationFlags collection
    * @return this AcademicEvaluation for method chaining
    */
   public AcademicEvaluation removeFromAcademicEvaluationFlags(AcademicEvaluationFlag academicEvaluationFlag) {
      if (academicEvaluationFlag != null) {
         if (getAcademicEvaluationFlags() != null) {
            getAcademicEvaluationFlags().remove(academicEvaluationFlag);
         }
      } else {
         throw new NullPointerException("Illegal attempt to remove a null AcademicEvaluationFlag from AcademicEvaluation");
      }
      return this;
   }


And for bidirectional:

Code:
   // Bidirectional collection management for agencyCertifications

   /**
    * Add a AgencyCertification to the AgencyCertifications collection
    * @param agencyCertification the instance of AgencyCertification to add to the AgencyCertifications collection
    * @return this Agency for method chaining
    */
   public Agency addToAgencyCertifications(AgencyCertification agencyCertification) {
      if (agencyCertification != null) {
         if (getAgencyCertifications() == null) {
            setAgencyCertifications(new HashSet<AgencyCertification>(0));
         }
         getAgencyCertifications().add(agencyCertification);
         agencyCertification.setAgency(this);
      } else {
         throw new NullPointerException("Illegal attempt to add a null AgencyCertification to Agency");
      }
      return this;
   }
   
   /**
    * Remove a AgencyCertification from the AgencyCertifications collection
    * @param agencyCertification the instance of AgencyCertification to remove from the AgencyCertifications collection
    * @return this Agency for method chaining
    */
   public Agency removeFromAgencyCertifications(AgencyCertification agencyCertification) {
      if (agencyCertification != null) {
         if (getAgencyCertifications() != null) {
            getAgencyCertifications().remove(agencyCertification);
            agencyCertification.setAgency(null);
         }
      } else {
         throw new NullPointerException("Illegal attempt to remove a null AgencyCertification from Agency");
      }
      return this;
   }


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 23, 2007 1:19 pm 
Newbie

Joined: Tue Feb 15, 2005 1:19 pm
Posts: 10
I am trying to generate Associations (OneToMany) using hbm2java.
Does the thread and above ftl file means that by default hbm2java doesn't support Associations.

Can u give me a working copy of the PojoPropertyAccessors.ftl with Associations. I have tried to use the copy above and the following ant tasks and it doesn't work

<hbm2java jdk5="true" ejb3="true"/>
<hbmtemplate
templatepath="${basedir}/templates/pojo/PojoPropertyAccessors.ftl"
templateprefix="pojo/"
template="pojo/PojoPropertyAccessors.ftl"
filepattern="{package-name}/{class-name}.java">
<property key="jdk5" value="true" />
<property key="ejb3" value="true" />
</hbmtemplate>


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 23, 2007 2:35 pm 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
As I stated, the ftl I posted is highly specific to my architecture and is in no way applicable out-of-the-box. For instance, I strip of the 'For' and 'By' on multi-homed associations, which requires tinkering with other templates.

hbm2java does form the associations correctly, but does not generate collection management routines in the POJOs. As evidenced by rushealy's post, it may be on the horizon.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 26, 2007 3:23 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
hi anansi,

it seems you are using the property names to find a "best match"; that might work sometimes - but definitly not all the time (but as you already said its highly dependent on your way of doing things - which is fine ;)

I just like to point out that the correct way of finding the "other side" is to find the opposite property that uses the same columns as the key/column.

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 26, 2007 3:50 pm 
Expert
Expert

Joined: Tue Jul 11, 2006 10:21 am
Posts: 457
Location: Columbus, Ohio
Absolutely Max, hence my plethora of caveats. ;)


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.