一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

關于OpenSessionInViewFilter

 minwh 2006-08-08

  
 

 

關于OpenSessionInViewFilter

轉自:Potain 的BLOG

OpenSessionInView

Created by potian. Last edited by admin 61 days ago. Viewed 181 times.
[edit] [attach]
Hibernate的Lazy初始化1:n關系時,你必須保證是在同一個Session內部使用這個關系集合,不然Hiernate將拋出例外。

另外,你不愿意你的DAO測試代碼每次都打開關系Session,因此,我們一般會采用OpenSessionInView模式。

OpenSessionInViewFilter解決Web應用程序的問題

如果程序是在正常的Web程序中運行,那么Spring的OpenSessionInViewFilter能夠解決問題,它:

protected void doFilterInternal(HttpServletRequest request,
                HttpServletResponse response,
                           FilterChain filterChain) throws ServletException, IOException {
                      SessionFactory sessionFactory = lookupSessionFactory();
                      logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
                      Session session = getSession(sessionFactory);
                      TransactionSynchronizationManager.bindResource(sessionFactory,
                new SessionHolder(session));
                      try {
                            filterChain.doFilter(request, response);
                      }
                      finally {
                            TransactionSynchronizationManager.unbindResource(sessionFactory);
                            logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
                            closeSession(session, sessionFactory);
                      }
                }
可以看到,這個Filter在request開始之前,把sessionFactory綁定到TransactionSynchronizationManager,和這個SessionHolder相關。這個意味著所有request執(zhí)行過程中將使用這個session。而在請求結束后,將和這個sessionFactory對應的session解綁,并且關閉Session。

為什么綁定以后,就可以防止每次不會新開一個Session呢?看看HibernateDaoSupport的情況:

public final void setSessionFactory(SessionFactory sessionFactory) {
                this.hibernateTemplate = new HibernateTemplate(sessionFactory);
                }
                protected final HibernateTemplate getHibernateTemplate() {
                return hibernateTemplate;
                }

我們的DAO將使用這個template進行操作:

