1 2 Previous Next 29 Replies Latest reply on Jan 20, 2006 2:36 PM by coryvirok Go to original post
      • 15. Re: Interesting Client/EJB3Container design question
        martinganserer

        Hey guys,

        forget about catching lazy load exceptions in the client! This is something that you might can implement but take a look at you client code!!
        It will look terrible (especially if you have complex hierarchies) and you always have to add new or remove code if you change the fetch strategy in the object tree.
        I must admit that you might have to change the client code too when you work with a load level argument, but it is much easier to change only one argument!

        • 16. Re: Interesting Client/EJB3Container design question
          pego

          IMHO the load-level solution is the best: fast, effective and clean.

          I have a question, probably a little off-topic (however it's about ejb3 design)... :)

          Im approaching to design the server "session side" of an application.
          I've already done the "persistence side" using entity bean and it is quite complex (a lot of hierarchies and associations between entities).

          I thought that a good solution for the "session side" could be a sort of mapping between session and entity beans.

          Example
          Entities:
          Domain and Email (one Domain is associated to many Email)
          Session:
          DomainSession and EmailSession (EmailSession has a filed @EJB DomainSession to set/get the Domain associated to the Email)

          And what about hierarchies of entities?
          Is it possible to do hierarchies of Session Beans?
          Is it a good way?

          Thank for your help

          • 17. Re: Interesting Client/EJB3Container design question

            another issue is the propagation of relations ...
            if something wrong is done in client on the relations of object to be merged - those relations are also merged ...

            • 18. Re: Interesting Client/EJB3Container design question
              phon

               

              "pego" wrote:

              Im approaching to design the server "session side" of an application.
              I've already done the "persistence side" using entity bean and it is quite complex (a lot of hierarchies and associations between entities).


              again, i'm in the same situation as you are :) i finished creating most of my entities and until now i used one session bean to persist all of my entities created or modified on the client side. As all my entities inherit from a @EmbeddableSuperclass BaseEntity, i just have one persist-method in my session bean : public BaseEntity persist(BaseEntity e), which returns the entity to the client (with the ID filled in) and one retrieve-method : public Collection getEntitiet(String EQL).

              It worked good for small "helper" entities, which basically are involved in no other operations than CRUD operations, and are used in SPP fields. But now i'm moving on to "bigger" entities like Customers and Articles i find that i need some more specific persisting operations. So i make specific session beans for these entities. I don't think you can make a hierarchy of session beans. So i think i use a mixture between your idea (mapping every entity bean to session bean,which seems like a good intuitive option, but may leave you with to many session beans) and the general idea (one session bean handles all entity operations, which is with any complex system impossible).

              I'm no expert in EJB or J2EE, this is my first application i'm developping, so these are no facts, just my thoughts. Any comments are welcome!

              @silviu : are you referring to the design of cascade and lazy settings for relations ?

              • 19. Re: Interesting Client/EJB3Container design question

                yes, the cascade (but not lazy, because let's say you want to display some parts of relations but to update only one object on the tree)
                if you update the relations on the detached part ... and you call merge ... also the cascaded relations with MERGE | ALL are propagated .. so this creates a level of complexity

                • 20. Re: Interesting Client/EJB3Container design question
                  patrick_ibg

                  Just a thought... I do not know enough about the Hibernate internals to determine if this is a viable solution, but it seems to me that Lazy loading of detached objects is a concern best addressed by Hibernate itself.

                  Would it be possible to modify hibernate (at the cache, Proxy, Session or CGLIB enhancer) to know when a Lazy association is being fetched, and to go ahead and remotely fetch the association?

                  This would introduce security issues (mainly on restricting data visibility) but there could be workarounds...

                  On the client side, it would require logic tantamount to what Hibernate does on the server side. Then perhaps the client-server transport layer (RMI or whatever) can be abstracted so it looks like a JDBC driver...

                  Just thinking out loud.

                  • 21. Re: Interesting Client/EJB3Container design question
                    gavin.king

                    No, it is absolutely not possible.

                    Use a Seam-managed extended persistence context to solve this problem (or an EJB3 extended persistence context).

                    • 22. Re: Interesting Client/EJB3Container design question
                      phon

                      where can i read more about an Extended EJB3 Persistence Context ? Does it put the client and server in the same "session" so you don't have to implicitly inialize lazy attributes that you may need on the client ?

                      • 23. Re: Interesting Client/EJB3Container design question
                        pego

                        @Phon: I found something useful in EJB3 trailblazer http://trailblazer.demo.jboss.com/EJB3Trail/services/apptrans/index.html

                        Im testing the loadlevel solution with a simple application:
                        There are 4 entities A,B,C,D.
                        A has OneToMany associations with both B and D (C extends A, i'll use it for other tests)

                        I did the "loadlevel" method:

                        public A getA(long id, List<loadLevel> levelList) {
                         A a = manager.find(A.class,id);
                         if(levelList.contains(loadLevel.B)){
                         a.getListB();
                         }
                         if(levelList.contains(loadLevel.D)){
                         a.getListD();
                         }
                        return a;
                        }
                        


                        loadLevel is an enum and i pass the list of nested object i want to load.

                        But there is a problem, if i call the method as you see it throws a LazyInitializationException, but if i use the "use" the a.getListB() and a.getListD like below
                        public A getA(long id, List<loadLevel> levelList) {
                         A a = manager.find(A.class,id);
                         if(levelList.contains(loadLevel.B)){
                         System.out.println(a.getListB());
                         }
                         if(levelList.contains(loadLevel.D)){
                         System.out.println(a.getListD());
                         }
                        return a;
                        }
                        


                        Everythings goes ok, i have the prints on the server side and on the client i can reach the collections.
                        Any suggestion?

                        • 24. Re: Interesting Client/EJB3Container design question
                          phon

                          i haven't checked it yet as i'm not in the office for the moment but maybe an optimization of java is not to actually return the result when it's not necessasry (in the first case you don't use the result of a.getListB())

                          i'll try the code in the office today and i'll let you know

                          • 25. Re: Interesting Client/EJB3Container design question
                            lzdobylak

                            Hi again.
                            I tried this solution:
                            I set all relations in entity beans to LAZY. and any references to relations as "transient". So serialization makes them null when they are sent to client.
                            On the client side I check if the refernce is null I call some session bean method to fill it. I do this inside getter so it is transparrent......well i'm only thinking and didn't figured out everything yet but this is my idea.
                            What do you think?

                            • 26. Re: Interesting Client/EJB3Container design question
                              ciovo

                              If you have this scenario:
                              A is associated with C. B is associated with D. B extends A.
                              A, B, C and D are Entity Beans. A and B use the JOINED strategy for Inheritance.

                              What is the best way to represent this kind of scenario with Session Beans?
                              I thought about these 3 possibilities:

                              1- 4 Session Beans: Asession extends Bsession, A has @EJB Csession and C has @EJB Dsession.
                              2- 4 Session Beans: Bsession has @EJB Asession, A has @EJB Csession and C has @EJB Dsession
                              3- 3 Session Beans: 2 beans for C and D, and one Session Bean for the hierarchy.


                              What could be the best choice, if you also have in mind that A has other children and B too?
                              What is the "standard" solution using a EJB3 approach?
                              What solution gives the best implementation, is smarter than the other, and is not too heavy in the server side?

                              • 27. Re: Interesting Client/EJB3Container design question

                                Hi, Marin,

                                Your idea of LoadLevel is a a great idea. It solved most of my problems.

                                Here is my approch for a tipical senario of Customer-Order-LineItem-Product. only one session bean is needed, all the business rules are at the server side(EjbAction).

                                public interface BaseEntity extends Serializable{
                                void initLazyLoad(int loadLevel);
                                }

                                @Entity
                                @Table(name = "Customer")
                                public class Customer implements BaseEntity {
                                public static final int LOAD_LEVEL_ORDERS=0x01;
                                public static final int LOAD_LEVEL_LINEITEMS=0x02;
                                public static final int LOAD_LEVEL_PRODUCTS=0x04;
                                public void initLazyLoad(int loadLevel) {
                                if((loadLevel&LOAD_LEVEL_ORDERS)>0){
                                this.getOrders().size();
                                }
                                if(...){
                                ...
                                }
                                }

                                private Collection orders;
                                @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY,
                                mappedBy = "customer")
                                public Collection getOrders() {
                                return operations;
                                }

                                public void setOrders(Collection aOrders) {
                                this.orders = aOrders;
                                }
                                ...
                                }

                                public class Order implements BaseEntity {
                                ...
                                }


                                public class LineItem implements BaseEntity {
                                ...
                                }

                                public class Product implements BaseEntity {
                                ...
                                }


                                public interface SessionFacade {

                                public Object findById(Class aClass, Object pk, int loadLevel) throws Exception;

                                public Collection findByQuery(Class aClass, String sql) throws
                                Exception;

                                public Collection findByQuery(Class aClass, String sql,
                                String paramName, Object param) throws
                                Exception;

                                public Serializable perform(EjbAction ejbAction) throws Exception;

                                }

                                public interface EjbAction extends Serializable{
                                public Serializable perform(EntityManager manager) throws Exception;
                                }

                                @Stateless
                                @Remote(SessionFacade.class)
                                public class SessionFacadeImpl implements SessionFacade {
                                private @PersistenceContext EntityManager manager;

                                public Serializable perform(EjbAction ejbAction) throws Exception {
                                return ejbAction.perform(this.manager);
                                }

                                public Object findById(Class aClass, Object pk, int loadLevel) throws Exception{
                                Object o=manager.find(aClass, pk);
                                if(o instanceof BaseEntity){
                                BaseEntity b=(BaseEntity)o;
                                b.initLazyLoad(loadLevel);
                                }else{
                                logger.info("not a class of BaseEntity");
                                }
                                return o;
                                }

                                public Collection findByQuery(Class aClass, String sql, String paramName1, Object param1) throws Exception{
                                Query query=manager.createNativeQuery(sql, aClass);
                                query.setParameter(paramName1,param1 );
                                return query.getResultList();
                                }
                                ...
                                }

                                //execute at the server side.
                                public class LineItemAddEjbActionImpl
                                implements EjbAction {

                                public LineItem lineItem;
                                public int orderId;

                                public Serializable perform(EntityManager manager) throws Exception{
                                Order order=manager.find(Order.class, orderId,0);
                                Collection items=order.getItems();
                                if(items==null){
                                items=new Vector();
                                order.setItems(items);
                                }
                                items.add(lineitem);
                                return lineItem;
                                }
                                }


                                //execute at client side:
                                public class LineItemTest
                                extends TestCase {

                                public void testDisplay() throws Exception {
                                Order aOrder = getSessionFacade().findById(Order.class, 12, Order.LOAD_LEVEL_LINEITEMS);
                                Collection items = order.getItems();
                                for (LineItem item : items) {
                                logger.info(item.getId());
                                }

                                }

                                public void testAdd() throws Exception {

                                LineItem item=new LineItem();
                                item.set...

                                LineItemAddEjbAction action=new LineItemAddEjbAction();
                                action.orderId=12;
                                action.lineItem=item;
                                getSessionFacade().perform(action);

                                }
                                }

                                Let me know if you have a better pattern

                                • 28. Re: Interesting Client/EJB3Container design question
                                  coryvirok

                                  I'm doing something similar. However, I notice that you are making sure the items actual get loaded via

                                  this.getOrders().size();


                                  Have you found that this does not in fact do anything when compiled and run due to Java optimizations? This was previously posted about on this thread and still hasn't really been addressed. The only way I've found to get around the java optimization is to actually use an output stream to print the loaded object. I do this in place of the above code via:

                                  log.debug(this.getOrders());


                                  This only works if the logging level is set to "debug" but it does the job. Now there's the obvious problem of wasted CPU cycles printing out a value that you really don't care about.

                                  Comments and suggestions welcome,
                                  Thanks,
                                  - Cory

                                  • 29. Re: Interesting Client/EJB3Container design question
                                    coryvirok

                                    Thought yall might find this useful, but beware of the side-effects of DOOM!
                                    :o)

                                    protected static void loadChildren(Object obj, int loadLevel) {
                                     if(loadLevel > 0) {
                                     Method[] methods = obj.getClass().getDeclaredMethods();
                                     Object result = null;
                                     for(Method m : methods) {
                                     if(m.getName().startsWith("get")) {
                                     try {
                                     result = m.invoke(obj, (Object[])null);
                                     log.debug(result.hashCode());
                                     loadChildren(result, loadLevel - 1);
                                     } catch (Throwable t) {
                                     //Ignore
                                     }
                                     }
                                     }
                                     }
                                     }


                                    If you don't feel like figuring out what it does, it's a depth first loader of nested objects that uses a load level to determine how "deep" to go. It prints prints out a hash of the object in order to force the optimizer to not throw away the loading code.

                                    Watch out for any "getXXX" method that has side effects. I don't think putting anything other than a single return statement inside a getXXX would be a good idea, but you never know.

                                    - Cory

                                    1 2 Previous Next