9 Replies Latest reply on May 3, 2005 11:49 AM by elkner

    Lazy stuff not usable at all on the client side ?

    elkner

      Actually I've a big problem, to understand, how a client should/can use the lazy fields of an entity.

      Up to now, my conclusion is, that lazy stuff can't be used on the client side at all, since (at least with jboss) we have a complete inconsistent behavior. E.g.:

      private Collection<A_Product> products;
      
       @ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE },
       fetch = FetchType.LAZY)
       @AssociationTable(table = @Table(name="products_to_categories"),
       joinColumns = {
       @JoinColumn(name="categories_id", referencedColumnName="categories_id")
       },
       inverseJoinColumns = {
       @JoinColumn(name="products_id", referencedColumnName="products_id")
       })
       public Collection<A_Product> getProducts() {
       return products;
       }


      But even if I try to access the products via a Dao in the following way, I still get LazyInitializationException: failed to lazily initialize a collection..., when I try to acces the collection on the client side.

      public Collection<A_Product> getProducts(A_Category category) {
       if (category == null) {
       return new ArrayList<A_Product>(0);
       }
       manager.refresh(category);
       return category.getProducts();
       }


      So what I have, is IMHO NOT a POJO but something beetween a POJO and RMI object, since the "pseudo" POJO still tries to invoke methods on the server side, which wrt. lazy objects or at least collections miserably fails., because it returns not the required object but a kind of "remote" reference to a not-working "thing".

      So, AFAIU at least at the moment and on the client side, EJB3 is not, what it ought to be.

      With tradional RMI one would probably simply call something like rmiObject.getProducts() and than one would get the serialized collection.

      Why is this different handled in EJB3 ? Do I need to write a DAO wrapper for every lazy object/collection of an entity (like return manager.getProducts().toArray[new A_Product[0]) ?
      But using the latter approach, one gets a "could not initialize proxy - no Session" when one tries to access a property of the product, even if it is not lazy (just e.g. a String) ...

      Of course, one could probably use EAGER, but if you have tried that with a little bit more complex database (i.e. more than 1 or 2 [in]direct references), one quickly sees, that this is not an option, since this is incredible slow and unneccessarily loads stuff into memory, which is not needed :(((

      So, has anybody a kind of howto or "best practice" tip, how lazy fields of an entity can be used on the client side? Or is the implementation in jboss simply bogus ?

        • 1. Re: Lazy stuff not usable at all on the client side ?
          bill.burke

          properties/associations can only be lazily loaded when they are managed.

          EJB3 Entity beans are plain java objects. Lazy fields cannot be used on the client side. Either put a stateless bean in front of the EntityManager, or when you obtain a bean on the server side to be passed back to the client, prefetch the lazy properties you are interested in.

          from Item i left join fetch i.bids
          


          For example, this forces the lazily loaded bids association to be fetched when the query returns instances of Item.

          • 2. Re: Lazy stuff not usable at all on the client side ?
            holtak

             

            "elkner" wrote:
            So, has anybody a kind of howto or "best practice" tip, how lazy fields of an entity can be used on the client side? Or is the implementation in jboss simply bogus ?


            Value Objects or DTO`s

            Forget about the prefetch thing, programmers are still human :-( (I hope this is going the change someday)

            We`ve tried the prefetch approach, it will end in unmaintainable, untestable code as soon as u divide your team into presentation(client) and business logic (server) teams where they don`t care about each other (hey man! works as designed...).


            NO jboss implementation is not bogus, works as designed... (in the JSR) :-)



            • 3. Re: Lazy stuff not usable at all on the client side ?
              epbernard

              I don't really see the actual difference etween changing a DTO because the presentation layer needs a property than updating the fetching strategy for the same reason except that the former involves more code.

              • 4. Re: Lazy stuff not usable at all on the client side ?
                elkner

                 

                "bill.burke@jboss.com" wrote:
                properties/associations can only be lazily loaded when they are managed.


                And that's why I setup the dao method getProducts(A_category c), where the EntityManager refreshes the owner (c) and than calls the method with the LAZY annotation - since all hapens in one Tx, I assume, that c is in a managed state and thus assumed, that the collections gets fetched - but probably wrong.

                "bill.burke@jboss.com" wrote:
                EJB3 Entity beans are plain java objects. Lazy fields cannot be used on the client side. Either put a stateless bean in front of the EntityManager


                Hmmm - I probably do not understand that. Can you give a little example? At the moment, I access all data via SLSBs with an @Inject ed EntityManager . So ... ?


                "bill.burke@jboss.com" wrote:
                , or when you obtain a bean on the server side to be passed back to the client, prefetch the lazy properties you are interested in.

                from Item i left join fetch i.bids
                


                For example, this forces the lazily loaded bids association to be fetched when the query returns instances of Item.


                Hmm - I tried this too and still get the "no session" error.

                But investigating the problem a little bit deeper, shows me, that IMHO hibernate seems to be quite bogus: sessions are not the problem, even if the exception tries (usually successfully) to mislead us into this wrong direction.

                The problem seems, that hibernate is unable to handle empty results/collections/null. I verified, that: if I use your suggested left outer join, there are categories, which have no products assigned to it and hence the appropriate column[s] in the result set are null (used the same SQL statement at cmdline, which comes up in the log file).

                I guess, that hibernate generates blindly a collection of proxies for each row (product) in the result set and if one tries to get a reference to a product via the returned collection, this works, because one gets the reference to a "product"-proxy. But if one tries to access a property of the product-proxy, hibernate finds null (oops) and throws the misleading exception.

                So IMHO hibernate is the problem, since it does not return an empty collection (as a human beeing would assume) but a collection of "null"-proxies ...

                Does this makes sense to you ?

                • 5. Re: Lazy stuff not usable at all on the client side ?
                  epbernard

                  Hibernate is not bogus.
                  Bill is right, you're accessing the collection while it is no longer managed. Really we can't help you wo showing a full simple "non working" test case.

                  • 6. Re: Lazy stuff not usable at all on the client side ?
                    elkner

                     

                    "epbernard" wrote:
                    Hibernate is not bogus.
                    Bill is right, you're accessing the collection while it is no longer managed. Really we can't help you wo showing a full simple "non working" test case.


                    As I said, it is managed, but has null values! To verify, create something with an association table, which contains on one side a key for an entity, whereby the appropriate table does not contain a row with the specified key. E.g.

                    Table A Table AB Table B
                    PK FKA FKB PK
                    A1 A1 B5 B1
                    A2 A1 B6 B2
                    A3 A1 B7 B3
                    A4 A4 B3 B4
                     A4 B5
                    
                    @Entity A {
                     ...
                     ManyToMany(...)
                     public Collection<B> getBees() {
                     ...
                     }
                    }
                    


                    For A1 one should get an empty collection, since B5..B7 do not exist, for A4 the collection should contain B4 only. However, the collection contain proxies for non-existent entities ("null"-proxies) and throw the misleading "no-session" exception, when one tries to access a property of such an proxy ...

                    • 7. Re: Lazy stuff not usable at all on the client side ?
                      anupama24

                      I saw the same problem with completely different client. I am running Preview 3, on a JSP client.

                      There is a LazyInitializationException when the content of the collection is empty, even though the property was previously accessed inside the stateless session bean.

                      Here were the steps -
                      Access the lazily initialized property in the stateless session bean to initialize it. The accessor returned the collection. Did not traverse the collection.
                      Returned the bean to the client. The property should be initialized already.
                      The client tried to access the property, and got LazyInitializationException.

                      It appears that hibernate is trying to access the collection from database when it is empty...perhaps unable to distinguish between empty collection and un-accessed collection?

                      • 8. Re: Lazy stuff not usable at all on the client side ?
                        epbernard

                        elkner, your data are *broken* the spec does not cover this kind of behavior.
                        Hibernate3 might be able to cover these cases, but I must admit I have to check.
                        anupama24, a collection is not accessed when you do parent.getChildren().
                        You have to access it for real parent.getChildren().size() for eg.
                        you can also do Hibernate.initialize( parent.getChildren() ), which is not portable though.

                        • 9. Re: Lazy stuff not usable at all on the client side ?
                          elkner

                           

                          "epbernard" wrote:
                          elkner, your data are *broken*

                          Yes, took me quite some time, to find out, that we are not living in an ideal world, where one can assume, that if a DB application is working without any problem, the DB is consistent ... :(

                          However, at least a better exception message (e.g. like NoSuchEntity or InconsistentData) could have saving me some time ....

                          "epbernard" wrote:
                          the spec does not cover this kind of behavior.
                          Hibernate3 might be able to cover these cases, but I must admit I have to check.


                          Well. I'm not sure, whether the spec has to cover it. If I want to get the children of A4, I would do something like that:
                          select B.* from AB join B on B.pk=AB.pkB where pkA='A4';


                          and this would be an empty resultset for 'A1' and a one elment collection for 'A4' and avoids a table scan on A completely. So actually not a spec problem but a query setup problem ...