Cayenne 4.1 DataRow and objEntity with java.lang.Boolean

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Cayenne 4.1 DataRow and objEntity with java.lang.Boolean

Meeks, Andrew
I have recently migrated from 4.0 to 4.1.  I have a few instances where I perform a query and then I create an instance of my entity from the resulting DataRow.  These object entities have both Integer and Boolean fields.  DataRow has always cast these fields as BigDecimal.

In 4.0 the DataContext.objectFromDataRow handled these fields, evidently because CayenneDataObject could put BigDecimal into the values map of <String, Object>.

In 4.1 it appears as though each entity implements its own writePropertyDirectly which puts the object into a typed field of that entity.  The objectFromDataRow fails because of the cast exception from BigDecimal to Boolean or BigDecimal to Integer.  So these fields evidently need to be converted to correct types before being set on the entity.

My workaround is to write a utility method that cleans up the DataRow before I hand it to objectFromDataRow.  The utility method looks for any attributes that are BigDecimal on the DataRow, then for the same field name in the entity, it looks for the desired type.  Then it does an appropriate conversion from BigDecimal.

        public static void cleanUpDataRow(DataContext dContext, Class clazz, DataRow dataRow) {
               ObjEntity entity = dContext.getEntityResolver().getObjEntity(clazz);
               Collection<ObjAttribute> entityAttributes = entity.getAttributes();
               for(ObjAttribute attribute : entityAttributes) {
                       String attributeClass = attribute.getType();
                       if(dataRow.get(attribute.getDbAttributeName()) != null &&
                         dataRow.get(attribute.getDbAttributeName()).getClass().getTypeName().equals("java.math.BigDecimal")) {
                              if(attributeClass.equals("java.lang.Boolean")) {
                                      dataRow.put(attribute.getDbAttributeName(), (dataRow.get(attribute.getDbAttributeName()).equals(BigDecimal.valueOf(1))));
                              } else if(attributeClass.equals("java.lang.Integer")) {
                                      dataRow.put(attribute.getDbAttributeName(), ((BigDecimal)dataRow.get(attribute.getDbAttributeName())).intValue());
                              }
                       }
               }
        }

Usage:

...
CayennePatch.cleanUpDataRow(dContext, ROrganizationalChart.class, dataRow);
ROrganizationalChart fetchedChart = dataContext.objectFromDataRow(ROrganizationalChart.class, dataRow);
...

How can I use DataRows in 4.1 with objectFromDataRow without using a custom method?
Thank you!
Andrew

Reply | Threaded
Open this post in threaded view
|

Re: Cayenne 4.1 DataRow and objEntity with java.lang.Boolean

Andrus Adamchik
Hi Andrew,

What is the origin of the DataRows? If they come from a SQLSelect/SQLTemplate query, there's a way to control value types with #result directive [1]. This will fix the issue in 4.1, and is arguably the best approach even in 4.0, as loading values of incorrect type to a DataObject via a generic API only defers a ClassCastException, not prevents it entirely.

Cheers,
Andrus

[1] https://cayenne.apache.org/docs/4.0/cayenne-guide/queries.html#sqltemplate


> On Dec 5, 2017, at 11:00 AM, Meeks, Andrew <[hidden email]> wrote:
>
> I have recently migrated from 4.0 to 4.1.  I have a few instances where I perform a query and then I create an instance of my entity from the resulting DataRow.  These object entities have both Integer and Boolean fields.  DataRow has always cast these fields as BigDecimal.
>
> In 4.0 the DataContext.objectFromDataRow handled these fields, evidently because CayenneDataObject could put BigDecimal into the values map of <String, Object>.
>
> In 4.1 it appears as though each entity implements its own writePropertyDirectly which puts the object into a typed field of that entity.  The objectFromDataRow fails because of the cast exception from BigDecimal to Boolean or BigDecimal to Integer.  So these fields evidently need to be converted to correct types before being set on the entity.
>
> My workaround is to write a utility method that cleans up the DataRow before I hand it to objectFromDataRow.  The utility method looks for any attributes that are BigDecimal on the DataRow, then for the same field name in the entity, it looks for the desired type.  Then it does an appropriate conversion from BigDecimal.
>
>        public static void cleanUpDataRow(DataContext dContext, Class clazz, DataRow dataRow) {
>               ObjEntity entity = dContext.getEntityResolver().getObjEntity(clazz);
>               Collection<ObjAttribute> entityAttributes = entity.getAttributes();
>               for(ObjAttribute attribute : entityAttributes) {
>                       String attributeClass = attribute.getType();
>                       if(dataRow.get(attribute.getDbAttributeName()) != null &&
>                         dataRow.get(attribute.getDbAttributeName()).getClass().getTypeName().equals("java.math.BigDecimal")) {
>                              if(attributeClass.equals("java.lang.Boolean")) {
>                                      dataRow.put(attribute.getDbAttributeName(), (dataRow.get(attribute.getDbAttributeName()).equals(BigDecimal.valueOf(1))));
>                              } else if(attributeClass.equals("java.lang.Integer")) {
>                                      dataRow.put(attribute.getDbAttributeName(), ((BigDecimal)dataRow.get(attribute.getDbAttributeName())).intValue());
>                              }
>                       }
>               }
>        }
>
> Usage:
>
> ...
> CayennePatch.cleanUpDataRow(dContext, ROrganizationalChart.class, dataRow);
> ROrganizationalChart fetchedChart = dataContext.objectFromDataRow(ROrganizationalChart.class, dataRow);
> ...
>
> How can I use DataRows in 4.1 with objectFromDataRow without using a custom method?
> Thank you!
> Andrew
>

