Announcement

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

  • Isomorphic
    replied
    As of the latest 5.1d builds, we now have built-in features for automatically converting Long, BigDecimal and BigInteger values to Strings when they are delivered to the browser - see dataSourceField.stringInBrowser.

    Leave a comment:


  • fatzopilot
    replied
    Hi,

    thanks, the last suggestion/clarification finally works in all cases and is comparatively simple.
    I was wrong in the assumption that using the custom stingId s/getter and mapping it to the id field using a valueXPath is basically the same as
    Originally posted by Isomorphic View Post
    add DMI logic to your "fetch" operation where you call the server-side DSResponse.getRecord()s and convert all the Long values to Strings
    Thanks for your patience and sorry for the trouble,
    fatzopilot

    Leave a comment:


  • Isomorphic
    replied
    You are mixing working approaches together in ways that break them.

    For the DMI approach, get rid of the valueXPath on your sequence field, and do not declare any additional setters. Add the described DMI logic to convert outbound dsResponse values to String and inbound dsRequest criteria and values to Long.

    That's it and nothing else.

    If you're still having trouble with this approach, you need to post your code for converting the dsRequest. It looks like it's not working.

    Leave a comment:


  • fatzopilot
    replied
    Originally posted by Isomorphic View Post
    Beating a dead horse here..
    The DMI solution does **not** involve declaring the field as type "text". It involves your own code converting from Long to String as described above. So there is no possible way the ID could end up "crippled".
    Again, the problem now is not the data, it is the criteria.
    When I do like advised, i.e.
    Code:
    <field name="id" type="sequence" title="Id" valueXPath="stringId" primaryKey="true"/>
    I still get
    Code:
    org.hibernate.TypeMismatchException: Provided id of the wrong type for class com.aCompany.Attachment. Expected: class java.lang.Long, got class java.lang.String
    	at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:135)
    	at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:1090)
    	at org.hibernate.impl.SessionImpl.get(SessionImpl.java:1005)
    	at com.isomorphic.hibernate.HibernateDataSource.processRequest(HibernateDataSource.java:1458)
    	at com.isomorphic.hibernate.HibernateDataSource.executeUpdate(HibernateDataSource.java:821)
    	at com.isomorphic.datasource.DataSource.execute(DataSource.java:1383)
    	at com.isomorphic.application.AppBase.executeDefaultDSOperation(AppBase.java:723)
    	at com.isomorphic.application.AppBase.executeAppOperation(AppBase.java:658)
    	at com.isomorphic.application.AppBase.execute(AppBase.java:491)
    	at com.isomorphic.datasource.DSRequest.execute(DSRequest.java:2044)
    	at com.aCompany.MyDMI$$EOHuass3.convertStringIDCriteriaToLong(MyDMI.java)
    	at com.aCompany.MyDMI$$EOHuass3.markDeleted(MyDMI.java:256)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:606)
    	at org.springsource.loaded.ri.ReloadedTypeInvoker$2.invoke(ReloadedTypeInvoker.java:122)
    	at org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1288)
    	at com.isomorphic.base.Reflection.adaptArgsAndInvoke(Reflection.java:973)
    	at com.isomorphic.datasource.DataSourceDMI.execute(DataSourceDMI.java:416)
    	at com.isomorphic.datasource.DataSourceDMI.execute(DataSourceDMI.java:64)
    	at com.isomorphic.datasource.DSRequest.execute(DSRequest.java:2041)
    	at com.isomorphic.servlet.IDACall.handleDSRequest(IDACall.java:216)
    	at com.isomorphic.servlet.IDACall.processRPCTransaction(IDACall.java:173)
    	at com.isomorphic.servlet.IDACall.processRequest(IDACall.java:138)
    	at com.isomorphic.servlet.IDACall.doPost(IDACall.java:74)
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
    on an update/delete operation, even if I manually convert the id Criteria into a Long in an updateDMI.
    This is what I don’t understand and cannot figure out by myself. The field is defined to be of type sequence (so this should not apply here:)
    Originally posted by Isomorphic View Post
    As far as Option 1, the field is declared as type "text" so validation forces it to a String.
    the id criteria is converted to Long and still the org.hibernate.impl.SessionImpl.get() method is invoked by com.isomorphic.hibernate.HibernateDataSource.processRequest () with an id of type String. Why? Is the type=”sequence” attribute meaningful at all in this case?
    This is the code where above exception is raised:
    org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:135)
    Code:
    final Class idClass = persister.getIdentifierType().getReturnedClass();
    		if ( persister.getIdentifierType().isComponentType() && EntityMode.DOM4J == event.getSession().getEntityMode() ) {
    			// skip this check for composite-ids relating to dom4j entity-mode;
    			// alternatively, we could add a check to make sure the incoming id value is
    			// an instance of Element...
    		}
    		else {
    			if ( idClass != null && ! idClass.isInstance( event.getEntityId() ) ) {
    				// we may have the kooky jpa requirement of allowing find-by-id where
    				// "id" is the "simple pk value" of a dependent objects parent.  This
    				// is part of its generally goofy "derived identity" "feature"
    				if ( persister.getEntityMetamodel().getIdentifierProperty().isEmbedded() ) {
    					final EmbeddedComponentType dependentIdType =
    							(EmbeddedComponentType) persister.getEntityMetamodel().getIdentifierProperty().getType();
    					if ( dependentIdType.getSubtypes().length == 1 ) {
    						final Type singleSubType = dependentIdType.getSubtypes()[0];
    						if ( singleSubType.isEntityType() ) {
    							final EntityType dependentParentType = (EntityType) singleSubType;
    							final Type dependentParentIdType = dependentParentType.getIdentifierOrUniqueKeyType( source.getFactory() );
    							if ( dependentParentIdType.getReturnedClass().isInstance( event.getEntityId() ) ) {
    								// yep that's what we have...
    								loadByDerivedIdentitySimplePkValue(
    										event,
    										loadType,
    										persister,
    										dependentIdType,
    										source.getFactory().getEntityPersister( dependentParentType.getAssociatedEntityName() )
    								);
    								return;
    							}
    						}
    					}
    				}
    				throw new TypeMismatchException(
    						"Provided id of the wrong type for class " + persister.getEntityName() + ". Expected: " + idClass + ", got " + event.getEntityId().getClass()
    				);
    			}
    because (as mentioned above) org.hibernate.impl.SessionImpl.get() is invoked with a String argument.

    Conclusion:
    Unless it is not possible to convert String id criteria to Long, the whole approach (stringIds on the client) is doomed to failure for update/delete operations using the hibernate datasource.
    Originally posted by Isomorphic View Post
    Likewise the alias field solution does not involve declaring the "id" field *at all*. So you can't possibly get a warning about a missing PK.
    Apologies, I missed an inheritsFrom statement which inherits a hidden id field. That’s why it appeared as if removing the id field does not work (because the update operations still requested it).

    Cheers,
    fatzopilot

    Leave a comment:


  • Isomorphic
    replied
    Beating a dead horse here..

    The DMI solution does **not** involve declaring the field as type "text". It involves your own code converting from Long to String as described above. So there is no possible way the ID could end up "crippled".

    Likewise the alias field solution does not involve declaring the "id" field *at all*. So you can't possibly get a warning about a missing PK. And again with this solution, your own code does the Long<->String conversion, so there's no way for the framework to be truncating the value.

    Leave a comment:


  • fatzopilot
    replied
    Hi Isomorphic,

    Thanks for your detailed response. My problem is not the data and not the fetch and add operations (these are solved already), it is the criteria in update operations that is making trouble whenever it contains the id.
    What happens:
    The id criteria (e.g. id=”1234567890123456) is of type text, so in order for this to work, I need to either
    Option 1: convert the criteria to long (DMI). This is not possible (as reported) because it is back converted to text due to the type of the id field (which is text). See
    Originally posted by Isomorphic View Post
    As far as Option 1, the field is declared as type "text" so validation forces it to a String.
    Option 2: do the update operation using an alias field of type text (stringId). Two suboptions
    Option 2.1: remove the id field form the DS. Then I get
    Code:
    com.isomorphic.base.UpdateWithoutPKException: Criteria received from the client for update operation is missing the following required unique and/or primary fields: [id]
    Option 2.2: let the id field in place
    What happens: I get both, id and stringID in the criteria. Because id is not accurate (crippled long version), I need to remove it from the critiera (DMI), which then leads to the same error as above.
    I don’t know whether both “issues” are by design, but they clearly derail my text field approach.

    Originally posted by Isomorphic View Post
    In theory, the same thing should also work by declaring a field "stringId" with a valueXPath to the field "id" - here you would be hoping JXPath is smart enough to handle String<-> Long conversion. It may not be, however, it doesn't look like you've tried this properly yet: in your previous attempts, you either did a no-op valueXPath (field name "id" valueXPath "id") or reversed the suggestion (field name "id" valueXPath "stringId").
    I tried this as well i.e.
    Code:
    <field name="stringId" type="text" title="IdString" valueXPath="id" primaryKey="true"/>
    But this id is crippled too.
    Originally posted by Isomorphic View Post
    And again the feature we referred to in the new version is for *SQL*DataSource, not Hibernate, so you won't get anything out of new versions, you'd have to switch to SQLDataSource first.
    Yeah, I missed that.
    Originally posted by Isomorphic View Post
    Also, what database product and version is this that is using full-range Longs as IDs?
    Postgres on the server, but the ids are by design. They are generated using a hash of the content of a second binary field. 64-bit hashes are just considered ok, less would become more and more problematic (collisions). These records are generated and used on mobile Android devices whose SQL-APIs (CursorAdapter) need an id field of type long.

    Hope things are clear now,
    Thanks,
    fatzopilot

    Leave a comment:


  • Isomorphic
    replied
    To hopefully clarify things again:

    The DMI approach means you simply declare fields normally, then add DMI logic to your "fetch" operation where you call the server-side DSResponse.getRecord()s and convert all the Long values to Strings, then call DSResponse.setData() with the updated data. Likewise, take any inbound values in a DSRequest that are in String format and change them to Long.

    The alternative approach of creating an "alias field" on your Hibernate bean works as follows: say your bean has getter/setter methods getId()/setId() that work with Long.

    1. Create getStringId()/setStringId() which return and take String, which actually convert between String and Long and call getId()/setId() with Long values

    2. Declare a field "stringId" of type "text" in your DataSource and make that the primaryKey.

    In theory, the same thing should also work by declaring a field "stringId" with a valueXPath to the field "id" - here you would be hoping JXPath is smart enough to handle String<-> Long conversion. It may not be, however, it doesn't look like you've tried this properly yet: in your previous attempts, you either did a no-op valueXPath (field name "id" valueXPath "id") or reversed the suggestion (field name "id" valueXPath "stringId").

    And again the feature we referred to in the new version is for *SQL*DataSource, not Hibernate, so you won't get anything out of new versions, you'd have to switch to SQLDataSource first.

    Also, what database product and version is this that is using full-range Longs as IDs?

    Leave a comment:


  • fatzopilot
    replied
    Originally posted by Isomorphic View Post
    No, a field declared as "text" is expected to be "text" and not something else.

    Changing that field to "integer" and yet still having an error message about a missing primary key doesn't make sense and we can't reproduce that.
    OK, seems like the last posts have caused some confusion.

    There are basically two approaches here:
    1) use the original id field to submit a text version of the long id
    2) use an alias data source field stringId with type text (and do some tricks to interact with hibernate.

    1:
    In order to work with very long ids, I tried to circumvent the crippled ids by converting them to text. This can be done if an additional getter/setter/formula field is provided in the bean which is named e.g. stringId. This can then be used in a valueXPath of the id field to obtain a String. Of course, the type of the id field in these cases is text.
    Problem here: A text id from the client (e.g. in update operations) cannot be converted back (as described in a previous post) and hibernate does not accept string ids

    2:
    The field id has a type of "long" in the DB and in usual cases a type of "sequence" in the DS description.
    As advised, I changed the type of the id data source field from sequence to integer and removed the primary key attribute, because I wanted to use a different data source field (e.g. stringId with corresponding getter/setter/formula fields in the bean) for that purpose.
    Problem here:
    Still, the id field is identified as a primary key field for some reason (probably, because it is a primary key field in hibernate), and so, all update operations require it (and it cannot be removed in a DMI, which is a showstopper here).

    Originally posted by Isomorphic View Post
    Sorry, we need to move on to bugs. Upgrading or using a DMI are two ways to solve this.
    I am working with DMIs here all the time...
    And I tried the latest Evaluation build
    SmartClient Version: SNAPSHOT_v9.1d_2013-09-17/EVAL Deployment
    which exhibits the exact same behavior in these regards.

    Leave a comment:


  • Isomorphic
    replied
    No, a field declared as "text" is expected to be "text" and not something else.

    Changing that field to "integer" and yet still having an error message about a missing primary key doesn't make sense and we can't reproduce that.

    Sorry, we need to move on to bugs. Upgrading or using a DMI are two ways to solve this.

    Leave a comment:


  • fatzopilot
    replied
    Originally posted by Isomorphic View Post
    As far as Option 1, the field is declared as type "text" so validation forces it to a String.
    Is there a way to disable validation for this case?

    Originally posted by Isomorphic View Post
    On the PK error message, you've declared the type as "sequence" so that implies primaryKey=true.
    I changed it to "integer" but without any change.

    Thanks,
    fatzopilot

    Leave a comment:


  • Isomorphic
    replied
    As far as Option 1, the field is declared as type "text" so validation forces it to a String.

    On the PK error message, you've declared the type as "sequence" so that implies primaryKey=true.

    Leave a comment:


  • fatzopilot
    replied
    Hm, seems like I was a bit to optimistic. Updating is still an issue as hibernate needs a long valued id to access the object via id (it's a long in the db).

    Option 1): The obvious option:
    Code:
    <field name="id" type="text" title="Id" valueXPath="stringId" primaryKey="true" detail="false"/> <!-- stringId delivers the id as string -->
    The problem arises because the id from the client needs to be transformed back to long (in a DMI or using DSRequestModifiers) in order for hibernate to work.
    I tried to replace both, the id criteria and the id value that are contained in a dsRequest. This works but if the request reaches Hibernate through DsRequest, AppBase, and HibernateDataSource, it is "magically" back converted to a String and consequently, Hibernate throws an exception:
    Code:
    org.hibernate.TypeMismatchException: Provided id of the wrong type for class com.aCompany.Attachment. Expected: class java.lang.Long, got class java.lang.String
    Option 2): Use an extra alias field (stringId) for the update query. This is what works for add and fetch operations using the methods described earlier. However, for update operations the stringID must be set as primaryKey in order to get submitted from the client, i.e.
    Code:
    <field name="id" type="sequence" title="Id" detail="false"/>
    <field name="stringId" type="text" title="Id" primaryKey="true" valueXPath="stringId" canView="true" hidden="false" detail="false"/>
    and we need to get rid of the crippled id from the client (done in a DMI).
    However, then I get
    Code:
    com.isomorphic.base.UpdateWithoutPKException: Criteria received from the client for update operation is missing the following required unique and/or primary fields: [id]. Either provide all primary key fields or set allowMultiUpdate on the OperationBinding
    So SGWT still demands to submit id albeit it is not defined as primary key in the DS.
    In addition, I feel that option 2 is clearly a bit too hacky.

    Can you please check why the long id gets back-converted as described in 1 or propose another solution?

    Thanks,
    fatzopilot

    Leave a comment:


  • fatzopilot
    replied
    OK, I was able to solve this using getStringId/setStringId on the bean, a valueXPath="stringId" in the DS field definition for id, and a formula for stringId on the bean (to make queries work).

    Cheers,
    fatzopilot

    Leave a comment:


  • Isomorphic
    replied
    Yes that's what we meant, but clearly it doesn't work.

    On the old version, you'll need to either transform the data once the dsResponse has been retrieved, or create a parallel getter/setter on your bean that returns a String and can take a String.

    Leave a comment:


  • fatzopilot
    replied
    Originally posted by Isomorphic View Post
    In older versions, setting a trivial valueXPath (same name as the bean getter method) would be one way to force conversion.
    Changing to
    Code:
    <field name="id" type="text" valueXPath="id" title="Id" primaryKey="true" detail="false">
    did not change anything. Or what did you have in mind?

    Thanks,
    fatzopilot

    Leave a comment:

Working...
X