Announcement

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

    JPA: lookup field values in other table by foreignKey

    I am using SmartGWTEE 2.3.

    I'm using the com.isomorphic.jpa.GAEJPADataSource and am a bit stumped on how to do something which I thought would be simple. I would greatly appreciate some help.

    I have two classes

    class A {
    String aId;
    String aValue1;
    String aValue2;
    }

    class B {
    String bId;
    String aId;
    }


    In my datasource definition for B, I would like to be able to define

    bId, aId, aValue1, aValue2

    So, aValue1 and aValue2 come from A based on the foreignKey aId.

    Is there an easy way to do this using a JPA DataSource? I realize I could create a DMI DataSource but that seems like overkill for such a simple operation.

    #2
    You can use dataSourceField.valueXPath to extract values from the related bean. See this sample.

    Comment


      #3
      It looks like I'm out of luck in using the XPath solution on Google App Engine, unless I am reading this exception incorrectly. It looks like JXPath is accessing a blacklisted package (java.io). Perhaps there is a way to tell JXPath not to do this, but I did not dig that deep.

      I am using the nightly build.

      Code:
      com.isomorphic.js.UnconvertableException: java.security.AccessControlException: access denied (java.io.FilePermission E:\Program Files\Java\jre6\lib\jxpath.properties read)
      	at java.security.AccessControlContext.checkPermission(Unknown Source)
      	at java.security.AccessController.checkPermission(Unknown Source)
      	at java.lang.SecurityManager.checkPermission(Unknown Source)
      	at com.google.appengine.tools.development.DevAppServerFactory$CustomSecurityManager.checkPermission(DevAppServerFactory.java:166)
      	at java.lang.SecurityManager.checkRead(Unknown Source)
      	at java.io.File.exists(Unknown Source)
      	at org.apache.commons.jxpath.JXPathContextFactory.findFactory(JXPathContextFactory.java:191)
      	at org.apache.commons.jxpath.JXPathContextFactory.newInstance(JXPathContextFactory.java:104)
      	at org.apache.commons.jxpath.JXPathContext.getContextFactory(JXPathContext.java:439)
      	at org.apache.commons.jxpath.JXPathContext.newContext(JXPathContext.java:416)
      	at com.isomorphic.datasource.DataSource.getProperties(DataSource.java:1126)
      	at com.isomorphic.datasource.DataSource.getProperties(DataSource.java:1057)
      	at com.isomorphic.datasource.DataSourceBeanFilter.filter(DataSourceBeanFilter.java:174)

      Comment


        #4
        Here's that error reproduced in the sample from smartgwtee-2.3\samples\ds-gae

        I modified the city data source to include the country name. Of course, perhaps I am doing something wrong.

        Code:
        <DataSource ID="city_DataSource" serverConstructor="com.isomorphic.jpa.GAEJPADataSource"
        	beanClassName="com.smartgwt.sample.server.City">
        	<fields>
        		<field name="cityId" type="text" hidden="true" primaryKey="true" />
        		<field name="cityName" type="text" title="City" required="true" />
        		<field name="countryId" type="text" title="Country" canEdit="false"
        			foreignKey="country_DataSource.countryId" />
        		<field name="country" type="country_DataSource" javaClass="com.smartgwt.sample.server.Country" />
        		<field name="countryName" type="text" title="Country Name"
        			valueXPath="country/countryName" />
        	</fields>
        </DataSource>

        Comment


          #5
          With some Googling, we're not seeing either a solution or a report of a similar problem. Certainly JXPath should be able to function by loading it's properties via the classpath instead, and a more recent version of the library may correct the problem (if you confirm this, please let us know).

          The alternative is to just add a DMI. You can retrieve the fetched POJO from the DSResponse after calling execute(), and replace them with Maps derived by a call to DataSource.getProperties() plus a manual call to the sub-bean to get the countryName and add it as an additional key.

          You could also make the pattern generic by detecting your own valueXPath-like property on DataSourceFields.

          Comment


            #6
            I got JXPath to run on GAE by setting the following system property as a VM arg

            Code:
             -Dorg.apache.commons.jxpath.JXPathContextFactory=org.apache.commons.jxpath.ri.JXPathContextFactoryReferenceImpl
            It's still not working properly yet, but it is past the AccessControlException

            Comment


              #7
              This is where I'm stuck now. The city class contains a Country object, but XPath can't access it.

              Code:
              === 2011-01-17 15:56:08,265 [62-6] INFO  DataSource - Couldn't get value at valueXPath: country/countryName for datasource: city_DataSource - ignoring.  Actual error: org.apache.commons.jxpath.JXPathNotFoundException: No value for xpath: country/countryName
              I checked in JXPath to see what this was being caused by and got http://commons.apache.org/jxpath/xre...eImpl.html#352

              Code:
              352     public Object getValue(String xpath, Expression expr) {
              353         Object result = expr.computeValue(getEvalContext());
              354         if (result == null) {
              355             if (expr instanceof Path && !isLenient()) {
              356                 throw new JXPathNotFoundException("No value for xpath: "
              357                         + xpath);
              358             }
              359             return null;
              360         }
              361         if (result instanceof EvalContext) {
              362             EvalContext ctx = (EvalContext) result;
              363             result = ctx.getSingleNodePointer();
              364             if (!isLenient() && result == null) {
              365                 throw new JXPathNotFoundException("No value for xpath: "
              366                         + xpath);
              367             }
              368         }
              369         if (result instanceof NodePointer) {
              370             result = ((NodePointer) result).getValuePointer();
              371             if (!isLenient() && !((NodePointer) result).isActual()) {
              372                 // We need to differentiate between pointers representing
              373                 // a non-existing property and ones representing a property
              374                 // whose value is null.  In the latter case, the pointer
              375                 // is going to have isActual == false, but its parent,
              376                 // which is a non-node pointer identifying the bean property,
              377                 // will return isActual() == true.
              378                 NodePointer parent =
              379                     ((NodePointer) result).getImmediateParentPointer();
              380                 if (parent == null
              381                     || !parent.isContainer()
              382                     || !parent.isActual()) {
              383                     throw new JXPathNotFoundException("No value for xpath: "
              384                             + xpath);
              385                 }
              386             }
              387             result = ((NodePointer) result).getValue();
              388         }
              389         return result;
              390     }
              I attached the source and traced in, and the exception is being thrown on line 383.

              Comment


                #8
                Hmm. So further investigating shows that XPath thinks that the country field is null (and I guess the exception says that as well).

                I wonder if this has to do with the JPA/GAE annotations?

                I'm going to try and pass regular beans using DMI rather than using com.isomorphic.jpa.GAEJPADataSource and see if I can figure out what is going on.

                Comment


                  #9
                  I got valueXPath working, but not with the GAE-DS sample. The XPath error was pretty accurate, the object was not set. The simple test for this is to display the object field.

                  I have a further question. If I want to show a list (multiple) values in an XPath field, is it possible?

                  Code:
                    <field name="objects" type="object_DataSource" javaClass="com.sample.MyObject" multiple="true" >
                  
                   <field name="objectName" type="text" valueXPath="objects/name" multiple="true" >
                  I'm guessing "no" since objects/name is not the correct reference. A correct reference is objects[1]/name, but how can I iterate across all n elements of "objects"?

                  Is this possible? If not, it might be a nice feature to add.

                  Comment


                    #10
                    To partially answer my own question, I guess one way to achieve this is to write my own JXPathContext and define the semantics for an array lookup. I could then define something like:

                    Code:
                    <field name="objectName" type="text" valueXPath="objects[*]/name" multiple="true" >

                    Comment


                      #11
                      If you declare the field as multiple="true", we use JXPathContext.iterate() and will retrieve multiple values if the expression selects more than one value.

                      Comment


                        #12
                        Can you please give an example where iterate() would work for the case above ( valueXPath="objects[x]/name" ) - I'm not clear on how that would work.

                        Comment


                          #13
                          Sorry, don't really understand the question.

                          If "object" refers to a Collection, JXPath will apply the remaining path (/name) to each item in the collection. The final result will be multiple values, and if you declare the field multiple="true" in your DataSource file, we'll retrieve the multiple values.

                          Retrieving multiple values happens to be done (internally) via JXPath.iterate(). You don't need to know this detail unless you plan to use JXPath directly - but if you are, please refer to the JXPath docs.

                          Comment


                            #14
                            Great, thank you for explaining.

                            Comment

                            Working...
                            X