Reply | Threaded
Open this post in threaded view
|

RE: Cayenne 4.1 DataRow and objEntity with java.lang.Boolean

Meeks, Andrew
Thank you, yes, I am using SQLSelect but I had not made use of the #result directive.  I shall do so!

Andrew

-----Original Message-----
From: Andrus Adamchik [mailto:[hidden email]]
Sent: Wednesday, December 06, 2017 4:23 PM
To: [hidden email]
Subject: Re: Cayenne 4.1 DataRow and objEntity with java.lang.Boolean

Hi Andrew,

What is the origin of the DataRows? If they come from a SQLSelect/SQLTemplate query, there's a way to control value types with #result directive [1]. This will fix the issue in 4.1, and is arguably the best approach even in 4.0, as loading values of incorrect type to a DataObject via a generic API only defers a ClassCastException, not prevents it entirely.

Cheers,
Andrus

[1] https://cayenne.apache.org/docs/4.0/cayenne-guide/queries.html#sqltemplate


> On Dec 5, 2017, at 11:00 AM, Meeks, Andrew <[hidden email]> wrote:
>
> I have recently migrated from 4.0 to 4.1.  I have a few instances where I perform a query and then I create an instance of my entity from the resulting DataRow.  These object entities have both Integer and Boolean fields.  DataRow has always cast these fields as BigDecimal.
>
> In 4.0 the DataContext.objectFromDataRow handled these fields, evidently because CayenneDataObject could put BigDecimal into the values map of <String, Object>.
>
> In 4.1 it appears as though each entity implements its own writePropertyDirectly which puts the object into a typed field of that entity.  The objectFromDataRow fails because of the cast exception from BigDecimal to Boolean or BigDecimal to Integer.  So these fields evidently need to be converted to correct types before being set on the entity.
>
> My workaround is to write a utility method that cleans up the DataRow before I hand it to objectFromDataRow.  The utility method looks for any attributes that are BigDecimal on the DataRow, then for the same field name in the entity, it looks for the desired type.  Then it does an appropriate conversion from BigDecimal.
>
>        public static void cleanUpDataRow(DataContext dContext, Class clazz, DataRow dataRow) {
>               ObjEntity entity = dContext.getEntityResolver().getObjEntity(clazz);
>               Collection<ObjAttribute> entityAttributes = entity.getAttributes();
>               for(ObjAttribute attribute : entityAttributes) {
>                       String attributeClass = attribute.getType();
>                       if(dataRow.get(attribute.getDbAttributeName()) != null &&
>                         dataRow.get(attribute.getDbAttributeName()).getClass().getTypeName().equals("java.math.BigDecimal")) {
>                              if(attributeClass.equals("java.lang.Boolean")) {
>                                      dataRow.put(attribute.getDbAttributeName(), (dataRow.get(attribute.getDbAttributeName()).equals(BigDecimal.valueOf(1))));
>                              } else if(attributeClass.equals("java.lang.Integer")) {
>                                      dataRow.put(attribute.getDbAttributeName(), ((BigDecimal)dataRow.get(attribute.getDbAttributeName())).intValue());
>                              }
>                       }
>               }
>        }
>
> Usage:
>
> ...
> CayennePatch.cleanUpDataRow(dContext, ROrganizationalChart.class,
> dataRow); ROrganizationalChart fetchedChart =
> dataContext.objectFromDataRow(ROrganizationalChart.class, dataRow); ...
>
> How can I use DataRows in 4.1 with objectFromDataRow without using a custom method?
> Thank you!
> Andrew
>