public abstract class BaseHibernateObjectDao
                      extends HibernateDaoSupport
                      implements BaseObjectDao {
                

      protected BaseEntityObject getByClassId(final long id) {             BaseEntityObject obj =                   (BaseEntityObject) getHibernateTemplate()                         .execute(new HibernateCallback() {

                  public Object doInHibernate(Session session)                         throws HibernateException {                         return session.get(getPersistentClass(), new Long(id));                   }

            });             return obj;       }

      public void save(BaseEntityObject entity) {             getHibernateTemplate().saveOrUpdate(entity);       }

      public void remove(BaseEntityObject entity) {             try {

                  getHibernateTemplate().delete(entity);             } catch (Exception e) {                   throw new FlexEnterpriseDataAccessException(e);             }       }

      public void refresh(final BaseEntityObject entity) {             getHibernateTemplate().execute(new HibernateCallback() {

                  public Object doInHibernate(Session session)                         throws HibernateException {                         session.refresh(entity);                         return null;                   }

            });       }

      public void replicate(final Object entity) {             getHibernateTemplate().execute(new HibernateCallback() {

                  public Object doInHibernate(Session session)                         throws HibernateException {                         session.replicate(entity, ReplicationMode.OVERWRITE);                         return null;                   }

            });       }

而HibernateTemplate試圖每次在execute之前去獲得Session,執(zhí)行完就力爭關閉Session
public Object execute(HibernateCallback action) throws DataAccessException {
                      Session session = (!this.allowCreate ?
                            SessionFactoryUtils.getSession(getSessionFactory(),
                false) :
                            SessionFactoryUtils.getSession(getSessionFactory(),
                getEntityInterceptor(),
                getJdbcExceptionTranslator()));
                      boolean existingTransaction =
                TransactionSynchronizationManager.hasResource(getSessionFactory());
                      if (!existingTransaction && getFlushMode() == FLUSH_NEVER) {
                            session.setFlushMode(FlushMode.NEVER);
                      }
                      try {
                            Object result = action.doInHibernate(session);
                            flushIfNecessary(session, existingTransaction);
                            return result;
                      }
                      catch (HibernateException ex) {
                            throw convertHibernateAccessException(ex);
                      }
                      catch (SQLException ex) {
                            throw convertJdbcAccessException(ex);
                      }
                      catch (RuntimeException ex) {
                            // callback code threw application exception
                            throw ex;
                      }
                      finally {
                            SessionFactoryUtils.closeSessionIfNecessary(
                session, getSessionFactory());
                      }
                }
而這個SessionFactoryUtils能否得到當前的session以及closeSessionIfNecessary是否真正關閉session,端取決于這個session是否用sessionHolder和這個sessionFactory在我們最開始提到的TransactionSynchronizationManager綁定。
public static void closeSessionIfNecessary(Session session,
                SessionFactory sessionFactory)
                throws CleanupFailureDataAccessException {
                      if (session == null ||
                         TransactionSynchronizationManager.hasResource(sessionFactory)) {
                            return;
                      }
                      logger.debug("Closing Hibernate session");
                      try {
                            session.close();
                      }
                      catch (JDBCException ex) {
                            // SQLException underneath
                            throw new CleanupFailureDataAccessException(
                            "Cannot close Hibernate session", ex.getSQLException());
                      }
                      catch (HibernateException ex) {
                            throw new CleanupFailureDataAccessException(
                            "Cannot close Hibernate session", ex);
                      }
                }

HibernateInterceptor和OpenSessionInViewInterceptor的問題

使用同樣的方法,這兩個Interceptor可以用來解決問題。但是關鍵的不同之處在于,它們的力度只能定義在DAO或業(yè)務方法上,而不是在我們的Test方法上,除非我們把它們應用到TestCase的方法上,但你不大可能為TestCase去定義一個接口,然后把Interceptor應用到這個接口的某些方法上。直接使用HibernateTransactionManager也是一樣的。因此,如果我們有這樣的測試:

Category parentCategory  = new Category ();
                      parentCategory.setName("parent");
                      dao.save(parentCategory);
                

      Category childCategory = new Category(); childCategory.setName("child");

      parentCategory.addChild(childCategory);       dao.save(childCategory);

      Category savedParent = dao.getCategory("parent");       Category savedChild = (Category ) savedParent.getChildren().get(0);       assertEquals(savedChild, childCategory);

將意味著兩件事情:
  • 每次DAO執(zhí)行都會啟動一個session和關閉一個session
  • 如果我們定義了一個lazy的關系,那么最后的Category savedChild = (Category ) savedParent.getChildren().get(0);將會讓hibernate報錯。

解決方案

一種方法是對TestCase應用Interceptor或者TransactionManager,但這個恐怕會造成很多麻煩。除非是使用增強方式的AOP.我前期采用這種方法(Aspectwerkz),在Eclipse里面也跑得含好。

另一種方法是在TestCase的setup和teardown里面實現(xiàn)和Filter完全一樣的處理,其他的TestCase都從這個TestCase繼承,這種方法是我目前所使用的。

posted on 2004-11-18 15:11 云在青天水在瓶 閱讀(2530) 評論(1)  編輯 收藏 引用 收藏至365Key

評論

# re: 關于OpenSessionInViewFilter  回復   

轉自:Karl Baum‘s Weblog

Karl Baum‘s Weblog

All | General | Java


Thursday July 08, 2004
Lazy Initialization and the DAO pattern with Hibernate and Spring

Hibernate and Lazy Initialization

Hibernate object relational mapping offers both lazy and non-lazy modes of object initialization. Non-lazy initialization retrieves an object and all of its related objects at load time. This can result in hundreds if not thousands of select statements when retrieving one entity. The problem is compounded when bi-directional relationships are used, often causing entire databases to be loaded during the initial request. Of course one could tediously examine each object relationship and manually remove those most costly, but in the end, we may be losing the ease of use benefit sought in using the ORM tool.

The obvious solution is to employ the lazy loading mechanism provided by hibernate. This initialization strategy only loads an object‘s one-to-many and many-to-many relationships when these fields are accessed. The scenario is practically transparent to the developer and a minimum amount of database requests are made, resulting in major performance gains. One drawback to this technique is that lazy loading requires the Hibernate session to remain open while the data object is in use. This causes a major problem when trying to abstract the persistence layer via the Data Access Object pattern. In order to fully abstract the persistence mechanism, all database logic, including opening and closing sessions, must not be performed in the application layer. Most often, this logic is concealed behind the DAO implementation classes which implement interface stubs. The quick and dirty solution is to forget the DAO pattern and include database connection logic in the application layer. This works for small applications but in large systems this can prove to be a major design flaw, hindering application extensibility.

Being Lazy in the Web Layer

Fortunately for us, the Spring Framework has developed an out of box web solution for using the DAO pattern in combination with Hibernate lazy loading. For anyone not familiar with using the Spring Framework in combination with Hibernate, I will not go into the details here, but I encourage you to read Hibernate Data Access with the Spring Framework. In the case of a web application, Spring comes with both the OpenSessionInViewFilter and the OpenSessionInViewInterceptor. One can use either one interchangeably as both serve the same function. The only difference between the two is the interceptor runs within the Spring container and is configured within the web application context while the Filter runs in front of Spring and is configured within the web.xml. Regardless of which one is used, they both open the hibernate session during the request binding this session to the current thread. Once bound to the thread, the open hibernate session can transparently be used within the DAO implementation classes. The session will remain open for the view allowing lazy access the database value objects. Once the view logic is complete, the hibernate session is closed either in the Filter doFilter method or the Interceptor postHandle method. Below is an example of the configuration of each component:

Interceptor Configuration

<beans>
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="openSessionInViewInterceptor"/>
</list>
</property>
<property name="mappings">
...
</bean>
...
<bean name="openSessionInViewInterceptor"
class="org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor">
<property name="sessionFactory"><ref bean="sessionFactory"/></property>
</bean>
</beans>
Filter Configuration

<web-app>
...
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate.support.OpenSessionInViewFilter
</filter-class>
</filter>
...
<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>*.spring</url-pattern>
</filter-mapping>
...
</web-app>
Implementing the Hibernate DAO‘s to use the open session is simple. In fact, if you are already using the Spring Framework to implement your Hibernate DAO‘s, most likely you will not have to change a thing. The DAO‘s must access Hibernate through the convenient HibernateTemplate utility, which makes database access a piece of cake. Below is an example DAO.

Example DAO

public class HibernateProductDAO extends HibernateDaoSupport implements ProductDAO {

public Product getProduct(Integer productId) {
return (Product)getHibernateTemplate().load(Product.class, productId);
}

public Integer saveProduct(Product product) {
return (Integer) getHibernateTemplate().save(product);
}

public void updateProduct(Product product) {
getHibernateTemplate().update(product);
}
}
Being Lazy in the Business Layer

Even outside the view, the Spring Framework makes it easy to use lazy load initialization, through the AOP interceptor HibernateInterceptor. The hibernate interceptor transparently intercepts calls to any business object configured in the Spring application context, opening a hibernate session before the call, and closing the session afterward. Let‘s run through a quick example. Suppose we have an interface BusinessObject:

public interface BusinessObject {
public void doSomethingThatInvolvesDaos();
}
The class BusinessObjectImpl implements BusinessObject:


public class BusinessObjectImpl implements BusinessObject {
public void doSomethingThatInvolvesDaos() {
// lots of logic that calls
// DAO classes Which access
// data objects lazily
}
}
Through some configurations in the Spring application context, we can instruct the HibernateInterceptor to intercept calls to the BusinessObjectImpl allowing it‘s methods to lazily access data objects. Take a look at the fragment below:

<beans>
<bean id="hibernateInterceptor" class="org.springframework.orm.hibernate.HibernateInterceptor">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="businessObjectTarget" class="com.acompany.BusinessObjectImpl">
<property name="someDAO"><ref bean="someDAO"/></property>
</bean>
<bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target"><ref bean="businessObjectTarget"/></property>
<property name="proxyInterfaces">
<value>com.acompany.BusinessObject</value>
</property>
<property name="interceptorNames">
<list>
<value>hibernateInterceptor</value>
</list>
</property>
</bean>
</beans>

When the businessObject bean is referenced, the HibernateInterceptor opens a hibernate session and passes the call onto the BusinessObjectImpl. When the BusinessObjectImpl has finished executing, the HibernateInterceptor transparently closes the session. The application code has no knowledge of any persistence logic, yet it is still able to lazily access data objects.

Being Lazy in your Unit Tests

Last but not least, we‘ll need the ability to test our lazy application from J-Unit. This is easily done by overriding the setUp and tearDown methods of the TestCase class. I prefer to keep this code in a convenient abstract TestCase class for all of my tests to extend.

public abstract class MyLazyTestCase extends TestCase {

private SessionFactory sessionFactory;
private Session session;

public void setUp() throws Exception {
super.setUp();
SessionFactory sessionFactory = (SessionFactory) getBean("sessionFactory");
session = SessionFactoryUtils.getSession(sessionFactory, true);
Session s = sessionFactory.openSession();
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s));

}

