Announcement

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

    hierarchical server data / JPA / Java Objects or XML

    Hi,

    We just finished a GWT application using client GWT-Ext and server JPA, client-server communication via GWT-RPC. To move ahead with another application, we are thinking about a new infrastructure in which the JPA layer will be kept intact. Client side is open for discussion. While moving towards Ext-GWT (GXT) we came accross SmartGWT. Very promissing components and good story about client-server integration, but the big question now is: how to handle complex hierarchical data structures (1:n, parent-multiple childs) when communicating between client and server ?

    With GWT-RPC we used Java Serializable Objects for this. Now trying SmartGWT we experimented with the RestDataSource, passing XML between client and server. Works fine, but this concept is Record oriented, so no multiple levels. Can this RestDataSource be an approach in the right direction, or can you suggest another approach ?

    I came accross another thread in the SmartClient forum: 2834 about hierarchical XML data binding. This thread pretty much explains the issue we have. However this is a solution for SmartClient, not SmartGWT.

    To be clear, we are not looking for direct communication with a JPA layer. On the server side we talk to JPA, which returns Java Serializable Objects (JPA entities), containing Collections for 1:n relations. But how to translate these JPA entities, so that the outcome can be used in SmartGWT Grids. Oh, and not just data retrieval of course, but how to handle the translation best when using a SmartGWT DynamicForm for add or update of a parent with multiple child records ?

    Kind Regards

    #2
    The easiest way to do this is with SmartGWT Enterprise, due out in a few days. You can get the gist of how this works by reading the Server-side data integration chapter from the SmartClient Reference.

    In a nutshell:

    1) you define a method for SmartClient to call via DMI, and which returns a List of (potentially nested) Java POJOs. SmartClient will deliver these to the browser, where they will be Records.

    2) your nested beans are available as Collections of Records on each parent Record. You can pass the child Records to eg an editable ListGrid for editing

    3) once edited you can put the edited data back onto the Record for the parent bean

    4) you can then pass the Record back to the server as data from eg DynamicForm.saveData() for a form that was editing the parent Record. SmartClient will automatically, recursively apply the nested Record data to your server-side beans

    That all said.. the approach for doing this instead with XML comm and no SmartClient Server is essentially similar. In place of #1 above, use XStream or a similar technology to serialize your recursive bean structure as XML and deliver it to a RestDataSource. In place of #4 above, serialize the resulting data as XML as indicated for SmartClient.

    It's also going to be possible to integrate with GWT-RPC services, but this is waiting on a SmartClient API to be added (DataSource.transformRequest()).

    Comment


      #3
      Override point DataSource.transformRequest() has been added to build 12-04-2008.

      Sanjiv

      Comment


        #4
        I am interested in how this approach with the enterprise server is different from the XML approach? If it is possible to handle nested records on the client side, then I would expect this to also be available on the LGPL client code? Or am I missing something. I know that the rpc mechanism uses a different serialization method as for instance the restdatasource. But once your back on the client that should not make a difference? If this is possible do you mean with nested records using a datasource with its foreignkey apporach?

        Comment


          #5
          Thanks for the quick response. 3 options I see: enterprise, XML or GWT-RPC. But I think with all 3 it is still required to have a good understanding on how to define the parent-child relations in DataSources and/or Records. Is it only the DataSourceField.setForeignKey or also DataSource.setInheritsFrom or DataSource.setChildrenField? Does not seam to be either of them.
          Maybe an easy example of such a DataSource definition would help people to start of with parent-child client-server interaction.

          Comment


            #6
            Hi guys,

            Taking a step back to provide more context, the primary way of dealing with "nested" records in SmartClient is to treat them as two related DataSources that are linked by foreignKey.

            For example, take "orders" and "items", where logically orders contain items, and there are DataSources "orders" and "items" that are related by foreignKey. You have an interface consisting of a grid bound to the "order" DataSource showing orders and a form next to that grid, also bound to the "order" DataSource, for showing all the fields of the order, and finally a second grid bound to the "items" DataSource.

            In this scenario, the data loaded in the "orders" grid does not contain the data for the "items" for that order - it only has the fields of the order as such (like shipDate). To populate the "items" grid you just call fetchRelatedData() (*see below) on that grid, passing the selected order. You need to make the "items" DataSource capable of receiving an order id as criteria and returning the items from that.

            Likewise if the order is edited and items are also edited, these will be independent DSRequests that your server code needs to deal with. There will be one DSRequest per added/modified item.

            This is the right approach if the number of items is potentially very large and data paging may be required. It's also potentially the more efficient approach if users don't often click through to view the items from most of the loaded orders, since it would then be a waste to load all the items data up front.

            The alternative is to load the items with each order, in advance. In this case the list of items ends up as an attribute/property of each "order" record. In place of fetchRelatedData() to populate the "items" grid, you take the Array of items from the "order" record and call setData() on the "items" grid - there's no server fetch and the "items" DataSource does not need to be capable of fetching. When saving, you call dataSource.updateData() passing the "order" record (which contains the items). The "items" DataSource does not need to be separately capable of "add" or "update".

            In this case there is no foreignKey declaration and the field's type is "items" (the name of the related DataSource) *see below

            Then, actually deliver the items to the browser as part of the order record, in both the SC server and the XML version it's sufficient to just include the data. In the SC server case if your DataSource field matches the name of the getter for the "items" on your POJO (for getItems() field name would be "items" as per Java Bean convention), you're done. For the XML case you want to set multiple:true on the field definition, then deliver the data like this:

            Code:
            <order>
                <shipDate>1976-09-12</shipDate>
                <items>
                     <item>
                         <name>foo</name>
                         ...
                     </item>
                     <item>
                         <name>bar</name>
                         ...
                     </item>
                 </items>
                 ...
            </order>
            For saving, both the RestDataSource and SC Server DataSource are going to send the data along automatically (use dataProtocol:"postMessage" with RestDataSource). For RestDataSource you'll need to parse the XML, validate it and make it the right type and call setters on your beans.

            For the SC server it's sufficient to just declare an Order bean as one of the parameters of a method invoked via DMI, and SmartClient will just fill in the bean after running validation.

            * API currently missing in SmartGWT, look for it in an upcoming nightly.

            Comment


              #7
              status of *fetchRelatedData() is this available now/planned release ?

              thanks very much for your time

              Comment


                #8
                Coming in an upcoming nightly, but probably after Christmas.

                You know it's very easy to reproduce with a call to fetchData() right? Just set up the criteria so that it selects records which have a value for the foreignKey field that match the other record's primary key value.

                Comment


                  #9
                  Originally posted by Isomorphic
                  Coming in an upcoming nightly, but probably after Christmas.

                  You know it's very easy to reproduce with a call to fetchData() right? Just set up the criteria so that it selects records which have a value for the foreignKey field that match the other record's primary key value.

                  Thanks for the quick reply. Still coming up to speed. I'll investigate fetchData...

                  Comment


                    #10
                    Originally posted by bwalsh
                    status of *fetchRelatedData() is this available now/planned release ?

                    thanks very much for your time
                    Build 12-17-2008 included this method.

                    Sanjiv

                    Comment


                      #11
                      setChildrenField

                      Hi, everybody!

                      Is it possible to build TreeGrid for next XML data:

                      <class name="a">
                      <class name="b">
                      <class name="d">
                      </class>
                      </class>
                      <class name="c">
                      </class>
                      </class>

                      via DataSource and setChildrenField?


                      This my code:

                      -------------------------------------------------

                      setRecordXPath("/class");

                      setChildrenField("class");

                      DataSourceTextField Name = new DataSourceTextField("Name", "Name");
                      Name.setValueXPath("@name");

                      -------------------------------------------------

                      In result I have tree that have all nodes of XML, and t have field "name" only for root node.
                      Last edited by gev; 29 Jan 2009, 21:56.

                      Comment


                        #12
                        I am trying out the alternative mentioned in this thread:

                        The alternative is to load the items with each order, in advance. In this case the list of items ends up as an attribute/property of each "order" record. In place of fetchRelatedData() to populate the "items" grid, you take the Array of items from the "order" record and call setData() on the "items" grid - there's no server fetch and the "items" DataSource does not need to be capable of fetching. When saving, you call dataSource.updateData() passing the "order" record (which contains the items). The "items" DataSource does not need to be separately capable of "add" or "update".
                        The thing that is not clear is how do I do the following:

                        take the Array of items from the "order" record' and call setData() on the "items" grid
                        I would be very grateful if someone can explain this alternative and perhaps provide a small code fragment. Thanks.

                        Comment


                          #13
                          Did you already try getAttribute("Address") on the Record?

                          Comment


                            #14
                            You are correct to assume that this is related to the datamodel posted here.

                            A Person record can have multiple Address sub-records. So the Address attribute value is a List not a String. I have tried using getAttributeAsJavaScriptObject to get the Address attribute value. I am not sure how to convert this JavaScriptObject to a ListGridRecord[] so I can call addressesGrid.setData() with the ListGridRecord[].

                            Here is the code I am envisioning and wondering what to replace the "someConversionMethod" with:

                            Code:
                            Record personRecord = ... //The Person Record
                            JavaScriptObject jso = personRecord.getAttributeAsJavaScriptObject("Address");
                            
                            ListGridRecord[] addressRecords = someConversionMethod(jso);
                            ListGrid addressesGrid = ... //Shows list of Addresses for a Person
                            addressesGrid.setData(addressRecords);

                            Comment


                              #15
                              No sooner had I posted last response I figured it out (see below)....
                              Thanks very much for your help.

                              Code:
                              Record personRecord = ... //The Person Record
                              JavaScriptObject jso = personRecord.getAttributeAsJavaScriptObject("Address");
                              
                              JsArray jsa = jso.cast();
                              
                              int cnt = jsa.length();
                              ListGridRecord[] addressRecords = new ListGridRecord[cnt];
                              for (int i=0; i<cnt; i++) {
                                  ListGridRecord rec = new ListGridRecord(jsa.get(i));
                                  addressRecords[i] = rec;
                              }
                              
                              ListGrid addressesGrid = ... //Shows list of Addresses for a Person
                              addressesGrid.setData(addressRecords);

                              Comment

                              Working...
                              X