Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Hibernate/Spring Many-to-one association and dynamicform DMI

    Hi

    Im sure this is a trivial but I cant find a sample or forum thread that discusses how to create a form that allows management of the association between a manytoone association. I am using smartgwtee pro 2.4 and the spring/hibernate/DMI and want a simple form to

    For example (non relevant attributes ignored) the domain classes look like:

    public class Person (

    @ManyToOne(fetch=LAZY,optional=true)
    @ForeignKey(name="fk_person_country")
    public Country getCountryOfResidence() {
    return countryOfResidence;
    }
    public void setCountryOfResidence(Country countryOfResidence) {
    this.countryOfResidence = countryOfResidence;
    }

    )

    Country (

    )


    I can happily list Person objects and show their country using the xpath value as per the spring sample. How should the form be constructed with a combobox to display a country for person. My approach at the moment is to create a DynamicForm with the datasource of Person and add a ComboBoxItem with a datasource of Country. This does not seem to work and the countryOfResidence form item shows as [object Object]

    Any help much appreciated.

    Many thanks

    Anthony

    #2
    You need two DataSources fields:

    1. the value to display for the related object. This uses valueXPath and is hidden.

    2. the id of the related object. This is declared as a foreignKey and sets displayField to the name of the field containing the display value. This will automatically become a SelectItem when you bind to a form, and use the optionDataSource functionality to retrieve values from the related DataSource.

    Comment


      #3
      Hi thanks for the response almost there!

      So in the datasource for Person I have this as the value to display for the related object.

      <field unknownType="true" name="countryOfResidence" type="domain.Country" valueXPath="countryOfResidence/name"></field>

      I think i'm struggling with the second datasource field you say is needed, the foreignKey .... the id of the related object is 'id' same as the Person ... how do specify this. I think im being a bit slow here :-) , ive just not seen this in the examples.

      If this works as you suggest it would be amazing.

      Best regards

      Anthony

      Comment


        #4
        Your second field is going to look something like this:

        Code:
        <field  name="countryOfResidenceId" type="integer" foreignKey="Country.id"/>
        This assumes you've got a DataSource for Country objects with a field "id" which is the PK. It also assumes that on your Person domain object, you've explicitly declared a "countryOfResidenceId" field as a foreignKey to Country, with some integral type (eg Long).

        Comment


          #5
          Hi

          Right almost there! so my domain object does not have a countryOfResidenceId it has countryOfResidence which is of type Country with the foreign key specified by the annotation:

          @ManyToOne(fetch=LAZY,optional=true)
          @ForeignKey(name="fk_person_country")

          countryOfResidence is currently specified as

          <field unknownType="true" name="countryOfResidence" type="domain.Country" valueXPath="countryOfResidence/longName"></field>

          Is the fact we dont have a integer as the foreign key the root cause of the problem?

          Thanks for your help

          Anthony

          Comment


            #6
            Hi

            Just posting back to any followers of the thread to inform that a single field in the datasource that looks something like the below works.

            <field unknownType="false" name="languageOfDelivery" type="domain.Language" valueXPath="languageOfDelivery/name" editorType="ComboBoxItem" foreignKey="Language.id"></field>

            One bug i did notice is that a camel case attribute does not seem to work. For example valueXPath="languageOfDelivery/longName" (assuming the name attribute is called longName on the language ds and the domain object) resulting in the combobox showing IDs and not the longName attribute.

            Thanks for your help

            Comment


              #7
              So this approach almost works .... when saving a form a Long is passed rather than the full object of the selected object from the drop down. Is this what you would expect?

              The saving DAO then needs to be aware of while attributes are foreign key .. which kinda defeats the point of having a nice generic DAO for entities.

              Comment


                #8
                Your entities are still not correct. Once again you want an explicit declaration of a foreignKey of some atomic type (integer/long/String), not just a declaration of the relation.

                Here's a sample using JPA annotations, which are very close to Hibernate's. Note the property "Long countryId" - you want a DataSource field of type "integer" for this with foreignKey="Country.id". This is field #2 from the previous discussion.

                Field #1 would be type "string" and use a valueXPath like "country/countryName" to get to the name in the related entity.

                Note all the "updateable=false" etc flags on the country property. This makes it so you can just update the ID and not worry about an additional fetch to get the related Country object.

                Code:
                @Entity
                @Table (name="Country")
                public class Country
                    implements Serializable
                {
                
                    @Id
                    @Column (nullable = false)
                    @GeneratedValue (strategy = GenerationType.IDENTITY)
                    private Long countryId;
                
                    @Column (nullable = false)
                    private String countryName;
                
                ....
                }
                
                @Entity
                @Table (name="City")
                public class City
                    implements Serializable
                {
                
                    @Id
                    @Column (nullable = false)
                    @GeneratedValue (strategy = GenerationType.IDENTITY)
                    private Long cityId;
                
                    @Column (nullable = false)
                    private String cityName;
                
                    // This property has real foreign key value
                    @Column (nullable = false)
                    private Long countryId;
                
                    // This property refers to related entity (read-only)
                    @ManyToOne
                    @JoinColumn(name="countryId", referencedColumnName="countryId", nullable=false, insertable=false, updatable=false)
                    private Country country;
                ....
                }
                Last edited by Isomorphic; 21 Apr 2011, 07:36.

                Comment


                  #9
                  Ok thank you.

                  Different domain object this time but same problem so my hibernate code looks like.



                  Code:
                  private Long languageOfDeliveryId;
                  	
                  	public Long getLanguageOfDeliveryId() {
                  		return languageOfDeliveryId;
                  	}
                  	public void setLanguageOfDeliveryId(Long languageOfDeliveryId) {
                  		this.languageOfDeliveryId = languageOfDeliveryId;
                  	}
                  	
                  	
                  	private Language languageOfDelivery;
                  	
                  	@ManyToOne
                  	@JoinColumn(name="languageOfDeliveryId", referencedColumnName="id", nullable=false, insertable=false, updatable=false)
                  	public Language getLanguageOfDelivery() {
                  		return languageOfDelivery;
                  	}
                  	public void setLanguageOfDelivery(Language languageOfDelivery) {
                  		this.languageOfDelivery = languageOfDelivery;
                  	}
                  And the data source fields look like

                  Code:
                  <field  name="languageOfDelivery" 	type="string" valueXPath="languageOfDelivery/name"></field>
                  		<field name="languageOfDeliveryId" 	type="long" 	foreignKey="true"></field>
                  The resulting form simply has a two text fields one for languageOfDelivery (showing the text name) and languageOfDeliveryId showing the FK ID. From your previous post the first field has a type of string and the second a long. How does smartGWT know what the type checkboxitem to show?

                  Comment


                    #10
                    It should not be foreignKey=true but rather foreignKey="otherDataSourceId.pkFieldName". Sorry, we introduced this error in a previous post (now corrected).

                    This will cause a SelectItem to be used with an optionDataSource on the related DS. The other field (with the valueXPath) should be hidden="true" and use as a displayField.

                    Not sure what you mean about a checkBoxItem, that's for booleans.

                    Comment


                      #11
                      Hi thanks again .. yes I tried with foreignKey="Language.id" and ignore checkBoxItem i meant SelectItem.

                      The resulting datasource fields look like

                      Code:
                      <field hidden="true" name="languageOfDelivery" 	type="text" valueXPath="languageOfDelivery/name"></field>
                       <field name="languageOfDeliveryId" 	type="long" 	foreignKey="Language.id"></field>
                      But no SelectItem displays in the form. It can be overridden with editorType="ComboBoxItem" but this does not behave correctly and shows the ID of language when the selectitem first displays .. although the dropdown shows a list of countries. The fact that the selectitem does not appear without editorType="ComboBoxItem" suggests something is still wrong.

                      Have I missed something here?

                      Again

                      Many thanks

                      Anthony

                      Comment


                        #12
                        Should be type="integer". Don't worry, a Long is used in server-side code for this type - we use logical types like "integer" because the type is abstract and needs to be mapped to JavaScript, Java and SQL.

                        As far as countries should up, make sure you actually have a DataSource called "Language" with a field "id" and it's mapped to the Hibernate bean for Language. Maybe you copied your Country.ds.xml file over and didn't change everything.

                        Comment


                          #13
                          Thanks for the suggestions .. the language datasource is here:

                          Code:
                          <!-- Auto-generated from Java class com.saviso.rad.minerva.server.domain.Language -->
                          
                          <DataSource 
                          	xmlFromConfig="true"
                          	beanClassName="domain.Language"
                          	ID="Language"
                          	dataSourceVersion="1"
                          	generatedBy="SC_SNAPSHOT-2011-02-28/EVAL Deployment 2011-03-01"
                          	serverType="generic"
                          >
                          	<fields>
                          		<field name="id" type="sequence" hidden="true" primaryKey="true"></field>
                          		<field name="archived" type="boolean"></field>
                          		<field name="name" type="text"></field>
                          		<field name="code" type="text"></field>
                          	</fields>
                          	<serverObject lookupStyle="spring" bean="languageDao"/>
                          </DataSource>
                          And the code to load the datasource works as below .. so we definilty have a datasource called Language

                          Code:
                          DataSource LanguageDS = DataSource.get("Language");
                          I have changed type from long to integer .... and now the fields look like

                          Code:
                           		<field hidden="true" name="languageOfDelivery" 	type="text" valueXPath="languageOfDelivery/name"></field>
                           		<field name="languageOfDeliveryId" 	type="integer" 	foreignKey="Language.id"></field>
                          Thanks again!

                          Anthony

                          Comment


                            #14
                            Are you reporting that it works now or still having a problem?

                            Comment


                              #15
                              Sorry .... still not working! Any other suggestions?

                              Thanks again

                              Comment

                              Working...
                              X