5 Replies Latest reply on Aug 22, 2009 12:07 AM by asookazian

    using DTO with Seam apps

    asookazian

      One of the benefits of using Seam is that it acts as the glue between the web technology and the server-side technology. By this we mean that the Seam Framework allows us to use enterprise beans (for example, Session Beans) directly within the Web tier without having to use Data Transfer Object (DTO) patterns and without worrying about exposing server-side functionality on the client.


      source: http://www.packtpub.com/article/developing-seam-applications


      What about cases where your JPAQL query returns more than one value (whether scalar or entity or both) in the select clause?  JPA/Hibernate returns a Object[] when there is more than one value, be it scalar or entity, in the select clause for a JPQL/HQL query (least common denominator tactics obviously, which makes sense).


      Then, in your JSF page while you are iterating thru the List in a dataTable, for example, you end up having to use array index references to extract the data from the Object[] array instances that are in the List<Object[]> that may have been outjected via @DataModel in your backing bean or DAO component.


      In the case of read-only data, it's ok to use the DTO (which is (un)officially an anti-pattern in EE 5 with the advent of JPA entity classes?)


      Is using DTOs to prevent array index referencing (i.e. hard-coding which is always dangerous!) of Object[] arrays a best or bad practice in these situations?


      Here is a simple example to illustrate the scenario using a theta-join:


      List<Object[]> myList = entityManager.createQuery(+
      " select foo, bar from Foo foo, Bar bar "+
      " where bar.fooId = foo.fooId ").getResultList();



      Or using ANSI sql syntax:


      List<Object[]> myList = entityManager.createQuery(+
      " select foo, bar from Foo as foo join foo.bar as bar ").getResultList();



      Refactor:


      List<GenericDTO> myList = entityManager.createQuery(+
      " select new com.your.company.CustomerInformationDTO( foo, bar ) from Foo as foo join foo.bar as bar ").getResultList();



      Now in your JSF, you can use this type of code in your EL:


      <a4j:repeat value="#{customerInformationList}" var="customerInformationDTO">                            
                                       <s:decorate template="layout/edit.xhtml">
                                              <ui:define name="label">Site: </ui:define>
                                              <h:outputText value="#{customerInformationDTO.listValue.description}"/>
                                         </s:decorate>     
                                         
                                    </a4j:repeat>



      Rather than using:


      <a4j:repeat value="#{customerInformationList}" var="customerInformation">                            
                                       <s:decorate template="layout/edit.xhtml">
                                              <ui:define name="label">Site: </ui:define>
                                              <h:outputText value="#{customerInformation[1].description}"/>
                                         </s:decorate>     
                                         
                                    </a4j:repeat>



      See what I mean?


      So what's the best way to handle this in two scenarios?


      1) when your data is read-only
      2) when your data is updateable (CUD)


        • 1. Re: using DTO with Seam apps
          israel.bgf

          That's my doubt too. It's incredible that is very difficult to find articles or blogs talking about aggregated entities, or custom results in JPA. Well here we use DTOs. But that's a good question Arbi.

          • 2. Re: using DTO with Seam apps
            jeanluc

            DTOs have their place when aggregating (parts of) entities. Sometimes the underlying queries are just projections from several tables, at others you simply want/know certain fields only. Indeed, with JEE5 they have limited uses, but when they do, they are the natural solution. Using Object[] and thus losing type safety and easy comprehension of the code just because 'a rule says so' is not good design.


            So DTOs can be used in both cases. For reads they are straightforward. For creates, they may be used when the UI supplies only a part of the attributes necessary for the full, persistable entity, but the logic to fill the missing values is based on business rules and shouldn't be left to the UI layer to do.


            • 3. Re: using DTO with Seam apps
              asookazian

              Generally, the first option isn't greatly appreciated by the client. So lets look at the second option. This approach involves determining exactly which columns you really need, and instanciating data-transfer JavaBean objects containing exactly those columns.

              select new CityItem(city.id, city.name, city.electrityCompany.name)
              from City city
              ...

              This technique is fast and efficient, and avoids the overheads of handling associated objects and of loading large numbers of persistent objects into the Hibernate session cache. The only downside is the need to create a dedicated data-transfer class for each query.

              source: http://www.javalobby.org/articles/hibernate-query-101/


              very good article...

              • 4. Re: using DTO with Seam apps

                Arbi Sookazian wrote on Aug 21, 2009 19:21:



                List<Object[]> myList = entityManager.createQuery(+
                " select foo, bar from Foo as foo join foo.bar as bar ").getResultList();



                Refactor:

                List<GenericDTO> myList = entityManager.createQuery(+
                " select new com.your.company.CustomerInformationDTO( foo, bar ) from Foo as foo join foo.bar as bar ").getResultList();





                I believe it is better to use List<Map> (no need to use a new class, and you can reference the values by human understandable name) :


                List<Map> myList = entityManager.createQuery(+
                " select new map( foo, bar ) from Foo as foo join foo.bar as bar ").getResultList();
                



                • 5. Re: using DTO with Seam apps
                  asookazian

                  I don't understand that solution.  Map is an interface for representing (storing) key/value pairs.


                  fname=John
                  fname=Larry
                  ...


                  What if there are more than two items in the select clause?


                  select foo, bar, baz from ...