PDA

View Full Version : How Common to Spring Manage Beans Created By Hibernate?



sethladd
Aug 23rd, 2004, 05:15 PM
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

Keith Donald
Aug 23rd, 2004, 09:36 PM
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

sethladd
Aug 23rd, 2004, 09:45 PM
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

dmiller
Aug 23rd, 2004, 11:05 PM
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.

sethladd
Aug 24th, 2004, 01:07 PM
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

dmiller
Aug 24th, 2004, 04:22 PM
Martin Fowler has some valuable insite on this topic in his article on the Anemic Domain Model (http://www.martinfowler.com/bliki/AnemicDomainModel.html).

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.

sethladd
Aug 24th, 2004, 04:40 PM
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.

adepue
Aug 24th, 2004, 06:59 PM
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?

sethladd
Aug 24th, 2004, 07:12 PM
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? :)

oliverhutchison
Aug 24th, 2004, 09:58 PM
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.java.springframework.user/3402
http://thread.gmane.org/gmane.comp.java.springframework.user/3513

And here is the code for the interceptor


/*
* 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.ClassEdi tor;
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().getCl assMetadata(
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.Obje ct,
* 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.lan g.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.Obje ct,
* 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.Ob ject,
* 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.It erator)
*/
public void preFlush(Iterator entities) throws CallbackException {
if (delegate != null) {
delegate.preFlush(entities);
}
}

/**
* @see net.sf.hibernate.Interceptor#postFlush(java.util.I terator)
*/
public void postFlush(Iterator entities) throws CallbackException {
if (delegate != null) {
delegate.postFlush(entities);
}
}

/**
* @see net.sf.hibernate.Interceptor#isUnsaved(java.lang.O bject)
*/
public Boolean isUnsaved(Object entity) {
if (delegate != null) {
return delegate.isUnsaved(entity);
} else {
return null;
}
}

