Page 1 of 6 123 ... LastLast
Results 1 to 10 of 58

Thread: How Common to Spring Manage Beans Created By Hibernate?

  1. #1
    Join Date
    Aug 2004
    Location
    Hawaii, US
    Posts
    225

    Default How Common to Spring Manage Beans Created By Hibernate?

    Hello,

    We've heard time and time again to "put business logic inside your business objects". Most of the time, full business logic requires dependencies to be injected in my business object. The business objects themselves (Business, Account, etc) are managed by Hibernate. Their dependencies are/should be managed by Spring.

    How common is this? If my business object is something that comes from the database, but requires external dependencies, then Spring must create and wire up my object.

    This seems fair and good, but I haven't seen many examples of this usage. It makes me wonder if my business domain objects (those that come from the database) should ever rely on external dependencies.

    My gut says yes: keep business logic in business object. Note: this does not mean DAO type actions.

    Of course, I could keep the business objects free from external dependencies, but then their ability to do work is greatly lessened.

    Thoughts? Comments? War stories?

    Thanks very much!
    Seth

  2. #2
    Join Date
    Aug 2004
    Location
    Melbourne, FL
    Posts
    2,794

    Default

    Don't forget about good ol' message passing! I find it helpful to pass in parameters to my business objects via messages telling them to "do some work" associated with the data they encapsulate. This is often better than asking them for the data so I can do the work for them.

    For example, in my most recent application, I developed a fairly sophisticated data loader that could parse custom file formats and translate them into a normalized database schema, performing type conversion and data transformations. In this system I had a central "loader service" (a workflow object) which encapsulated the general parse / load algorithm. I also had individual parses that encapsulated logic to parse a particular file format into fields, and separate metadata objects that would then perform any necessary type conversions/data transformations on parsed field values. The result was a bunch of business objects working together, with the workflow object brokering a good deal of the message passing, telling the other objects to "do work" as part of a larger algorithm. Why was this good? Data and behaivior were kept together, the workflows algorithm was kept completely generic (meaning it was coded once and hasn't changed since even though our system has grown to support a large number of custom formats and conversion rules.)

    Just food for thought!

    Keith

  3. #3
    Join Date
    Aug 2004
    Location
    Hawaii, US
    Posts
    225

    Default

    Don't forget about good ol' message passing! I find it helpful to pass in parameters to my business objects via messages telling them to "do some work" associated with the data they encapsulate. This is often better than asking them for the data so I can do the work for them.
    Exactly my point!

    This is exactly what I want to do with my business objects:

    BusinessObject bo = facade.load(pk);
    bo.myBusinessMethod();
    facade.save(bo);

    where bo.myBusinessMethod() might do something with velocity, for instance. So I'll need Spring to wire up my BusinessObject, which is loaded from DB via Hibernate.

    Just wondering how common that is, to wire Hibernate objects using Spring. Seems to be pretty important if we want to keep business logic inside business objects.

    Seth

  4. #4
    Join Date
    Aug 2004
    Location
    Mount Joy, PA
    Posts
    34

    Default

    If I understand what you're saying correctly, the thing you will need to worry about is performance when you're loading a large set of Spring-managed domain objects from the db. Since each Hibernate-loaded domain object obviously must be a prototype (non-singleton) in the Spring context, the performance hit will come when you need to wire up 10,000 new instances of Foo. If this is not a problem, the concept sounds viable.

    The only other downside I can think of is the added complexity in your business objects (where did that object come from). However, this is a weak argument because the Spring application context provides a very valuable kind of "documentation" about how objects within the application interact. A Spring context provides a means to distinguish the forrest from the trees when you need to learn what an application does in an overall sense vs. how it handles the details.
    ~ Daniel Miller

  5. #5
    Join Date
    Aug 2004
    Location
    Hawaii, US
    Posts
    225

    Default

    The only other downside I can think of is the added complexity in your business objects
    That's just it... if we're supposed to keep business logic in business objects, then a little complexity (in the form of depedencies) is unavoidable.

    There seems to be two different definitions of the term "business object" out there.

    1) The domain objects that come from the database. The objects from the business model.

    2) Where the work is done.

    It seems that we should be combining definitions 1 and 2. Is that a safe assumption?

    Here are the rule breakers:

    1) Business Object is not a transaction demarcation. That is the role of the Facade.
    2) Business Object is not concerned with persistence. That is the role of the DAO.

    For a Business Object to contain Business Logic, it will inevitably need dependencies. This is something I haven't seen too much in examples. All the examples only show CRUD operations as the business logic.

    Just trying to find out how practical it is to keep business logic inside business objects. And if my definition of Business Object is even correct.

    I'll post a Use Case that I think will clearly outline this problem if that would help. The more best practices we can catalogue, the better.

    Seth

  6. #6
    Join Date
    Aug 2004
    Location
    Mount Joy, PA
    Posts
    34

    Default

    Martin Fowler has some valuable insite on this topic in his article on the Anemic Domain Model.

    This quote sums it up fairly well:

    In general, the more behavior you find in the services, the more likely you are to be robbing yourself of the benefits of a domain model. If all your logic is in services, you've robbed yourself blind.
    After reading that, my second argument about complexity seems null and void. The domain model is where the logic belongs, which means a bit of complexity is, as Seth said, unavoidable.
    ~ Daniel Miller

  7. #7
    Join Date
    Aug 2004
    Location
    Hawaii, US
    Posts
    225

    Default

    That's a great quote! Thanks for posting.

    So, back to my original question/observation: If we're supposed to put business logic into business objects, then business objects need dependency injection. And if that's true, then objects loaded by persistence should be managed by Spring.

    That seems like a logical conclusion. So where are the examples?

    Of course, it's easy to figure out, and some examples have been posted to the mailing list in the past. It would be really nice to see this type of practice in some of the sample apps included with Spring. Granted, to warrant this, a more complex business operation other than CRUD would be needed.

    Leading off of this thread (good thread, and thanks for all the insight!) I've got another observation of Best Practice vs Real World. I'll start another thread to see what others think.

  8. #8
    Join Date
    Aug 2004
    Posts
    229

    Default

    I would tend to agree with you, though this brings up other questions. Two other thoughts off the top of my head:
    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).
    2. It sure would be nice if Spring had a built in facility for managing Hibernate objects. Maybe there is one and I'm not aware of it? I think right now you would have to implement a custom Hibernate interceptor, no? Anyway, Spring is geared for one time context initialization from a performance perspective... I wonder how well it would perform when injecting into thousands of instances on the fly?

  9. #9
    Join Date
    Aug 2004
    Location
    Hawaii, US
    Posts
    225

    Default

    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).
    You bring up a good point. While your Business Objects still contain all the logic, there still is a need (sometimes) for a Facade. A good use case is remote clients, as you have pointed out. However, even with a Facade, all the logic still remains in the Business Object. The only logic in the Facade ever is to break down its course grained interface into one or more calls into the business logic world.

    2. It sure would be nice if Spring had a built in facility for managing Hibernate objects. Maybe there is one and I'm not aware of it? I think right now you would have to implement a custom Hibernate interceptor, no? Anyway, Spring is geared for one time context initialization from a performance perspective... I wonder how well it would perform when injecting into thousands of instances on the fly?
    There is, and I forget what it is at the moment. I don't know if it's builtin, but the facility is there and its pretty straight forward.

    Sounds like a good example candidate for the Wiki, no?

  10. #10
    Join Date
    Aug 2004
    Location
    Melbourne, Australia
    Posts
    335

    Default

    It sure would be nice if Spring had a built in facility for managing Hibernate objects. Maybe there is one and I'm not aware of it? I think right now you would have to implement a custom Hibernate interceptor, no? Anyway, Spring is geared for one time context initialization from a performance perspective... I wonder how well it would perform when injecting into thousands of instances on the fly?
    Check out the following:
    http://thread.gmane.org/gmane.comp.j...work.user/3402
    http://thread.gmane.org/gmane.comp.j...work.user/3513

    And here is the code for the interceptor
    Code:
    /*
     * SpringInterceptor.java
     *
     */
    
    import java.io.Serializable;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Properties;
    
    import net.sf.hibernate.CallbackException;
    import net.sf.hibernate.HibernateException;
    import net.sf.hibernate.Interceptor;
    import net.sf.hibernate.SessionFactory;
    import net.sf.hibernate.type.Type;
    
    import org.springframework.beans.BeanWrapper;
    import org.springframework.beans.BeanWrapperImpl;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.BeanFactoryAware;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.beans.propertyeditors.ClassEditor;
    import org.springframework.util.StringUtils;
    
    /**
     * 
     * @author Oliver Hutchison
     * @see Interceptor
     */
    public class SpringInterceptor implements Interceptor, InitializingBean,
            BeanFactoryAware {
        private BeanFactory beanFactory;
    
        private SessionFactory sessionFactory;
    
        private String sessionFactoryBeanName;
    
        private Map persistantClassBeanNames = new HashMap();
    
        private Interceptor delegate;
    
        /**
         * Constructor
         */
        public SpringInterceptor() {
            super();
        }
    
        public void setBeanFactory(BeanFactory beanFactory) {
            this.beanFactory = beanFactory;
        }
    
        public void setSessionFactory(SessionFactory sessionFactory) {
            this.sessionFactory = sessionFactory;
        }
    
        public void setSessionFactoryBeanName(String sessionFactoryBeanName) {
            this.sessionFactoryBeanName = sessionFactoryBeanName;
        }
    
        public void setPersistantClassBeanNames(Properties persistantClassBeanNames) {
            ClassEditor ce = new ClassEditor();
    
            for (Iterator i = persistantClassBeanNames.keySet().iterator(); i
                    .hasNext();) {
                String className = (String) i.next();
                String beanName = persistantClassBeanNames.getProperty(className);
    
                ce.setAsText(className);
    
                Class clazz = (Class) ce.getValue();
    
                this.persistantClassBeanNames.put(clazz, beanName);
            }
        }
    
        public void setDelegateInterceptor(Interceptor delegateInterceptor) {
            this.delegate = delegateInterceptor;
        }
    
        public void afterPropertiesSet() {
            if (beanFactory == null) {
                throw new IllegalArgumentException("beanFactory is required");
            }
    
            if ((sessionFactory == null)
                    && !StringUtils.hasText(sessionFactoryBeanName)) {
                throw new IllegalArgumentException(
                        "sessionFactory or sessionFactoryBeanName is required");
            }
        }
    
        /**
         * @see net.sf.hibernate.Interceptor#instantiate(java.lang.Class,
         *      java.io.Serializable)
         */
        public Object instantiate(Class beanClass, Serializable id)
                throws CallbackException {
            String name = (String) persistantClassBeanNames.get(beanClass);
            if (name == null) {
                if (delegate == null) {
                    return null;
                } else {
                    return delegate.instantiate(beanClass, id);
                }
            }
            if (beanFactory.isSingleton(name)) {
                throw new UnsupportedOperationException("Bean name [" + name
                        + "] must be a prototype. i.e. singleton=\"false\"");
            }
            Object newEntity = beanFactory.getBean(name);
            try {
                BeanWrapper wrapper = new BeanWrapperImpl(newEntity);
    
                wrapper.setPropertyValue(getSessionFactory().getClassMetadata(
                        beanClass).getIdentifierPropertyName(), id);
            } catch (HibernateException e) {
                throw new CallbackException(
                        "Error getting identifier property for class " + beanClass,
                        e);
            }
            return newEntity;
        }
    
        private SessionFactory getSessionFactory() {
            if (sessionFactory == null) {
                sessionFactory = (SessionFactory) beanFactory
                        .getBean(sessionFactoryBeanName);
            }
    
            return sessionFactory;
        }
    
        /**
         * 
         * @see net.sf.hibernate.Interceptor#onLoad(java.lang.Object,
         *      java.io.Serializable, java.lang.Object[], java.lang.String[],
         *      net.sf.hibernate.type.Type[])
         */
        public boolean onLoad(Object entity, Serializable id, Object[] state,
                String[] propertyNames, Type[] types) throws CallbackException {
            if (delegate != null) {
                return delegate.onLoad(entity, id, state, propertyNames, types);
            } else {
                return false;
            }
        }
    
        /**
         * @see net.sf.hibernate.Interceptor#onFlushDirty(java.lang.Object,
         *      java.io.Serializable, java.lang.Object[], java.lang.Object[],
         *      java.lang.String[], net.sf.hibernate.type.Type[])
         */
        public boolean onFlushDirty(Object entity, Serializable id,
                Object[] currentState, Object[] previousState,
                String[] propertyNames, Type[] types) throws CallbackException {
            if (delegate != null) {
                return delegate.onFlushDirty(entity, id, currentState,
                        previousState, propertyNames, types);
            } else {
                return false;
            }
        }
    
        /**
         * @see net.sf.hibernate.Interceptor#onSave(java.lang.Object,
         *      java.io.Serializable, java.lang.Object[], java.lang.String[],
         *      net.sf.hibernate.type.Type[])
         */
        public boolean onSave(Object entity, Serializable id, Object[] state,
                String[] propertyNames, Type[] types) throws CallbackException {
            if (delegate != null) {
                return delegate.onSave(entity, id, state, propertyNames, types);
            } else {
                return false;
            }
        }
    
        /**
         * @see net.sf.hibernate.Interceptor#onDelete(java.lang.Object,
         *      java.io.Serializable, java.lang.Object[], java.lang.String[],
         *      net.sf.hibernate.type.Type[])
         */
        public void onDelete(Object entity, Serializable id, Object[] state,
                String[] propertyNames, Type[] types) throws CallbackException {
            if (delegate != null) {
                delegate.onDelete(entity, id, state, propertyNames, types);
            }
        }
    
        /**
         * @see net.sf.hibernate.Interceptor#preFlush(java.util.Iterator)
         */
        public void preFlush(Iterator entities) throws CallbackException {
            if (delegate != null) {
                delegate.preFlush(entities);
            }
        }
    
        /**
         * @see net.sf.hibernate.Interceptor#postFlush(java.util.Iterator)
         */
        public void postFlush(Iterator entities) throws CallbackException {
            if (delegate != null) {
                delegate.postFlush(entities);
            }
        }
    
        /**
         * @see net.sf.hibernate.Interceptor#isUnsaved(java.lang.Object)
         */
        public Boolean isUnsaved(Object entity) {
            if (delegate != null) {
                return delegate.isUnsaved(entity);
            } else {
                return null;
            }
        }
    
        /**
         * @see net.sf.hibernate.Interceptor#findDirty(java.lang.Object,
         *      java.io.Serializable, java.lang.Object[], java.lang.Object[],
         *      java.lang.String[], net.sf.hibernate.type.Type[])
         */
        public int[] findDirty(Object entity, Serializable id,
                Object[] currentState, Object[] previousState,
                String[] propertyNames, Type[] types) {
            if (delegate != null) {
                return delegate.findDirty(entity, id, currentState, previousState,
                        propertyNames, types);
            } else {
                return null;
            }
        }
    }

Similar Threads

  1. Hibernate Long Session Per Flow?
    By akw in forum Web Flow
    Replies: 21
    Last Post: Dec 12th, 2005, 08:06 PM
  2. Spring container fails with no exception
    By naor in forum Container
    Replies: 9
    Last Post: Oct 1st, 2005, 03:39 PM
  3. Spring code remarks
    By Alarmnummer in forum Architecture
    Replies: 18
    Last Post: Apr 7th, 2005, 07:17 AM
  4. Replies: 14
    Last Post: Feb 21st, 2005, 05:41 PM
  5. Mixing hibernate & spring into single beans
    By ImanRahmati in forum Container
    Replies: 1
    Last Post: Dec 21st, 2004, 07:26 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •