ถ้าใครใช้ Hibernate ใน web application มักจะพบเจอปัญหา LazyInitialize ทุกราย
ซึ่ง case classic ก็คือ
ใน data access layer มีการเปิด session และทำการ query ข้อมูล
จากนั้นก็ปิด session แล้ว return ข้อมูลกลับไปยัง UI
ซึ่งพอ jsp ทำการ render ข้อมูล
และเกิดไปแตะข้อมูลที่ยังไม่ได้ initialize
ก็จะเกิด LazyInitializeException ขึ้น
(แต่ถ้าเกิดการแตะข้อมูลที่ยังไม่ initialize ในขณะที่ session ยังเปิดค้างไว้อยู่,
Hibernate จะ initialize ข้อมูลให้โดยอัตโนมัต)
ทางแก้ทางหนึ่งก็คือโอนความรับผิดชอบในการเปิดปิด session
ไปให้กับ Servlet Filter เสีย
Spring ได้เตรียม Filter ที่ทำหน้าที่แบบนี้ไว้ให้แล้ว
ชื่อว่า
OpenSessionInViewFilterการ config ก็แค่เพิ่ม block นี้ลงใน web.xml
<filter>
<filter-name>OpenSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OpenSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
การทำงานภายในของ
OpenSessionInView
ก็คือ
เมื่อเกิด request เข้ามา
มันจะทำการค้นหา
SessionFactory
จากใน Spring Context
โดยมันจะตั้ง Assumption ว่า มันจะค้นหา
SessionFactory
จาก Bean ที่มีชื่อว่า sessionFactory
public static final String DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory";
protected SessionFactory lookupSessionFactory() {
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
return (SessionFactory) wac.getBean(getSessionFactoryBeanName(), SessionFactory.class);
}
จากนั้น มันก็จะทำการ สร้าง session และ bind session โดยเรียกใช้
TransactionSynchronizationManager.bindResource
ซึ่งจะทำการ put ค่า session นั้นเก็บไว้ใน
ThreadLocalsession = getSession(sessionFactory);
TransactionSynchronizationManager.bindResource(sessionFactory,
new SessionHolder(session));
doFilter ...
TransactionSynchronizationManager.unbindResource(sessionFactory);
ที่นี้บางคนก็จะมีคำถามว่า แล้วเวลาเรียกใช้ session หล่ะ
มันจะเกิดอะไรขึ้น
ใน spring, เรามักใช้ HibernateTemplate เข้้ามาช่วยอยู่แล้ว
ซึ่ง HibernateTemplate เอง มันจะเรียกเปิด openSession
ซึ่งภายใน จะ delegate ต่อไปยัง
SessionFactoryUtilsprotected Session getSession() {
..
return SessionFactoryUtils.getSession(
getSessionFactory(),
getEntityInterceptor(),
getJdbcExceptionTranslator());
}
ถ้าตามไปดู code ใน
SessionFactoryUtils
ก็จะเห็นว่า
มีการใช้
TransactionSynchronizationManager
เข้าไปดึงค่า
SessionHolder ที่ OpenSessionInView เป็นคนสร้าง
และเก็บไว้ใน ThreadLocal
private static Session getSession(
SessionFactory sessionFactory, Interceptor entityInterceptor,
SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)
throws DataAccessResourceFailureException, IllegalStateException {
SessionHolder sessionHolder = (SessionHolder)
TransactionSynchronizationManager.getResource(sessionFactory);
...
return sessionHolder.getSession();
... }
ถ้าสรุปง่ายๆ (ลอกจาก
Configuring Hibernate, Spring and (MyFaces) JSF)
- Sets up the Servlet filter that manages the Hibernate session
- Associates a Hibernate session with the current thread assigned to the current request
- Classes that use HibernateTemplate will access the same session by default
- HibernateTemplate uses SessionFactoryUtils.getSession
- SessionFactoryUtils.getSession uses existing Hibernate Session associated with the current thread
ง่ายๆเลย ไม่ต้องไปใล่ดู code ให้เสียเวลา