protected Object getBean(String beanName) {
//Code to get objects from Spring application context
}

public void tearDown() throws Exception {
super.tearDown();
SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
Session s = holder.getSession();
s.flush();
TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.closeSessionIfNecessary(s, sessionFactory);
}
}

( Jul 08 2004, 09:39:55 AM EDT ) Permalink Comments [2]

Trackback URL: http:///trackback/kbaum/Weblog/orm_lazy_initialization_with_dao
Comments:


A few things to keep in the back of your mind if you take this approach; 1. If any errors occur while attempting to lazy load relationships in the view (JSP) it would be hard to present a nice error to the user. 2. This would result in at least 2 hibernate sessions (db connections being open for any one request), so you might want to up the number of connections available. Cheers, Dan
Posted by Dan Washusen on July 08, 2004 at 09:02 PM EDT #

I am a little confused on why it would be difficult to show a nice error jsp. Couldn‘t we just use the provided servlet container error page mechanisms? In regards to the 2 hibernate sessions being opened. Are you saying that the OpenSessionInViewInterceptor would be run twice if an exception was thrown? Thanks for your feedback!
Posted by Karl Baum (63.170.158.133) on July 09, 2004 at 09:48 AM EDT #
2004-11-18 15:16 | 逃離開發(fā) 

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    日韩欧美一区二区不卡看片| 91久久精品在这里色伊人| 一区二区在线激情视频| 久久91精品国产亚洲| 日本午夜一本久久久综合| 国产肥妇一区二区熟女精品| 亚洲一区二区三区三州| 激情内射亚洲一区二区三区| 人妻中文一区二区三区| 国产精品人妻熟女毛片av久| 久久国产青偷人人妻潘金莲| 亚洲精品中文字幕在线视频| 黄片在线免费看日韩欧美| 亚洲精品欧美精品日韩精品| 日本人妻熟女一区二区三区 | 国产精品二区三区免费播放心| 噜噜中文字幕一区二区| 丰满人妻少妇精品一区二区三区| 老熟妇乱视频一区二区| 日本婷婷色大香蕉视频在线观看| 欧美午夜国产在线观看| 亚洲妇女作爱一区二区三区| 好骚国产99在线中文| 丝袜av一区二区三区四区五区| 美国欧洲日本韩国二本道| 国产永久免费高清在线精品| 亚洲国产精品国自产拍社区| 99精品国产一区二区青青 | 熟女白浆精品一区二区| 九九九热在线免费视频| av在线免费播放一区二区| 国产免费操美女逼视频| 精产国品一二三区麻豆| 在线观看视频成人午夜| 少妇成人精品一区二区| 中国日韩一级黄色大片| 老司机精品线观看86| 中文字幕人妻一区二区免费| 国产日韩综合一区在线观看| 偷拍洗澡一区二区三区| 福利一区二区视频在线|