Announcement

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

    #16
    Originally posted by Isomorphic View Post
    "treating as a simple field" means treating as an ordinary type="text" field as previously discussed.
    Gee, I wish it were simple. If I set the field in the Datasource xml.ds file to type="text", I get the same behavior as if I had deleted it or set it to "int". So by having it be some custom gibberish must cause some other code path to be executed.

    In your log, before the NumberFormatException, when we attempt to call getParentDomainId() there is a NullPointerException. That appears to be the root cause so you should see why your getter would crash.
    Well, that would be because the node's parent is null. The parentDomain and the related parentDomainId column's are marked as optional/nullable so a null doesn't cause the DB any grief. When the type was "junk", it seems to be able to handle a null parentDomainId field without a near-fatal null-pointer exception.

    So is it possible that the "int/long" type processing for ids don't handle nulls very well? That would seem to be my experience. It would seem the only way to work around that is to treat the domainParentId as junk (where nulls seem to be tolerated, but it would seem to be relying on some unreliable behavior) or use root id of 0 or some other reserved Id and then hack up my getDomainParent and related accessors such that they treat a domainParentId as null.

    I'll try to rework using 0 and hack up my accessors, but I'll have to go back to our original database schema and change things from expecting null column values for that field.

    Comment


      #17
      Again what appears to be happening is that your getParentDomainId() method is actually throwing a NullPointerException. It's fine for it to just return null, but the logs say that it is actually throwing a NullPointerException.

      All the rest of your problems, as far as we can tell, start here.

      Comment


        #18
        Originally posted by Isomorphic View Post
        Again what appears to be happening is that your getParentDomainId() method is actually throwing a NullPointerException. It's fine for it to just return null, but the logs say that it is actually throwing a NullPointerException.

        All the rest of your problems, as far as we can tell, start here.
        I wish that was where it started.

        So I went back to square one, changed things back to use a tree root of 0 and int types for the parentId. I'm back at nothing working at all. So I'm really missing something. About 3 or 4 posts back, the instructions were:
        When you use a rootValue that isn't null with a JPA field of type int or Integer, as you saw, JPA will reject any kind of marker value like 0 or 1 if there's no such entity. There are a couple of approaches here:

        1. create a JPA entity to serve as the official root entity.

        2. add getters and setters for parentIdField without adding any JPA declarations, so that calls to getParentIdField() on a root level entity return the rootValue, and setParentIdField(rootValue) sets the relation to null / unlinked.
        So right now, JPA is rejecting my parentDomainId saying that 0 doesn't exist. I have not been able to do #1 because JPA/Hibernate just flat out refuses to add a node where the parentId does not exist in the DB. I tried removing the JPA annotations for the domainParentId fields and then modifying the getDomainParentId accessor to return the "parent objects" id or 0 if null. I changed the setDomainParentId setting to set the parentDomain field to null if it was passed a 0 or null. These do not work/solve the issue.

        So I'm translating these instructions incorrectly or something else is wrong. If I leave the declaration of an int parentDomainId (sans JPA) Hibernate somehow thinks I need two parentDomainId columns and still fails. If I remove the declaration, Hibernate, etc can't find it. Nothing I seem to do in the getters/setters seems to influence what JPA/Hibernate decide to insert into the database.

        The only thing that seemed to work depended upon some unexplained behavior due to using a custom type in the ds.xml datasource file. I've seen the JPA examples and they do seem to work - but they don't create a tree from scratch - they use a demo database all created for them in advance.

        I'm about to just give up. I'd be happy to send you my files. I've pretty much stripped them down to the bare minimum and crammed them into your JPA sample app. Of if you have something/anything that works, I'd gladly like to see that.

        Comment


          #19
          Sorry you're having trouble with JPA - you've made it clear (even if you don't recognize it yet :) that you are not actually having a problem with SmartGWT.

          We'd recommend one of two approaches:

          1. Run some tests with just JPA (no SmartGWT) until you have figured out how to insert a root entity properly and/or verified your accessor methods do not throw NullPointerException. Then return to SmartGWT and things will go smoothly

          2. Abandon JPA and just use SQLDataSource. This is the recommended approach, because it avoids the unnecessary complexity of JPA, so you don't get tied in knots with inexplicable JPA exceptions

          Comment


            #20
            Ummm, I am not having a problem with JPA. JPA/Hibernate has no problem with inserting a null parentId and working with that to create a hierarchy that is easily translatable to a tree.

            My problem has been mapping that JPA logic to smartgwt and its enterprise data sources without writing custom data management code. That was one of the reasons I was evaluating smartgwt-ee and not just the open source version (or other gwt and java/js frameworks).

            In fact, I had a complete working version in SmartGWT-EE with null as the tree root. My only problem was that to get that to work, I had to use a bogus type field in my ds.xml file. It was also not the recommended approach as suggested by Isomorphic.

            So in my effort to use an integer value as the root, I have gotten absolutely nowhere. At best I can get a table row with a null parentId but the treegrid can't render it because the id is not 0 (int) and is instead a null. I've completely bastardized working JPA code to try and work around it but have gotten no where. Even when I did get a working version, I had to add a parentId field to my JPA just to get it to work.

            So if someone can show me how to get a JPA bean with the following fields and get it to work with a TreeGrid (create root nodes using addData), I'd be ecstatic. As it is, I've burned a whole week on this.

            Code:
            @Entity
            @Table(name = "T_DOMAIN")
            public class Domain {
            	@Id
            	@GeneratedValue(strategy = IDENTITY)
            	@Column(nullable = false)
            	private int id;
            
            	@Basic
            	private String name;
            
            	@ManyToOne(fetch = LAZY, optional = true)
            	private Domain parentDomain;
            	
            	@OneToMany(mappedBy = "parentDomain", fetch = LAZY)
            	private Collection<Domain> subDomains;
            }
            This is an obvious, basic tree structure defined in JPA. It easily works if I create the server logic to create the nodes and persist them. What I seem to have failed to grasp is how to get smartgwt-ee's datasources to work with it.

            For reference, the following JPA code worked for me, along with a null tree root.
            Code:
            @Entity
            @Table(name = "T_DOMAIN")
            public class Domain {
            	@Id
            	@GeneratedValue(strategy = IDENTITY)
            	@Column(nullable = false)
            	private int id;
            	@Basic
            	private String name;
            
            	@Column(name = "parentDomainId", nullable=true)
            	private Integer parentDomainId;
            
            	@ManyToOne(fetch = LAZY, optional = true)
                    @JoinColumn(name = "parentDomainId", nullable=true, insertable=false, updatable=false)
            	private Domain parentDomain;
            	
            	@OneToMany(mappedBy = "parentDomain", fetch = LAZY)
            	private Collection<Domain> subDomains;
            }
            and its ds.xml (note that type = "Integer" is bogus and I get a warning when using it this way. type = "text" or no type specified results in a non-working treegrid (numberformatexception and nullpointerexception results in null parentId fields).
            Code:
            <DataSource
                ID="domain_DataSource"
                serverConstructor="com.isomorphic.jpa.JPADataSource"
                beanClassName="mobi.droidcloud.mgmt.model.Domain"
                dropExtraFields="true"    
                >
                <fields>
                    <field name="id"    type="sequence" hidden="true"   primaryKey="true" />
                    <field name="name"  type="text"     title="Name"    required="true"   />
                    <field name="parentDomainId" type="Integer" title="Parent Domain Id" canEdit="false" 
                    	foreignKey="id" />
                </fields>
            </DataSource>
            Last edited by bjvetter; 23 Jul 2012, 22:22.

            Comment


              #21
              We've already answered this question - see post 9.

              On that post, so far as we can tell you have not tried strategy #2 (even though it is very simple).

              On strategy #1, your JPA code is throwing a NullPointerException, but you don't seem to have investigated that. Certainly it would be trivial to add a try..catch block to the getter to see why it throws an exception.

              So we're left puzzled as to why you are posting more about the same problems, without trying the solutions..

              Just to review - the format of data you need to deliver to a tree is very well covered in docs that you've read. How the JPA/Hibernate/bean connector retrieves data from JPA entities is very well covered - if you haven't seen this it's in the QuickStart under DMI - Returning Data and continues into JavaDoc for DataSource.getProperties().

              So you should have everything you need.. just need to apply it. Let us know if we can help once you've applied the recommended approaches.

              Comment


                #22
                Originally posted by Isomorphic View Post
                We've already answered this question - see post 9.

                On that post, so far as we can tell you have not tried strategy #2 (even though it is very simple).

                On strategy #1, your JPA code is throwing a NullPointerException, but you don't seem to have investigated that. Certainly it would be trivial to add a try..catch block to the getter to see why it throws an exception.

                So we're left puzzled as to why you are posting more about the same problems, without trying the solutions..

                Just to review - the format of data you need to deliver to a tree is very well covered in docs that you've read. How the JPA/Hibernate/bean connector retrieves data from JPA entities is very well covered - if you haven't seen this it's in the QuickStart under DMI - Returning Data and continues into JavaDoc for DataSource.getProperties().

                So you should have everything you need.. just need to apply it. Let us know if we can help once you've applied the recommended approaches.
                I am trying my best to not be offended and pissed off. I have tried many things including your suggestions. They haven't worked or they weren't clear enough such that I failed to implement them properly. I didn't detail every last bit of my work. So I did everything all over again, this time keeping a long diary of what I found. Here you go:

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

                First attempt:

                remove JPA annotated attribute for parentDomainId. Replace with {get,set}ParentDomainId() accessors. get tests parentDomain for null and if so, returns the id of the parent object. set tests the incoming id and if 0, sets the parentDomain object to null and if non-zero, looks up the object and sets the parent object to the found object.

                changed the tree root to be 0 (from null). changed the type = IntegerJunk to type = int. changed addData calls that create root objects to use 0 instead of null.

                Result: get error popup in the UI with the following error:

                org.hibernate.QueryException: could not resolve property: parentDomainId of: mobi.droidcloud.mgmt.model.Domain [select _Domain from mobi.droidcloud.mgmt.model.Domain _Domain where _Domain.parentDomainId = :p0]

                in the server log file, I see the following exception:

                Got exception while executing. Transaction will be rolled back.
                java.lang.IllegalArgumentException: org.hibernate.QueryException: could not resolve property: parentDomainId of: mobi.droidcloud.mgmt.model.Domain [select _Domain from mobi.droidcloud.mgmt.model.Domain _Domain where (lower(_Domain.name) = lower(:p0)) and _Domain.parentDomainId = :p1]
                at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1201)
                at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1147)
                at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:275)
                at com.isomorphic.jpa.JPADataSource.executeFetch(JPADataSource.java:753)

                this is obviously due to the fact that my ds.xml file refers to the property domainParentId. while I have getters/setters in my class for this property, I do not have an explicit field definition. Not only did I remove the JPA annotations for the field, I removed the entire field.

                Miraculously, hibernate did add something to the database even with these errors. There is a single record with name of "Global" (the name value in the record passed to addData) and a domainParentId of null.

                On the positive side, when looking at the RPCs, any "null" parentDomainId values are returned as 0. But due to various errors or mappings in the server, hibernate/jpa continue to place a null in the database column.

                Second attempt:

                Because of the complaint of no domainParentId field, the next step I added back a private field parentDomainId. I did not add any JPA annotations. It is nothing but a placeholder. I changed the getter/setter code to mirror the parentDomain object's id into this field or 0 if the parentDomain is null.

                Result: No error dialog this time, but …

                I get a warning message in my server log:

                === 2012-07-24 11:20:19,502 [l0-0] WARN JPADataSource - [builtinApplication.domain_DataSource_add] Can not find related object with id=0. Setting to NULL.

                There were no other errors or warnings in the log. That said, there was nothing rendered in the tree. There was a single record in the database table with the proper name, an id of 1, and two columns labeled "PARENTDOMAINID" and "PARENTDOMAIN_ID". The first has a value of 0, the second a value of null.

                I reloaded the page/app and this time the TreeGrid rendered with a single node "Global". I looked at the RPC results of the fetches and they returned an object with a name "Global", and id of 1, and an parentDomainId of null. I checked my accessor (getParentDomainId()) and it is not throwing an exception and just before it returns, it logs a message that it is returning a 0 because the parentDomain object is null. So I have no idea why the smartgwt RPC code has chosen to return null for parentdomainId, but it does. Perhaps it is looking at the other parentDomain object and its parentDomainId field. But my datasource explicitly uses parentDomainId and not parentDomain. Nothing is calling my getParentDomain() accessor - so if it is being captured, it must be through a reflection interface at the bean level.

                Since the Global node showed up in the TreeGrid, I decided to see if I could add a child node to it. When I send the addData request with the parent node being my global node, I get a numberformatexception:

                === 2012-07-24 11:49:11,697 [0-19] WARN DataSource - [builtinApplication.domain_DataSource_add] Couldn't set property 'parentDomainId' for datasource 'domain_DataSource'. Actual error: java.lang.NumberFormatException

                Contrary to the opinions of a previous Isomorphic response, this is not due to a null pointer exception being thrown from my getter (or setter). My getter was called later. My setter was never called. The sequence in my log file is:
                === 2012-07-24 11:49:11,692 [0-19] DEBUG JPADataSource - [builtinApplication.domain_DataSource_add] Executing add.
                Hibernate:
                select
                domain0_.id as id25_,
                domain0_.name as name25_,
                domain0_.parentDomain_id as parentDo4_25_,
                domain0_.parentDomainId as parentDo3_25_
                from
                T_DOMAIN domain0_
                where
                domain0_.id=?
                === 2012-07-24 11:49:11,697 [0-19] WARN DataSource - [builtinApplication.domain_DataSource_add] Couldn't set property 'parentDomainId' for datasource 'domain_DataSource'. Actual error: java.lang.NumberFormatException
                Hibernate:
                insert
                into
                T_DOMAIN
                (id, name, parentDomain_id, parentDomainId)
                values
                (null, ?, ?, ?)
                Hibernate:
                call identity()
                === 2012-07-24 11:49:11,707 [0-19] DEBUG RPCManager - Content type for RPC transaction: text/plain; charset=UTF-8
                === 2012-07-24 11:49:11,707 [0-19] DEBUG JPADataSource - Committing transaction for 1 queued operation(s).
                === 2012-07-24 11:49:11,708 [0-19] DEBUG RPCManager - non-DMI response, dropExtraFields: true
                ***************** getparentDomainId ********************
                ----------------- getparentDomainId -----------------
                parentDomain is null
                ----------------- getparentDomainId -----------------
                Returning 0
                === 2012-07-24 11:49:11,710 [0-19] DEBUG JPADataSource - Releasing entity manager.

                Looking at the RPC calls, the add passed the following DS request:

                {
                "dataSource":"domain_DataSource",
                "operationType":"add",
                "componentId":"isc_TreeGrid_0",
                "data":{
                "name":"new-domain",
                "parentDomainId":1
                },
                "showPrompt":true,
                "oldValues":{
                "name":"new-domain",
                "parentDomainId":1
                },
                "requestId":"domain_DataSource$6272"
                }

                The response was:
                [
                {
                data:{
                parentDomainId:null,
                id:2,
                name:"new-domain"
                },
                invalidateCache:false,
                isDSResponse:true,
                operationType:"add",
                queueStatus:0,
                status:0
                }
                ]

                The database now has two entries. The original Global with a ParentDomainId of 0 and a ParentDomain_Id of null. It also has "new-domain" with nulls for both ParentDomainId and ParentDomain_ID. Remember from my earlier statement: my setter for parentDomainId was never called, so the field was never set - that's probably why ParentDomainId is also null.

                When refreshing the tree, only the Global node shows up as there is no parentDomainId setting for it.

                Third Attempt:

                Since the second attempt didn't work and I now have two separate parentDomainId columns in my database and in my app, that perhaps there is some way to share a single column. Unfortunately, I don't know how to do that without adding JPA annotations to the field I added - which is directly opposite of what I've been told by Isomorphic to do - refresher: "add getters and setters for parentIdField without adding any JPA declarations, so that calls to getParentIdField() on a root level entity return the rootValue, and setParentIdField(rootValue) sets the relation to null / unlinked." So either I am misinterpreting this statement or it is a dead-end.

                Not wanting to stop, I see two options:

                1) remove the JPA declarations from my ManyToOne relationship to parent domains and add them back for the parentDomainId field.
                2) add some JPA declarations that let me share the parentDomainId field

                In the case of #1, it is completely counter to what we have done. We have a boat load of JPAs that represent our persistent objects. Tossing out the relationships and the auto-generated support for those within Hibernate, etc isn't in the spirit of working with JPA either. Switching to using raw SQL as some keep suggesting isn't in our plans. Our application isn't just a UI app - it has backend processes that are using/exercises those relationships.

                So I tried #2.

                I marked up the two fields to share the same column using the following JPA annotations:

                @Column(name = "parentDomainId")
                private Integer parentDomainId;

                @ManyToOne(fetch = LAZY, optional = true)
                @JoinColumn(name = "parentDomainId", insertable=false, updatable=false)
                private Domain parentDomain;

                The point here was to keep the JPA/Hibernate code from inserting/updating using the parentDomain field and putting the control in the hands of the parentDomainId field and its setters. I made changes to the getter/setters of the parentDomainId field to guarantee that the ID is set to 0 if by some chance it is passed a null as opposed to deferring to the id value in the parentDomain instance. I also explicitly removed nullable in the column definition.

                Result:

                The ui failed when loading with a db constraint error:

                org.hibernate.exception.ConstraintViolationException: could not insert: [mobi.droidcloud.mgmt.model.Domain]

                You can see where this is going. In my server log, the first problem I see is the familiar:

                === 2012-07-24 12:36:09,998 [l0-2] WARN JPADataSource - [builtinApplication.domain_DataSource_add] Can not find related object with id=0. Setting to NULL.

                Not long after that call, I see a call to my setParentDomainId() setter with a null value. The setter replaces the NULL with a value of 0. That doesn't seem to matter. Not long after, there is a fetch followed by an insert. The insert gets the constraint violation. Here's the logs between those two points:

                === 2012-07-24 12:43:12,835 [l0-2] WARN JPADataSource - [builtinApplication.domain_DataSource_add] Can not find related object with id=0. Setting to NULL.
                === 2012-07-24 12:43:12,835 [l0-0] DEBUG DSRequest - [builtinApplication.domain_DataSource_fetch] Clobbering existing FreeResourcesHandler of type 'com.isomorphic.jpa.JPADataSource' with a 'com.isomorphic.jpa.JPADataSource'
                === 2012-07-24 12:43:12,836 [l0-0] DEBUG JPADataSource - [builtinApplication.domain_DataSource_fetch] Executing fetch.
                === 2012-07-24 12:43:12,837 [l0-0] DEBUG JPADataSource - [builtinApplication.domain_DataSource_fetch] Query string: select _Domain from Domain _Domain where _Domain.parentDomainId = :p0
                ***************** setparentDomainId() ********************
                parentDomainId is null, resetting it to 0
                set parentDomainId to 0
                === 2012-07-24 12:43:12,863 [l0-0] DEBUG JPADataSource - [builtinApplication.domain_DataSource_fetch] Parameter p0: 0
                Hibernate:
                select
                domain0_.id as id25_,
                domain0_.name as name25_,
                domain0_.parentDomainId as parentDo3_25_
                from
                T_DOMAIN domain0_
                where
                domain0_.parentDomainId=?
                === 2012-07-24 12:43:12,867 [l0-0] INFO DSResponse - [builtinApplication.domain_DataSource_fetch] DSResponse: List with 0 items
                === 2012-07-24 12:43:12,867 [l0-0] DEBUG RPCManager - Content type for RPC transaction: text/plain; charset=UTF-8
                === 2012-07-24 12:43:12,867 [l0-0] DEBUG JPADataSource - Committing current transaction.
                === 2012-07-24 12:43:12,868 [l0-0] DEBUG RPCManager - non-DMI response, dropExtraFields: true
                === 2012-07-24 12:43:12,870 [l0-0] DEBUG JPADataSource - Releasing entity manager.
                Hibernate:
                insert
                into
                T_DOMAIN
                (id, name, parentDomainId)
                values
                (null, ?, ?)
                === 2012-07-24 12:43:12,884 [l0-2] DEBUG JPADataSource - [builtinApplication.domain_DataSource_add] Marking transaction for roll back.
                === 2012-07-24 12:43:12,884 [l0-2] DEBUG JPADataSource - [builtinApplication.domain_DataSource_add] Got exception while executing. Transaction will be rolled back.
                javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not insert: [mobi.droidcloud.mgmt.model.Domain]
                at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1214)
                at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1147)
                at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1153)
                at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:678)
                at com.isomorphic.jpa.JPADataSource.executeAdd(JPADataSource.java:840)

                Caused by: org.hibernate.exception.ConstraintViolationException: could not insert: [mobi.droidcloud.mgmt.model.Domain]
                at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
                at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
                at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.java:64)
                at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2345)

                Caused by: java.sql.SQLException: Integrity constraint violation - no parent FK2FD97F0FB7B327CF table: T_DOMAIN in statement [insert into T_DOMAIN (id, name, parentDomainId) values (null, ?, ?)]
                at org.hsqldb.jdbc.Util.throwError(Unknown Source)
                at org.hsqldb.jdbc.jdbcPreparedStatement.executeUpdate(Unknown Source)
                at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.java:57)

                If you notice, besides my setter being called early on after the failure, there was no call to my getter. It seems plausible that some code just plugged away and inserted a null regardless of what I put in my JPA bean. And there were no calls into the setters/getters for the parentDomain field as well. I have added log messages and thus they were never called. There are no null pointer exceptions being thrown in my accessors. It is somewhere in the JPA/Datasource code.

                So at this point, I'm pretty much done. I don't see how I can prevent a null being inserted using my JPA bean's setter methods.

                The only thing that has worked has been to set the tree root value as null, set up nullable parentDomainId columns, and inexplicably set the type in the ds.xml file for the parentDomainId to an unknown value. It is only with the latter do I get my little test app to work. I have read the posts that this is supposed to be the same a "text" (the default), but the behavior is different. If you don't believe me, I'll send you every bit of my source code.

                Comment


                  #23
                  On your first/second attempt, it looks like you may have forgotten to mark the field as "transient" (see this overview for example). Otherwise JPA persists things automatically. Still looking through the rest of your results but this problem is what would prevent strategy #2 from working.

                  Comment


                    #24
                    Looking through the rest of your attempts, looks like they are all obviated by just preventing JPA from persisting the parentDomainId field, which is really just intended to be a pair of getters/setters and not a separately persisted field. Sorry that we said "no JPA annotations" it would have been clearer to specify no annotations that would cause the field to be stored.

                    However you are reporting some bizarre results with your getter being called yet another value being returned (we have no code that could do this) and with your setter not being called. In the absence of full logs and/or code, our best guess is something like a camel caps issue making the setter inapplicable for the field.

                    For example if the field is declared as parentDomainId, setParentDomainID() would not be called (the "d" should be lowercase).

                    Or it could be just confusion from trying so many different things and still having old code / settings around.

                    In general, your posts contain a lot of prose paired with logs that have been trimmed or aren't shown with related source. When you do this we can't catch things like typos, which are often the problem. It's better to show the actual logs and code than to explain it all in prose, and that's easier for you as well.

                    In this particular instance, you've posted some new logs that once again result in a mysterious NumberFormatException *but* you trimmed off the area of the log that previously revealed a NullPointerException as the root cause. Even if the NPE is actually gone, we still need to see the preceding logs so we can spot any similar error that is really the root cause.

                    Comment


                      #25
                      One more clarification regard the "type" attribute - when you are using JPA's implicit relationships with no concrete field declared to hold the foreign entity's ID, *the field has no particular type*. It's not really the type of the key, and not the type of the related entity either, because it is providing a mapping between the two.

                      The docs and samples for JPA relations show the type being omitted, and this is the right approach - no need to add a type declaration with some fake type value. In the docs we're adding an explicit statement that this is the right approach.

                      You can and should add a type declaration if you have a normal field that actually maps directly to a persistent JPA field of a specific atomic type.

                      Comment


                        #26
                        On your first/second attempt, it looks like you may have forgotten to mark the field as "transient" (see this overview for example). Otherwise JPA persists things automatically. Still looking through the rest of your results but this problem is what would prevent strategy #2 from working.
                        I went back and retried that approach with @Transient in front of the parentDomainId field. So the two fields in particular in the JPA bean look like the following:
                        Code:
                        	@Transient
                        	private Integer parentDomainId;
                        
                        	@ManyToOne(fetch = LAZY)
                        	private Domain parentDomain;
                        Unfortunately this doesn't work either. I get an exception from Hibernate due to not being able to find the parentDomainId property. The text is:

                        java.lang.IllegalArgumentException: org.hibernate.QueryException: could not resolve property: parentDomainId of: mobi.droidcloud.mgmt.model.Domain [select _Domain from mobi.droidcloud.mgmt.model.Domain _Domain where (lower(_Domain.name) = lower(:p0)) and _Domain.parentDomainId = :p1]

                        Perhaps Hibernate has some special issue with transient data fields? Or is it not expecting someone to use transient in a query?

                        I will attach the entire log so you can look at it.

                        On the positive side, there is only a single PARENTDOMAIN_ID column in the database.

                        Originally posted by Isomorphic View Post
                        However you are reporting some bizarre results with your getter being called yet another value being returned (we have no code that could do this) and with your setter not being called. In the absence of full logs and/or code, our best guess is something like a camel caps issue making the setter inapplicable for the field.

                        For example if the field is declared as parentDomainId, setParentDomainID() would not be called (the "d" should be lowercase).
                        I saw that in an older post and did look for it. I don't see that as the issue.
                        Originally posted by Isomorphic View Post
                        Or it could be just confusion from trying so many different things and still having old code / settings around.
                        Possible. But in all of these variations, I haven't had to muck with the client code after switching it to use 0 for the root instead of null. I've looked over all of the places I use the parentDomainId property in the records and believe they are all using either 0 or an int value returned in an existing TreeNode record. There is some untested code in the setParentDomainId() setter that could be wrong, but it has yet to be executed since I have yet to have it be called with anything but a null/0 parentDomainId as a parameter.
                        Originally posted by Isomorphic View Post
                        In general, your posts contain a lot of prose paired with logs that have been trimmed or aren't shown with related source. When you do this we can't catch things like typos, which are often the problem. It's better to show the actual logs and code than to explain it all in prose, and that's easier for you as well.
                        I can easily fix that. I've attached the 3 main source files (client, JPA bean, and the datasource xml file). I also included the log file for the latest problem with the @Transient
                        Originally posted by Isomorphic View Post
                        In this particular instance, you've posted some new logs that once again result in a mysterious NumberFormatException *but* you trimmed off the area of the log that previously revealed a NullPointerException as the root cause. Even if the NPE is actually gone, we still need to see the preceding logs so we can spot any similar error that is really the root cause.
                        I promise you, I didn't trim off any NullPointerException. There was nothing in the log that contained a NullPointerException. If there was one, it was caught by something and not written to the log. I've attached the server log file for the latest. I will go back and recreate some of the other "versions" of these.
                        Attached Files

                        Comment


                          #27
                          Originally posted by Isomorphic View Post
                          One more clarification regard the "type" attribute - when you are using JPA's implicit relationships with no concrete field declared to hold the foreign entity's ID, *the field has no particular type*. It's not really the type of the key, and not the type of the related entity either, because it is providing a mapping between the two.

                          The docs and samples for JPA relations show the type being omitted, and this is the right approach - no need to add a type declaration with some fake type value. In the docs we're adding an explicit statement that this is the right approach.

                          You can and should add a type declaration if you have a normal field that actually maps directly to a persistent JPA field of a specific atomic type.
                          So if I remove the type="junk", I start getting nullpointerexceptions and numberformatexceptions.

                          The nullpointerexception I believe is due to the root node's parentId is null (in this incarnation, I'm using setTreeRootValue((Integer)null)) but it isn't clear. I can't peer into that code. I do see that my setParentDomainId() setter is getting called with null.

                          On the initial node create/add though, this isn't fatal. I ended up with a single row in the database with the name "Global" and a parentDomainId of null.

                          However, when I send a subsequent addData request to add a node underneath that root node (it is passing a parentDomainId of 1 to the server verified in the RPC DSRequest for the add), I see a numberformatexception. There is no nullpointerexception at that time in my log. If it is happening, I don't see it. One does occur about 10ms later. It happens after the insert transaction was committed. While no expert here, I suspect it is generated when the datasource prepares its results to send back. I see in the response for that addData request that there is no parentDomainId in it. That RPC request/response if you care looks like the following:

                          {
                          "dataSource":"domain_DataSource",
                          "operationType":"add",
                          "componentId":"isc_TreeGrid_0",
                          "data":{
                          "name":"new-domain",
                          "parentDomainId":1
                          },
                          "showPrompt":true,
                          "oldValues":{
                          "name":"new-domain",
                          "parentDomainId":1
                          },
                          "requestId":"domain_DataSource$6274"
                          }

                          And the response:
                          [
                          {
                          data:{
                          id:2,
                          name:"new-domain"
                          },
                          invalidateCache:false,
                          isDSResponse:true,
                          operationType:"add",
                          queueStatus:0,
                          status:0
                          }
                          ]

                          Then I performed a new "root node" insert with the parentDomainId set to null. This time there was no numberformatexception, just the nullpointerexception after the db insert was committed. I'm again guessing that this occurred during the response generation.

                          So it would seem that I only get the numberformatexception if the parentDomainId provided via addData() is non-null.

                          The log file was attached as nullroot-notype-log.txt

                          Now if I add the type="gibberish" back to my datasource xml, I get a bunch of warnings that the type isn't understood and that it was using the default.

                          === 2012-07-24 17:25:52,742 [l0-5] WARN BasicDataSource - Related data source 'domain_DataSource' does not match declared field type 'Integer' for field 'parentDomainId'. Treating as simple field.

                          At that point, everything works - no more exceptions. If the parentDomainId is non-null, it does provide a warning from the validator:

                          === 2012-07-24 17:28:32,981 [l0-5] WARN Validation - [builtinApplication.domain_DataSource_add] No such type 'Integer', not processing field value at /domain_DataSource/parentDomainId

                          So I don't know what exactly is the difference between not providing a "type" in the ds.xml for that field, but it is clearly different behavior than using a bogus type setting.

                          I'll go out on a limb here and say that there appears to be a bug or two in handling root tree nodes where the treerootvalue is null (and the type is not set). First, results get a nullpointerexception when any of the parentIds are null. Second, when a non-null integer value is provided as the parentId, the datasource-jpa is getting a numberformatexception.

                          I would be happy to provide you with the source code changes to this if you would like them.
                          Attached Files

                          Comment


                            #28
                            Regarding this NPE/NumberFormatException issue, this is the interesting one to isolate - can we see the DataSource definition and client code?

                            Note that the log has two different threads (10-2 and 10-3) with interleaved logs. It looks like you are simultaneously adding new nodes and fetching existing nodes. Unless you wait for the callback from DataSource.addData() before fetching, this is going to have unpredictable results from run to run. Also, your own logs aren't going through log4j so aren't getting thread markers, so we can't tell what thread you're in.

                            Comment


                              #29
                              Actually, it's more whacked then even that. The logs show:

                              1. two concurrent fetch requests, both with criteria parentDomainId=null, but one also with name="Global" also added

                              2. you've got setter code (even though you shouldn't in this case) which is apparently called by JPA as it populates beans as a result of a fetch. Your setter code actually goes and creates a new entity because it modifies the fetched beans. This is probably where your weird extra records are coming from

                              3. As a result of these weird shenanigans your getter method throws an NPE

                              4. Something goes wrong with the subsequent add, but it appears you're trying to specify an id of "1" when you haven't declared a concrete field for holding the foreignKey value. You don't get to decide on the type or format of the ID in this case because your leaving it up to JPA how to handle persistence of the relation, hence the NumberFormatException when it expects one of its own IDs and not just "1".

                              .. we're going to stop there until we see the code, but suffice it to say, it's definitely not a good assumption that we have a bug in this area, at least not yet.

                              Comment


                                #30
                                To spell it out, this log doesn't show your code ever attempting to insert a record with no parentId. It tries with "1", and that's invalid (due to JPA and your settings, not SmartGWT).

                                Comment

                                Working...
                                X