Announcement Announcement Module
Collapse
No announcement yet.
How Common to Spring Manage Beans Created By Hibernate? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • #16
    It would be good to quantify the performance cost of doing this. Anybody done a benchmark? (I would do it, but I don't have time at the moment.)

    To support this usage I've slightly optimized prototype creation in Spring 1.1, and hope to dramatically reduce the overhead in common cases in Spring 1.2 by using clone() where possible.

    Comment


    • #17
      I am about to integrate the SpringInterceptor into our project. This is a dream come true, being able to implement business logic into our beans.

      Would anyone know if this is something Spring will include in future releases?

      Thanks!
      Seth

      Comment


      • #18
        In Rod and Juergens book they have BusinessObjectManager and BusinessObject. We further refactored business strategies from business objects like BusinessStrategy.

        Our BusinessObjectManager is wired up by dependency injection. It is injected with BusinessStrategy. BusinessObjectManager does not operate on BusinessStrategy but passes it by to BusinessObject.

        Code snippet taken from BusinessObjectManager:
        Code:
        private BusinessStrategy businessStrategy;
        private BusinessObjectDAO businessObjectDAO;
        ...
          BusinessObject bo = businessObjectDAO.loadBusinessObject(id);
        ...
          bo.executeBusinessStrategy(businessStrategy);
        ...
        Code snippet taken from BusinessObject:
        Code:
        ...
        public void executeBusinessStrategy(BusinessStrategy businessStrategy) {
          businessStrategy.execute(this);
        }
        What do you think about this?
        Are there any issues related with our architecture?

        thomas

        Comment


        • #19
          Oliver, thanks for posting that code. I think we should definitely consider including something based on it in Spring 1.2. (Btw, "setPersistantClassBeanNames" should be "setPersistentClassBeanNames").

          As I've said before, my main concern is the performance. I do mean to optimize the prototype functionality where possible in 1.2, but need to be able to quantify the resulting performance.

          If you were to contribute this code as a basis, that would be great. If you or someone else could also contribute a simple app using it (or use case involving one or two mapped classes, possibly returning large result sets) that we could profile and benchmark that would be even better. I could then verify the actual overhead, and the effect of my optimizations.

          Comment


          • #20
            Ollie's approach is quite interesting and I'm sure it's helpful in many situations. I found Fower's POEAA p 134, "Service Layer: How It Works" very informative. If you've got the book and are about to put everything into domain objects, may I suggest having a read of it first.

            In summary it is helpful to break "business logic" into its two forms: domain logic and application logic. Domain logic deals with the problem domain alone and should be in the business objects. Application logic deals with the application responsibilities (gateways, DAOs etc) and should be in service objects.

            By nature application responsibilities are probably singletons and managed in a Spring application context. They also tend to be associated with AOP concerns like transactions and security. It's therefore a natural fit and quite easy to follow this advice of using a service layer for application responsibilities.

            Take an example:

            Our Product class may have an updateTax(TaxRate effectiveRate), which is domain logic as it deals solely with the problem domain. We'd probably re-use our Product in other applications as it properly models the problem domain. If a requirement was then to send an email to report unusual TaxRate usages to the financial accountant, that is application logic. In this case there should be a ProductService.updateTax(Product product, TaxRate effectiveRate) so that ProductService can send the email. Of course we could have Product include a setEmailGateway(EmailGateway) method, automagically populated from Spring, but then Product is not re-usable in other applications as we've "polluted" it with application logic and application interfaces. It gets even more ugly as you add more and more application services. Take security. If we decide only a person with ROLE_ACCOUNTANT for a given product family can update the tax rate, you now also need Product to contain setAccessDecisionManager, setAclManager and some way of ensuring the ContextHolder contains a valid Authentication object. Your previously simple updateTax method will have become an ugly non-resuable mix-match of application and domain logic.

            So AFAIK there is nothing technically preventing injecting collaborators into domain object instances. But it seems easier to me to just stick with what Spring makes easy and allows maximum reuse: delineation between application and domain logic, in service layers and domain objects respectively. If you need a singleton collaborator or AOP functionality (security, transactions), it's probably application logic.

            Comment


            • #21
              And yet there is a better solution in my opnion

              Take a look at AspectJ and Spring integration. It is in its infancy but can be pretty powerfull already.

              Basically, the problem you are having is if your object graph is pretty big you would not want to let Spring create proxies for it (performance issues), on the other hand you would need the business objects to have dependencies injected somehow.

              So the most optimal solution to the problem would be, if you somehow could use the dependent objects inside your business objects (that would be created by Hibernate), but inject dependencies into them say at construction time. This is where AspectJ and Spring integration comes in very handy. Basically you pre-compile your business objects with AspectJ and let Spring inject deps to the aspect itself. At construction time AspectJ's aspect would wire those dependents to your non-proxied BOs. It is pretty neat I must say. Read chapter 6 of the new Spring (1.1) docs for more info on this.

              There are of course disadvantages:

              1. AspectJ's aspects are not dynamic, so if you have multiple deployments of the BOs you would need to pre-compile for each deployment.

              2. AFIKT there is a somewhat signifact problem where perthis, pertarget and percfolw aspects of AspectJ are used.

              HTH

              Comment


              • #22
                I'm not sure what we need is that each domain object contains the injected session and/or sessionFactory. All you need is a way to map the domain class to the sessionFactory it's coming from - and with spring that mapping shouldn't really be hard to do.

                I can imagine a singleton bean containing Map<Class, SessionFactory> and static getSessionFactory(Class) and getSession(Class) (using SessionFactoryUtils). If you want to, you can fill the map using the Configuration.getClassMappings().

                My guess is this should be enough. If you have one class coming from multiple sessionFactories... Well, you are out of luck anyway. :P

                Comment


                • #23
                  Here is some sample code for my mapping bean idea:

                  Code:
                      <bean name="mySessionUtil" class="dejanp.spring.HibernateSessionUtil">
                          <property name="localSessionFactoryBeans">
                              <list>
                                  <ref bean="&amp;mySessionFactory"/>
                              </list>
                          </property>
                          <property name="mappings">
                              <map>
                                  <entry key="dejanp.test.TestDomainClass">
                                      <ref bean="mySessionFactory"/> 
                                  </entry>
                              </map>
                          </property>
                      </bean>
                  So, one can use a list of LocalSessionFactoryBeans or a manually filled map or both to init the bean.

                  Code:
                  package dejanp.spring;
                  
                  import net.sf.hibernate.Session;
                  import net.sf.hibernate.SessionFactory;
                  import net.sf.hibernate.cfg.Configuration;
                  import net.sf.hibernate.mapping.PersistentClass;
                  import org.springframework.orm.hibernate.LocalSessionFactoryBean;
                  import org.springframework.orm.hibernate.SessionFactoryUtils;
                  
                  import java.util.HashMap;
                  import java.util.Iterator;
                  import java.util.List;
                  import java.util.Map;
                  
                  public class HibernateSessionUtil &#123;
                  
                      private static Map<String, SessionFactory> map;
                  
                      public HibernateSessionUtil&#40;&#41; &#123;
                          map = new HashMap<String, SessionFactory>&#40;&#41;;
                      &#125;
                  
                      public void setLocalSessionFactoryBeans&#40;List<LocalSessionFactoryBean> localSessionFactoryBeans&#41; &#123;
                          for &#40;LocalSessionFactoryBean localSessionFactoryBean &#58; localSessionFactoryBeans&#41; &#123;
                              addLocalSessionFactoryBean&#40;localSessionFactoryBean&#41;;
                          &#125;
                      &#125;
                  
                      private void addLocalSessionFactoryBean&#40;LocalSessionFactoryBean localSessionFactoryBean&#41; &#123;
                          Configuration config = localSessionFactoryBean.getConfiguration&#40;&#41;;
                          SessionFactory sessionFactory = &#40;SessionFactory&#41; localSessionFactoryBean.getObject&#40;&#41;;
                          Iterator classMappings = config.getClassMappings&#40;&#41;;
                          while &#40;classMappings.hasNext&#40;&#41;&#41; &#123;
                              PersistentClass persistentClass = &#40;PersistentClass&#41; classMappings.next&#40;&#41;;
                              String name = persistentClass.getMappedClass&#40;&#41;.getName&#40;&#41;;
                              map.put&#40;name, sessionFactory&#41;;
                          &#125;
                      &#125;
                  
                      public void setMappings&#40;Map<String, SessionFactory> map&#41; &#123;
                          HibernateSessionUtil.map.putAll&#40;map&#41;;
                      &#125;
                  
                      private static SessionFactory getSessionFactory&#40;Class c&#41; &#123;
                          return getSessionFactory&#40;c.getName&#40;&#41;&#41;;
                      &#125;
                  
                      private static SessionFactory getSessionFactory&#40;String name&#41; &#123;
                          return map.get&#40;name&#41;;
                      &#125;
                  
                      public static Session getSession&#40;Class c, boolean allowCreate&#41; &#123;
                          return SessionFactoryUtils.getSession&#40;getSessionFactory&#40;c&#41;, allowCreate&#41;;
                      &#125;
                  
                  &#125;
                  Typical usage would be something like:

                  Code:
                  public class Order&#123;
                      ....
                  
                      public Collection getSomeOrderItems&#40;...&#41; &#123;
                          Session session = HibernateSessionUtil.getSession&#40;Order.class, false&#41;;
                          ...
                      &#125;
                  &#125;

                  Comment


                  • #24
                    Another way to do this that is simpler and might perform better: use one of the autowire methods from AutoWireCapableBeanFactory to configure the Hibernate object via Setter Injection, using a dependencyCheck value of false. This way the interceptor should be quite simple. Hibernate would still use new to create objects: Spring would just wire some setters. There would be no need for a prototype definition or any other change in the Spring definitions. The interceptor would need to keep track of which Hibernate-managed classes it wished to wire using Spring.

                    Comment


                    • #25
                      Ahh... that's a great idea! And, it supports my idea of "Autowire by Prototype". I don't mind declaring one abstract prototype in my context. I could then create an autowire type with semantics of "Autowire physical bean with this abstract prototype bean as an example". I'm not sure that's any better than the other autowire types (by name, by type, etc), but it might be useful.

                      Great suggestion, I think I'll switch over to using this method. The Hibernate Interceptor can fallback to the autowire method if it doesn't find a Prototype that matches.

                      Thanks!
                      Seth

                      ps BTW I've been using this Interceptor for a while now, and it works great. It's really simplified our world, allowing us to perform business logic on regular old beans now. So liberating! Now our Facades are simply transaction boundaries, and that's it.

                      Comment


                      • #26
                        Originally posted by adepue
                        1. What about the client? Do business objects cross the service (facade) boundary, or do you copy everything over into another graph (DTOs?) before sending to the client? If you send the business objects themselves, you would have to rewire all their dependencies on the client side... if you don't send the business objects, then the client is forced to use the facade to access all business logic (good or bad, you decide).
                        Also, what if your boundary is over SOAP where your BOs (which have bidirectional references) have to be serialized into XML. This is our scenario and we ended up using DTOs to transfer data back and forth, but find the conversion between the two tedious and to clutter our web service layer somewhat (we continue to use BOs from the application layer down). We've resorted to reflection, recursion and BeanUtils to convert object graphs between DTOs and BOs with good success, but I was wondering if there is a better solution.

                        I'm not sure if it's clear to me how Interceptors can help to that end.

                        TIA,
                        Lou

                        Comment


                        • #27
                          Thanks to Seth, who sent me the code, and Oliver, who originally wrote it, I'm in the process of adding this feature in the Spring sandbox. I've done quite a bit of refactoring as I've pulled out a ChainedInterceptorSupport class, as Seth suggested, and also a DependencyInjectionAspectSupport class, which can provide a superclass for an AspectJ/AspectWerkz aspect that does autowiring. I've also added support for autowiring by property (by type or name) as well as the present prototype approach. This will enable much less verbose configuration in simple cases.

                          Rgds
                          Rod

                          Comment


                          • #28
                            I've discussed with Andy Jefferson (co-lead of JPOX JDO) adding support in JPOX to enable something like this (though a PersistenceManagerListener mechanism), and I'll discuss it with Patrick Linskey of SolarMetric when I see him at JAOO next week. So hopefully we soon should not merely have Hibernate support for this idiom!

                            Comment


                            • #29
                              Originally posted by Rod Johnson
                              I've discussed with Andy Jefferson (co-lead of JPOX JDO) adding support in JPOX to enable something like this
                              CVS HEAD for JPOX now provides a PersistenceInterceptor which allows hooks for load, delete and store operations (load not yet fully implemented) on the PersistenceManagerFactory. This will be released in a forthcoming 1.1.0-alpha-3 in about a week.

                              Comment


                              • #30
                                Originally posted by andy
                                CVS HEAD for JPOX now provides a PersistenceInterceptor
                                This will be updated in the future, as a full JDO 2.0 nears, into "LifecycleListener" - a standard JDO 2.0 facility.

                                Comment

                                Working...
                                X