/**
* @see net.sf.hibernate.Interceptor#findDirty(java.lang.O bject,
* 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;
}
}
}

diegum
Aug 24th, 2004, 11:19 PM
I have a question related to that SpringInterceptor

Let's suposse we have mapped 200 classes to tables but we need inject dependencies to about a 20% (40 of them)

Does SpringInterceptor enter on scene with every type of object Hibernate hidrate?

In other words, if a query returns 1000 object instances with all of them belonging to the remaining 80%, is SpringInterceptors going to be called 1000 times in vain?

If it is, there exists some workaround for such undesirable situation?

sethladd
Aug 24th, 2004, 11:24 PM
There might be a way to first query the BeanFactory to see if it handles a particular class. That might be a quick lookup.

Or, if you know which classes you need to manage, you could configure that to the INterceptor, by checking a list of classes you want Spring to manage. That would at least bypass BeanFactory immediately with the added expense of a little more to configure.

irbouho
Aug 24th, 2004, 11:33 PM
Let's suposse we have mapped 200 classes to tables but we need inject dependencies to about a 20% (40 of them)

You can specify the 20% using "persistantClassBeanNames" property.



Let's suposse we have mapped 200 classes to tables but we need inject dependencies to about a 20% (40 of them)

Does SpringInterceptor enter on scene with every type of object Hibernate hidrate?

Yes, this is how hibernate works, and this is a well know limitation for Hibernate Interceptors.



If it is, there exists some workaround for such undesirable situation?
even Hibernate3 new Listeners (http://blog.hibernate.org/cgi-bin/blosxom.cgi/Steve%20Ebersole/v3-events.html) will be called for each event / object.

irbouho
Aug 24th, 2004, 11:34 PM
There might be a way to first query the BeanFactory to see if it handles a particular class. That might be a quick lookup.

Or, if you know which classes you need to manage, you could configure that to the INterceptor, by checking a list of classes you want Spring to manage. That would at least bypass BeanFactory immediately with the added expense of a little more to configure.

This functionality is already implemented in SpringInterceptor. See my above post.

diegum
Aug 25th, 2004, 10:44 PM
We have solved the problem of business object dependency injection after Hibernate loading

Our solution is not the best, it has PROS and CONS but we have tried to mitigate its CONS

I suggest you take a look and make your opinions

We have in our "domain" packages classes which model business objects

Some of them needs to be injected with behavior after being recovered from database records. Let's say com.company.domain.product.Product as an example

So, what we have done was subclass Product as an Hibernate specific class in a integration layer package

Thus, we have obtained com.company.integration.hibernate.product.ProductH ibernate

ProductHibernate implements an old (deprecated?) Hibernate interfase: Lifecycle with some methods of interest, in this case, onLoad(Session, ...)

In "Hibernate in Action", Bauer and King suggest not to use that interfase because implies adding Hibernate specific methods to a class which has only business methods

We realize that point but try to mitigate it by isolating Hibernate specific logic in a subclass in a strictly isolated hibernate package, warrantying the fact that in business layer no class will know that Product's are really ProductHibernate

If we have to change business logic, we change Product

Some PROS: we don't interfere Hibernate on every loaded class. Only in those classes we want to be interfered (really less than 5% of O/R mappings ocurred during execution)

Some limitation: what happen if tomorrow we need, in business layer, subclassing Product in ProductSpecial? Shall we need in such case to subclass again in ProductSpecialHibernate?

Really we now our solution has serious pitfalls but, with certain conditions, works fine

Rod Johnson
Aug 26th, 2004, 03:55 AM
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.

sethladd
Aug 26th, 2004, 08:14 PM
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

tufelix
Aug 30th, 2004, 05:17 AM
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:


private BusinessStrategy businessStrategy;
private BusinessObjectDAO businessObjectDAO;
...
BusinessObject bo = businessObjectDAO.loadBusinessObject(id);
...
bo.executeBusinessStrategy(businessStrategy);
...


Code snippet taken from BusinessObject:


...
public void executeBusinessStrategy(BusinessStrategy businessStrategy) {
businessStrategy.execute(this);
}


What do you think about this?
Are there any issues related with our architecture?

thomas

Rod Johnson
Aug 30th, 2004, 05:58 AM
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.

Ben Alex
Aug 31st, 2004, 09:08 PM
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.

bpolka
Sep 1st, 2004, 12:17 PM
And yet there is a better solution in my opnion :D

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

dejanp
Sep 6th, 2004, 10:26 AM
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

dejanp
Sep 6th, 2004, 04:11 PM
Here is some sample code for my mapping bean idea:



<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.



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.LocalSessionFact oryBean;
import org.springframework.orm.hibernate.SessionFactoryUt ils;

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:



public class Order&#123;
....

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

Rod Johnson
Sep 10th, 2004, 02:58 AM
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.

sethladd
Sep 10th, 2004, 12:26 PM
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.

Loumeister
Sep 14th, 2004, 06:16 PM
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

Rod Johnson
Sep 17th, 2004, 07:31 AM
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

Rod Johnson
Sep 17th, 2004, 07:33 AM
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!

andy
Sep 17th, 2004, 12:28 PM
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 (http://www.jpox.org/docs/1_1/interceptors.html) 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.

andy
Sep 17th, 2004, 01:55 PM
CVS HEAD for JPOX now provides a PersistenceInterceptor (http://www.jpox.org/docs/1_1/interceptors.html)
This will be updated in the future, as a full JDO 2.0 nears, into "LifecycleListener" - a standard JDO 2.0 facility.

Rod Johnson
Sep 18th, 2004, 03:15 PM
See org.springframework.orm.hibernate.support.Dependen cyInjectionInterceptorFactoryBean in the sandbox. Configuration is via the setter methods on the DependencyInjectionAspectSupport superclass. These would be shared with JDO/other implementations to allow consistent configuration.

In case you're wondering why it's a factory bean, rather than a plain object as in Oliver's contribution--I wanted to extend ChainedInterceptorSupport as well as DependencyInjectionAspectSupport. As that's not possible in Java, I used a trick where inheritance from DependencyInjectionAspectSupport is via a FactoryBean (enabling inheritance of the config parameters that might be shared with JDO, aspect-based implementations, even possibly implementations not concerned with persistence), and inheritance from ChainedInterceptorSupport is handled within the return of getObject().

As this class is a Spring infrastructure class, IMO it's no problem that it depends on the Spring IoC container FactoryBean feature.

Rgds
Rod

sicarius
Oct 5th, 2004, 02:27 AM
As far as I can see there is a little problem. Spring offers two points for consistent (net.sf.hibernate.)Interceptor injection, the LocalSessionFactoryBean and the HibernateTransactionManager (via the entityInterceptor properties). I prefer to use the LocalSessionFactoryBean way. But configuring the DependencyInjectionInterceptorFactoryBean with a LocalSessionFactoryBean via a Spring XML configuration is not possible: Each needs the other as a dependency, yielding a circle.

For the time being I use the following work-around: I have my LocalSessionFactoryBean reference a dummy-implementation of the ChainedInterceptorSupport abstract class. After both the LocalSessionFactoryBean and the DependencyInjectionInterceptorFactoryBean are set up, I use a simple "connector" class, which adds the Interceptor created by the DependencyInjectionInterceptorFactoryBean to the interceptor chain of the dummy-implementation.

Rod Johnson
Oct 5th, 2004, 02:44 AM
Good point. Unfortunately I ran out of time to work on this, and only got as far as unit testing. Hopefully I'll have time to take another look at it next week.

tm_jee
Oct 7th, 2004, 10:23 PM
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.

If not mistaken, i guess what you mean is that querying through dao should be in the service logic layer, while the actuall execution that has business sense should be in the domain object, something like below, no?



class PersonManager&#40;&#41; &#123;
IPersonDao get/setPersonDao&#40;&#41;;

List findRelativesOfPersonById&#40;Long personId&#41; &#123;
Person p = getPersonDao.findPerson&#40;personId&#41;;
Stirng someOtherParam = // some other parameters
OtherInfo otherInfo = // query Dao for other information needed
p.findRelativesOfPerson&#40;someOtherParams, otherInfo&#41;;
&#125;

&#125;

class Person &#123;
Long get/setId&#40;&#41;;
String get/setName&#40;&#41;;
...etc...

// behavioral methods
List findRelativesOfPersonById&#40;String someparam, OtherInfo otherInfo&#41; &#123;
// do business logic here
&#125;
&#125;


In this case, the service(facade) has to deal with transaction (through aop) and also dao querying but the actuall business handling is still in the domain object (business object) such that the domain object is no longer a fake one. I think this is also another alternative. Any comments? How does this compare to the code where Dao access are in the domain object and using Hibernate's Interceptor to get bean (with dependecies injected)?

Thanks in advance

regards
tmjee

oliverhutchison
Oct 7th, 2004, 11:56 PM
Rod, sicarius,

when I originally implemented SpringInterceptor I also ran into the circular dependency between the interceptor and the session factory. I got around it by allowing the interceptor to take the name of the session factory bean or the actual bean. If the bean name was provided it would then lazily look it up from the BeanFactory only when it was needed.

Also it doesn't appear that it's possible to specify "next interceptor" for the interceptor created by DependencyInjectionInterceptorFactoryBean. Do you need to copy the setNextInterceptor(Interceptor nextInterceptor) method from ChainedInterceptorSupport into DependencyInjectionInterceptorFactoryBean?

Ollie

PS. Rod, I really like having ability to simply auto-wire the persistent objects rather that have to define prototypes in the bean factory. Very nice.

oliverhutchison
Oct 8th, 2004, 12:30 AM
Rod,

I created an issue in Jira and attached a patch that resolves the circular dependency (as per my last post) and lack of setter for next interceptor issues.

http://opensource.atlassian.com/projects/spring/browse/SPR-382

I wish we could add attachments to the forum....

HTH

Ollie

Rod Johnson
Oct 8th, 2004, 03:42 AM
Thanks Ollie. I'll look at your patch and resolve the circular dependency today or next week. Yes, the next interceptor will need to be a property on the enclosing FactoryBean.

Rod Johnson
Oct 11th, 2004, 06:40 AM
I've fixed this and closed the JIRA issue. The fix uses a name property for the SessionFactory, like Oliver's original contribution.

analogueboy
Oct 12th, 2004, 09:18 AM
Can one of you summarise how to avail of this service, as most of you seem to be very familiar with Spring's architecture? Is this something that will make it into Spring 1.2, or will it remain in the sandbox for some time?

TIA

Rod Johnson
Oct 12th, 2004, 09:33 AM
Can one of you summarise how to avail of this service, as most of you seem to be very familiar with Spring's architecture?
The fundamental configuration step is to add an appropriately configured DependencyInjectionInterceptorFactoryBean as a Hibernate interceptor in your Hibernate SessionFactory. Do this via the "entityInterceptor" property of the LocalSessionFactoryBean, for example. A trivial example:



<bean id="sessionFactory"
class="org.springframework.orm.hibernate.LocalSessionFact oryBean">

<property name="dataSource"><ref local="dataSource"/></property>

<!-- Set up the Dependency Injector
<property name="entityInterceptor"><ref local="dependencyInjectionInterceptor"/></property>

<property name="mappingResources">
<value>/mycom/packages/mine.hbm.xml</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">net.sf.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>



<bean id="dependencyInjectionInterceptor"
class="org.springframework.orm.hibernate.support.Dependen cyInjectionInterceptorFactoryBean"
>
<property name="sessionFactoryName"><value>sessionFactory</value></property>
<property name="defaultAutowireMode"><value>1</value></property>
</bean>


Note that the configuration of the DependencyInjectionInterceptorFactoryBean here is trivial: please refer to that class's Javadoc for more information about its capabilities. I'll be improving the Javadoc also before it makes the main source tree.


this something that will make it into Spring 1.2, or will it remain in the sandbox for some time?
It is scheduled for release in 1.2. We should have documentation for it at that time.

tareq
Oct 18th, 2004, 03:51 AM
where can I find the code of org.springframework.orm.hibernate.support.Dependen cyInjectionInterceptorFactoryBean ?

Rod Johnson
Oct 18th, 2004, 03:52 AM
In the sandbox directory in CVS.

tareq
Oct 18th, 2004, 04:21 AM
I looked there but could not find it. Could someone give the exact location (or maybe a link)?. Thanks in advance for your help.

tareq
Oct 18th, 2004, 05:49 AM
I found it! I was lokking in the wrong directory. Thnaks for your help.

calvin
Nov 5th, 2004, 06:39 PM
The fundamental configuration step is to add an appropriately configured DependencyInjectionInterceptorFactoryBean as a Hibernate interceptor in your Hibernate SessionFactory. Do this via the "entityInterceptor" property of the LocalSessionFactoryBean, for example. ....


Everything appears to be being wired up correctly for me using the three new classes from the sandbox with 1.1.1, except that my Hibernate persisted object does not get its setApplicationContext method called after being rehydrated (all the other properties are correctly wired). It implements ApplicationContextAware, but is there something additional that I need to do here?

Thanks for any help.

Rod Johnson
Nov 6th, 2004, 04:16 AM
Everything appears to be being wired up correctly for me using the three new classes from the sandbox with 1.1.1, except that my Hibernate persisted object does not get its setApplicationContext method called after being rehydrated (all the other properties are correctly wired).
It won't call setApplicationContext() with the autowiring approach. Autowiring does not register the object with the application context as a prototype, it merely does Dependency Injection based on resolving properties. This is consistent with the BeanFactory semantics on autowiring existing objects with dependencies from a factory.

If you want the ApplicationContextAware and other lifecycle interfaces to be invoked, you should use full factory management of your beans by defining them as prototypes.

Guess I should update the Javadoc to indicate this.

calvin
Nov 9th, 2004, 06:14 PM
If you want the ApplicationContextAware and other lifecycle interfaces to be invoked, you should use full factory management of your beans by defining them as prototypes.

Ah, that makes sense now. After changing autowire to 0 and defining the prototypes in managedClassNamesToPrototypeNames, it works great.

Thanks...

Rod Johnson
Nov 10th, 2004, 03:20 AM
I've updated the Javadoc. Thanks for bringing this to my attention.

sethladd
Dec 13th, 2004, 08:40 PM
There is a slight inconsistency with how Hibernate might wire a bean's ID and how this new interceptor is doing it.

The interceptor will use BeanWrapper.setPropertyValue to set the ID. This will not work if the bean has only a getter for the ID (and only field access for the ID). If no setter exists, then a NotWritablePropertyException is thrown.

Hibernate, on the other hand, is able to use field access to set properties of a bean.

I wonder if the interceptor should first try setPropertyValue, and if it fails, to try setting the field directly? We removed our setId() method to make it more clear users aren't able to set this. We'll set it to private for now, but if we're able to set the ID via its field, that would be even better.

Any reason not to?

sethladd
Dec 13th, 2004, 08:43 PM
Actually, turns out I have to create a public setId() to get it to work with setPropertyValue.

I'd like to be able to work with private setters, or even better yet, just accessing the field. If this is a good idea, I'll post the patch.

sethladd
Dec 14th, 2004, 03:13 PM
I just performed some basic tests to see if there is a speed difference between autowiring and the prototype. For my tests, it was 10.6% faster to use the prototype map than to use autowiring.

Just a note.

sfwalter
Mar 4th, 2005, 11:22 AM
I am new to Spring, could some one please post the xml config settings for getting the SpringInterceptor up and running?

thanks in advance.

calvin
Mar 4th, 2005, 02:31 PM
I am new to Spring, could some one please post the xml config settings for getting the SpringInterceptor up and running?

thanks in advance.

Something like the following is what I had, though your needs might be different (might not want lazy-init set to true, might not use JTA, etc):


<bean id="dependencyInjectionInterceptor" class="org.springframework.orm.hibernate.support.Dependen cyInjectionInterceptorFactoryBean">
<property name="sessionFactoryName">
<value>sessionFactory</value>
</property>
<property name="defaultAutowireMode">
<value>0</value>
</property>
<property name="managedClassNamesToPrototypeNames">
<props>
<prop key="com.thatone.archie.engine.task.MoveTask">moveTask</prop>
<prop key="com.thatone.archie.engine.task.DeleteTask">deleteTask</prop>
<prop key="com.thatone.archie.engine.task.CopyTask">copyTask</prop>
<prop key="com.thatone.archie.persistence.ArchieDataSourceImp l">archieDataSource</prop>
<prop key="com.thatone.archie.engine.job.QuartzJobImpl">archieJob</prop>
</props>
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate.LocalSessionFact oryBean" lazy-init="true">
<property name="dataSource">
<ref local="wrappedAppDataSource"/>
</property>
<property name="mappingResources">
<list>
<value>hbm/ApplicationModel.hbm.xml</value>
<value>hbm/ArchieWorkflow.hbm.xml</value>
<value>hbm/Datasource.hbm.xml</value>
<value>hbm/Job.hbm.xml</value>
<value>hbm/Signature.hbm.xml</value>
<value>hbm/Task.hbm.xml</value>
<value>hbm/Context.hbm.xml</value>
<value>hbm/workflow/HibernateCurrentStep.hbm.xml</value>
<value>hbm/workflow/HibernateHistoryStep.hbm.xml</value>
<value>hbm/workflow/HibernateWorkflowEntry.hbm.xml</value>
<value>hbm/workflow/PropertySetItemImpl.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<ref local="hibernateProperties"/>
</property>
<property name="entityInterceptor">
<ref local="dependencyInjectionInterceptor"/>
</property>
<property name="jtaTransactionManager">
<ref local="jotm"/>
</property>
</bean>

I've omitted non-essential bean definitions, but those are the crucial ones.

hth,

calvin

haug
Mar 6th, 2005, 11:07 AM
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

If you use dependency injection (Spring beans) how would you like to solve the issue that a persistent object lives only as long as the transaction lives?
If you create a Spring bean and it references a persistent object you'll get an exception if the next transaction tries to access the referenced object. This is because the object is only valid for the transaction it was loaded. After commit/rollback the association with the DB is lost.

sethladd
Mar 7th, 2005, 12:48 PM
I think you are talking about how once you close the session, you aren't able to traverse lazy loaded relationships. That is true. It's a good practice to load up all relationships needed for the use case before the session is closed. Then, when that object "goes out into the wild", you have loaded everything.

We've been DI'ing our Hibernate objects for some time with great success. I think it was a key missing ingredient for a true OO system.

Did I misinterpret the meaning of your post?

zlendon
Nov 15th, 2006, 09:16 AM
I see that the lates code in the sandbox (at least that I could find) related to this has not been updated in over a year and never made it into the actual framework. Currently, using Hibernate 3 without Hibernate 2 in the classpath breaks the sandbox code, as it relies on Hibernate 2 Interceptor (net.sf) syntax. Not intending to re-open old wounds, but what's the current thinking on how
best to address this type of functionality?

thanks,
Zach

manifoldronin
Nov 15th, 2006, 10:18 AM
My understanding is, @Configure+AspectJ (http://static.springframework.org/spring/docs/2.0.x/reference/aop.html#aop-atconfigurable) is the preferred way to achieve this in Spring 2.0.

zlendon
Nov 15th, 2006, 11:07 AM
Thanks for the follow-up. Since my post, I noticed that approach referenced here:

http://www.springframework.org/docs/reference/aop.html
in section 6.8.1

As well as some approaches here:
http://debasishg.blogspot.com/2006/07/spring-20-aop-spruce-up-your-domain.html

While I like aspectj, on large development teams it isn't always possible to force aspectj weaving on everyone. In addition, the documentation in 6.8.1 does reference DependencyInjectionInterceptorFactoryBean, but when I look at both the svn and cvs repository here, I don't see the bean.

I'm going to investigate further the blog post referenced above to see if I can figure out a decent, non-aspectj solution to this problem, outside of apparently DependencyInjectionInterceptorFactoryBean, unless I can find (or am told about) an up to date version somewhere.

thanks,
Zach