Announcement Announcement Module
Collapse
No announcement yet.
Value Object-Data Transfer Object Merge Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Value Object-Data Transfer Object Merge

    As probably with most Spring/Hibernate architectures, we leveraging our Hibernate VOs as DTOs as well. This works rather nicely, however I have one issue: if I pass in a DTO (detached VO that represents a row) there might be some data changed. Fine, I normally just do an .update() to reattach the DTO, but suppose, I need to read some information about this record from the database before I update it. Once I do the read, I now have a VO in the session that represents the row in the db. But I also have my detached VO which has my changes.

    My solution seems to be that I need to merge my detached VO with the one I got in this session, iff the version field is the same, since we are using optimistic locking. Does this sound like a sound approach? Do either frameworks provide any utilities to solve this issue. I'm thinking a generic solution would be nice with the reflection API. Basically, take in the two objects to merge and sync up their fields.

    Thanks,
    Lou

  • #2
    Web based data access.

    I don't know of any existing solutions; but here is an idea I just started to work on today. (also note that I am using OJB and not Hibernate, but the concepts should be pretty much the same as far as I know).

    First, you want a 'generic' transaction/session controller which is basically acts like a cross between a Spring OM template and a Spring MVC Controller, starting a new transaction apon recieving a request and either commiting or rolling back the transaction before returning a ModelAndView. In the template method you do the actual 'work'. As a final responsiblity, this controller should be responsible for storing a OID in the HttpSession representing the object.

    Then:
    • onRead you use Spring helpers to parse request, construct query, and return 'command' object. Its OID is auto stored in the session.
    • onUpdate you retrieve the 'real' command object by OID from the object mapper, then use Spring's ServletRequestDataBinder to auto-update the properties from the request. When the transaction is commited the object's new state is then stored.
    • onCreate is the same as onUpdate, except a new instance of the command class is created and populated by the binder before being stored.
    • onDelete would lookup the object by OID, delete it, and remove the OID from the session.

    I haven't quite worked out all the inner mechanisms, but I think something along these line would represent a very good solution for this sort of application.

    Comment


    • #3
      Re: Value Object-Data Transfer Object Merge

      Originally posted by Loumeister
      As probably with most Spring/Hibernate architectures, we leveraging our Hibernate VOs as DTOs as well.
      You mean Business Objects as DTOs? VO and DTO are two names for the same thing.

      What I do is copy properties with commons-beanutils in my hibernate daos.

      You can check it at http://oness.sourceforge.net in the oness-common-model module

      Regards

      Comment


      • #4
        Re: Value Object-Data Transfer Object Merge

        Originally posted by carlos
        You mean Business Objects as DTOs? VO and DTO are two names for the same thing.
        Well I guess it depends in what circles you travel in; VOs to us represent a POJO representing a row in a table, whereas DTO represents the object we'd pass back to the presentation tier. Agreed that BOs are what I am talking about when I say VOs. It's just that acroymn is so rank! :lol:

        Ok, so now back to the my question. Does commons-beanutils use reflection to do this? I wasn't able to find your sample right off on your website, but I'll look again more closely. If you could kindly post back the specific location.

        Thanks for sharing your website here!

        Lou

        Comment


        • #5
          Scratch that...I found it. Pretty cool!

          Thanks,
          Lou

          Comment


          • #6
            Re: Value Object-Data Transfer Object Merge

            Originally posted by carlos
            Originally posted by Loumeister
            As probably with most Spring/Hibernate architectures, we leveraging our Hibernate VOs as DTOs as well.
            You mean Business Objects as DTOs? VO and DTO are two names for the same thing.

            What I do is copy properties with commons-beanutils in my hibernate daos.

            You can check it at http://oness.sourceforge.net in the oness-common-model module

            Regards
            Could you point to me exactly. I am very interested in it.

            dino

            Comment


            • #7
              Hibernate support versioning auto
              For instance :
              You have class with version 3 and update it
              hibernate do query
              update ...
              where <your where> and version=3

              If row is changed version is update to 4 and Hibernate return stale exception
              If row don't changed hibernate update row and change version to 4

              You have to set version column in mapping only - hibernate do hard work

              regards

              Comment


              • #8
                Re: Value Object-Data Transfer Object Merge

                Originally posted by hucmuc
                Could you point to me exactly. I am very interested in it.
                http://oness.sourceforge.net/multipr...ref/index.html
                HibernateDao and HibernateDaoSupport

                Comment


                • #9
                  Carlos,

                  I had a chance to look at your code. In HibernateDao I see that you do a find and set it to a destination object then you are nulling out uninitialized collections if you pass in the appropriate switch. If you null out those collections, won't that flag the object as dirty and cause Hibernate to persist the state? Also, why would you do another find for the destination object? Wouldn't the object passed in be the persistent object? (code below)

                  I was thinking of taking the approach of passing the Hibernate VO and then use beanutils to copy over the properties that are in my destination object which would exclude unitialized Sets and foreign keys. I'd also have to recurse through the sets that were initialized so I could clean them up as well. I'll post something back once I come up with something.

                  Lou

                  Code:
                  127     private HibernateCallback getProcessCollectionsCallback&#40;
                  128             final AuditableBusinessObject object, final int operation&#41; &#123;
                  129 
                  130         return new HibernateCallback&#40;&#41; &#123;
                  131 
                  132             public Object doInHibernate&#40;Session session&#41;
                  133                     throws HibernateException &#123;
                  134 
                  135                 ClassMetadata classMetadata = getSessionFactory&#40;&#41;
                  136                         .getClassMetadata&#40;object.getClass&#40;&#41;&#41;;
                  137 
                  138                 /* get persistent properties */
                  139                 Type&#91;&#93; propertyTypes = classMetadata.getPropertyTypes&#40;&#41;;
                  140                 String&#91;&#93; propertyNames = classMetadata.getPropertyNames&#40;&#41;;
                  141 
                  142                 /* destination bean */
                  143                 AuditableBusinessObject dest = findById&#40;object.getId&#40;&#41;&#41;;
                  144 
                  145                 /* for each persistent property of the bean */
                  146                 for &#40;int i = 0; i < propertyTypes.length; i++&#41; &#123;
                  147 
                  148                     if &#40;!propertyTypes&#91;i&#93;.isPersistentCollectionType&#40;&#41;&#41;
                  149                         continue;
                  150 
                  151                     CollectionMetadata collectionMetadata = getSessionFactory&#40;&#41;
                  152                             .getCollectionMetadata&#40;
                  153                                     &#40;&#40;PersistentCollectionType&#41; propertyTypes&#91;i&#93;&#41;
                  154                                             .getRole&#40;&#41;&#41;;
                  155 
                  156                     /* if it's a lazy collection operate on it */
                  157                     if &#40;collectionMetadata.isLazy&#40;&#41;&#41; &#123;
                  158                         switch &#40;operation&#41; &#123;
                  159                         case &#40;INITIALIZE_LAZY&#41;&#58;
                  160                             Collection c = filterNotDeleted&#40;&#40;Collection&#41; classMetadata
                  161                                     .getPropertyValue&#40;object, propertyNames&#91;i&#93;&#41;&#41;;
                  162                             classMetadata.setPropertyValue&#40;dest,
                  163                                     propertyNames&#91;i&#93;, c&#41;;
                  164                             break;
                  165                         case &#40;NULLIFY_LAZY&#41;&#58;
                  166                             classMetadata.setPropertyValue&#40;dest,
                  167                                     propertyNames&#91;i&#93;, null&#41;;
                  168                             break;
                  169                         default&#58;
                  170                             throw new IllegalArgumentException&#40;
                  171                                     "Unknown operation"&#41;;
                  172                         &#125;
                  173                     &#125;
                  174                 &#125;
                  175                 return dest;
                  176             &#125;
                  177         &#125;;
                  178     &#125;

                  Comment


                  • #10
                    Originally posted by Loumeister
                    I had a chance to look at your code. In HibernateDao I see that you do a find and set it to a destination object then you are nulling out uninitialized collections if you pass in the appropriate switch. If you null out those collections, won't that flag the object as dirty and cause Hibernate to persist the state? Also, why would you do another find for the destination object? Wouldn't the object passed in be the persistent object? (code below)
                    I don't nullify lazy collections anymore. IIRC the object passed to processAndInitializeCollections it's a copy of the persistend object.

                    Maybe you'd prefer moving this thread to the ONess mailing list.

                    Regards

                    Comment


                    • #11
                      Thanks Carlos to opening my eyes to the low-level API and tools for doing some of this stuff. I hope you don't mind that I leveraged some of your code to that end. I have successfully been able to develop a generic solution to this problem. I've posted the guts of it below.

                      Basically, I put this in an abstract class that my VOs extend and then my DAOs can just call them to do the conversion and return the appropriate type (or DTO). Despite the recursion, it runs pretty fast (< 500 ms) for a decent sized graph. Just be aware that a large graph will slow your performance.

                      Caveat: if your VOs have BigDecimals that are set to null, then BeanUtils will through a runtime conversion exception. I rewrote my getters on those fields to return 0.0 instead and that went away.

                      HTHO,
                      Lou

                      Code:
                          
                      /**
                           * Method that leverages the Hibernate API and reflection to generically
                           * recurse through object graph and send back a resulting graph that is
                           * devoid of any circular references and any uninitiliazed collections,
                           * since we use lazy initialization.
                           * </p> 
                           * 
                           * @param sesFactory
                           * @param object
                           * @return 
                           */
                          public Object getObjectType&#40;final SessionFactory sesFactory,
                                  final Object object&#41; &#123;
                              Monitor mon = MonitorFactory.start&#40;this.getClass&#40;&#41;.getName&#40;&#41; + "&#58;getObjectType"&#41;;
                              try &#123;
                                  BeanUtilsBean beanUtil = new BeanUtilsBean&#40;&#41;;
                                  Class objClass = object.getClass&#40;&#41;;
                      
                                  ClassMetadata classMetadata = sesFactory.getClassMetadata&#40;objClass&#41;;
                      
                                  // get persistent properties
                                  Type&#91;&#93; propertyTypes = classMetadata.getPropertyTypes&#40;&#41;;
                                  String&#91;&#93; propertyNames = classMetadata.getPropertyNames&#40;&#41;;
                      
                                  // result bean to return to client
                                  Object typeRslt = getClassInstance&#40;classMetadata.getMappedClass&#40;&#41;&#41;;
                                  try &#123;
                                      beanUtil.copyProperties&#40;typeRslt, object&#41;;
                                  &#125; catch &#40;RuntimeException e&#41; &#123;
                      		log.debug&#40;"Bean utils failed.", e&#41;;
                      		throw new HibernateException&#40;e&#41;;
                                  &#125; 
                      
                                  // for each persistent property of the bean
                                  for &#40;int i = 0; i < propertyTypes.length; i++&#41; &#123;
                      
                                      // We need special handling for collections; bean utils will
                                      // just copy fields for us so we continue if not collection
                                      if &#40;!propertyTypes&#91;i&#93;.isPersistentCollectionType&#40;&#41;&#41;
                                          continue;
                      
                                      CollectionMetadata collectionMetadata = sesFactory
                                              .getCollectionMetadata&#40;&#40;&#40;PersistentCollectionType&#41; propertyTypes&#91;i&#93;&#41;
                                                      .getRole&#40;&#41;&#41;;
                      
                                      String propName = propertyNames&#91;i&#93;;
                                      Collection collection = &#40;Collection&#41; classMetadata
                                              .getPropertyValue&#40;object, propName&#41;;
                      
                                      // if it's a lazy collection operate on it
                                      if &#40;collectionMetadata.isLazy&#40;&#41;&#41; &#123;
                                          if &#40;!Hibernate.isInitialized&#40;collection&#41;&#41; &#123;
                                              if &#40;collection != null&#41; &#123;
                                                  try &#123;
                                                      // If lazy, then we will nullify to prevent
                                                      // serialization from inflating
                                                      beanUtil.setProperty&#40;typeRslt, propName, null&#41;;
                                                  &#125; catch &#40;NullPointerException npe&#41; &#123;
                                                      // We assume empty already, so we don't need to
                                                      // null
                                                  &#125;
                                              &#125;
                                          &#125; else &#123;
                                              // TODO Needs to handle other types of collections, but
                                              // for now, Set is only used
                                              Set set = new HashSet&#40;&#41;;
                                              set = recurseAndFixGraph&#40;sesFactory, collection&#41;;
                                              beanUtil.setProperty&#40;typeRslt, propName, set&#41;;
                                          &#125;
                                      &#125; else &#123;
                                          //if not lazy, then just recurse and fix subgraph
                      
                                          // TODO Needs to handle other types of collections, but
                                          // for now, Set is only used
                                          Set set = new HashSet&#40;&#41;;
                                          set = recurseAndFixGraph&#40;sesFactory, collection&#41;;
                                          beanUtil.setProperty&#40;typeRslt, propName, set&#41;;
                                      &#125;
                                  &#125;
                                  return typeRslt;
                              &#125; catch &#40;HibernateException he&#41; &#123;
                                  log.debug&#40;"Hibernate Exception thrown", he&#41;;
                                  SessionFactoryUtils.convertHibernateAccessException&#40;he&#41;;
                              &#125; catch &#40;IllegalAccessException iae&#41; &#123;
                                  log.debug&#40;"IllegalAccessException Exception thrown", iae&#41;;
                                  SessionFactoryUtils
                                          .convertHibernateAccessException&#40;new HibernateException&#40;iae&#41;&#41;;
                              &#125; catch &#40;InvocationTargetException ite&#41; &#123;
                                  log.debug&#40;"InvocationTargetException Exception thrown", ite&#41;;
                                  SessionFactoryUtils
                                          .convertHibernateAccessException&#40;new HibernateException&#40;ite&#41;&#41;;
                              &#125;
                              finally &#123;
                                  mon.stop&#40;&#41;;
                                  // show stats
                                  log.info&#40;mon&#41;;
                              &#125;
                              return null;
                          &#125;
                      
                          /**
                           * This method will be help facilitate recursion to go through the object
                           * graph and call getObjectType to fix. Currently only handles Sets so we
                           * may need to consider other collections in the future.
                           * 
                           * @param sesFactory
                           *            Factory required by recursive method
                           * @param collection
                           *            collection to be recursed and fixed
                           * @return set resulting fixed Set to return to client
                           * @throws HibernateException
                           */
                          private Set recurseAndFixGraph&#40;final SessionFactory sesFactory,
                                  final Collection collection&#41; &#123;
                              try &#123;
                                  Set set = new HashSet&#40;&#41;;
                                  for &#40;Iterator iter = collection.iterator&#40;&#41;; iter.hasNext&#40;&#41;;&#41; &#123;
                                      Object o = iter.next&#40;&#41;;
                                      Object destSet = getObjectType&#40;sesFactory, o&#41;;
                                      set.add&#40;destSet&#41;;
                                  &#125;
                                  return set;
                              &#125; catch &#40;RuntimeException e&#41; &#123;
                                  // TODO Auto-generated catch block
                                  e.printStackTrace&#40;&#41;;
                                  return null;
                              &#125;
                          &#125;
                      
                          /**
                           * Helper method to locate instances of beans from application context via
                           * dependency injection. This is used so we only need to change the
                           * application context should we need to add more beans in the future.
                           * </p>
                           * 
                           * Bean type names are in the format of "type&#91;Hibernate VO Name&#93;" and so a
                           * string manipulation is done to dynamical obtain the VO name and append
                           * this to the PREFIX_BEAN_TYPE.
                           * </p>
                           * 
                           * @param clazz
                           *            the Hibernate VO classname
                           * @return returns an instant of the appropriate Type
                           */
                          private Object getClassInstance&#40;Class clazz&#41; &#123;
                              String typeClassName = PREFIX_BEAN_TYPE
                                      + StringExt.rightBack&#40;clazz.getName&#40;&#41;, "."&#41;;
                      
                              return ServiceLocator.getInstance&#40;&#41;.getBean&#40;typeClassName&#41;;
                          &#125;
                      Also, not in the last method that I store my DTOs in the application context for flexibility as follows:

                      Code:
                      	<bean id="typeMyClass" 
                      		class="com.company.services.MyType">
                      	</bean>

                      Comment


                      • #12
                        If you already merge your VO by using optimistic locking then give me some tips!!I regards you!!Business Merger

                        Comment

                        Working...
                        X