"Transaction in view" with Hibernate, Spring, Struts
From the Hibernate reference manual: "Use a single database transaction to serve the clients request, starting and committing it when you open and close the Session"
Does Spring support this pattern? I've been using Spring transaction support with the @Transactional annotation and the "Open session in view" pattern (o开发者_Go百科rg.springframework.orm.hibernate3.support.OpenSessionInViewFilter) but transactions have to be confined to service methods, so I get multiple transactions per view, not just one.
You probably do not want a Transaction in View type of functionality. Typically what happens on a request is
- request comes in
- invokes some operation that saves stuff to database
- response goes out. i.e. render the success page or return json or whatever
The reason you dont want 'transaction in view' is because if there is an error at step 3, your transaction will be rolled back, even though the the actual business logic was successful, only rendering the response failed.
Now, I have made the assumption that you want the data to be committed even if there is an error after you commit it. If that assumption is false, then ok, thats up to you.
Since service methods are wrapped in transactions, perhaps an easier solution to your problem is to refactor your code such that one service is called per incoming request. Typically this is called the 'facade pattern'. Lets say you have
Service1.op1()
Service2.op2()
and you call both those methods for one request. You could simple create another service
AppFacade.doOp1andOp2()
that invokes op1 and op2 on the relevant services.
Another possibility would be to configure Spring to put transactions around your Struts Actions with declarative transaction management. Note with transactions, from the time the tx is open, to the time it closes, all db operations will use that same transaction, so even if you call multiple services, they all use the same tx.=. See the documentation here: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html
specifically 10.5.2
But I think using one service call per unit of business functionality is the best approach.
When you use a "Open session in view" pattern (org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
) to enable for example lazy loading in your http rendering (for example JSP), and mix it with transaction controlling by @Transactional
at your service method, then this seems to me a best practice approach.
The reason why I think so is:
- If you need lazy loading in your JSP, than "Open session in view" is a practical must.
- Transaction controlling belongs to your business logic, so it cannot (really) be done by a filter.
So at least you need both.
Declare a class which implements Hibernate's OpenSessionInViewFilter/Interceptor..... An example is given below...
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.hibernate3.support.OpenSessionInViewFilter;
public class CustomHibernateSessionViewFilter extends OpenSessionInViewFilter {
protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
Session session = super.getSession(sessionFactory);
session.setFlushMode(FlushMode.COMMIT);
return session;
}
protected void closeSession(Session session, SessionFactory factory) {
session.flush();
super.closeSession(session, factory);
}
}
In web.xml (or Application Context) :-
<filter>
<filter-name>OSIVF Filter</filter-name>
<filter-class>your.path.to.CustomHibernateSessionViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OSIVF Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Hope this would solve your problem. Please let me know whether it worked or not.
精彩评论