結合上一篇的ssh,討論延遲加載問題
什麼是延遲加載問題 ?
業務層查詢數據,返回後,session關閉了, 表現層獲取數據如果關聯延遲數據,無法初始化 ! (No Session 延遲加載問題 )
舉例:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 引入外部屬性配置文件 -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- c3p0連接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 方式二: (推薦)將hibernate參數配置到spring文件 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<!-- 1.數據源 -->
<property name="dataSource" ref="dataSource"/>
<!-- 2.Hibernate的屬性 -->
<property name="hibernateProperties">
<props>
<!-- 設置方言 -->
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<!-- 設置打印sql語句 -->
<prop key="hibernate.show_sql">true</prop>
<!-- 格式化sql語句 -->
<prop key="hibernate.format_sql">true</prop>
<!-- 自動建表 -->
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
<!-- 3.mapping映射 -->
<property name="mappingResources">
<list>
<value>cn/itcast/ssh/domain/Book.hbm.xml</value>
<value>cn/itcast/ssh/domain/User.hbm.xml</value>
</list>
</property>
</bean>
<!-- 配置聲明式事務處理 -->
<!-- 平臺事務管理器的實現 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<!-- 注入sessin工廠 -->
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- 配置聲明式事務 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 具體事務的屬性策略 -->
<tx:attributes>
<tx:method name="save*"/>
<tx:method name="update*"/>
<tx:method name="delete*"/>
<tx:method name="find*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 事務的切入點和切面配置 -->
<aop:config>
<!-- 切入點 -->
<aop:pointcut expression="bean(*Service)" id="txPointcut"/>
<!-- 切面 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
<bean id="userDao" class="cn.itcast.ssh.dao.UserDaoImpl">
<!-- 注入sessionFactory,這是在Dao層使用hibernateTemplate的條件,用來操作數據庫的crud -->
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="userService" class="cn.itcast.ssh.service.UserServiceImpl">
<!-- 注入dao -->
<property name="userDao" ref="userDao"/>
</bean>
<bean id="bookAction" class="cn.itcast.ssh.action.BookAction" scope="prototype">
<property name="userService" ref="userService"/>
</bean>
</beans>
book類
public class Book {
private Integer id;
private String name;
private Double price;
private User user;
......
}
Book.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.ssh.domain.Book" table="book">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="price"/>
<many-to-one name="user" class="cn.itcast.ssh.domain.User" column="uid"></many-to-one>
</class>
</hibernate-mapping>
user類:
public class User {
private Integer id;
private String name;
private Integer age;
private List<Book> books;
......
}
User.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.itcast.ssh.domain.User" table="user">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="age"/>
<set name="books" cascade="save-update,delete" inverse="true">
<key column="uid"></key>
<one-to-many class="cn.itcast.ssh.domain.User"></one-to-many>
</set>
</class>
</hibernate-mapping>
我們在action中獲取User
// action
public class BookAction extends ActionSupport implements ModelDriven<Book> {
public IUserService userService;
public void setUserService(IUserService userService){
this.userService = userService;
}
//業務方法:保存圖書
public String add(){
List<User> all = userService.findAll();
System.out.println(all);
return "NONE";
}
}
// service
public class UserServiceImpl implements IUserService {
public List<User> findAll(){
return userDao.findAll();
}
}
// dao
public class UserDaoImpl extends HibernateDaoSupport implements IUserDao {
public List<User> findAll(){
return super.getHibernateTemplate().loadAll(User.class);
}
}
查詢結果如下:
因爲action中執行代碼出異常,是不會在server控制檯打印保存信息的,所以我們得手動捕捉異常
public String add() {
try {
List<User> all = userService.findAll();
System.out.println(all);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("打印結束");
return "NONE";
}
我們訪問action的add方法
,執行結果如下:
我們在dao中執行loadAll(User.class)
方法時,執行sql,但這個sql只查詢User表,而user對象中的books屬性,只有當使用的時候,纔會通過session去數據庫中查詢.
當spring整合了hibernate後,我們使用的HibernateTemplate,本質上使用的是getCurrentSession(綁定在當前線程上的session)
getCurrentSession方法創建的Session實例會被綁定到當前線程中,它在提交或回滾操作時會自動關閉。
而當前被事務管理的是service,也就等於在service方法執行的第一步,我通過session開啓了事務,當service執行結束時,我們對事務進行了提交,此時session關閉
而我們在action中執行System.out.println(all)
;會調用user對象的books屬性,但是此時session已經關閉了,無法在去查詢,所以報了no session異常
問題: 如何解決延遲加載問題 ?
方案一: 在Xxx.hbm.xml中配置爲立即加載 lazy=false (不推薦 )
方案二: Service方法返回前, 對延遲數據進行初始化 (缺點多寫代碼 )
//查詢:複雜條件查詢,根據書名模糊查詢
public class UserServiceImpl implements IUserService {
public List<User> findAll(){
List<User> all = userDao.findAll();
// 通過sout方法,調用user中的books
System.out.println(all);
// 或者使用 Hibernate.initialize();
return all;
}
}
方案三: spring提供了OpenSessionInView 機制 (將Session開啓到表現層 最前面 Filter )
Spring 提供 OpenSessionInViewFilter `[注意:需要配置在struts2 Filter前面,否則不起作用 ]
web.xml
<!-- OpenSessionInView機制:會將會話到表現層,讓會話在請求結束之後關閉,延遲了session關閉,需要放置到struts2的過濾器的前面 -->
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
OpenSessionInViewFilter:在request過程中維持session(此session是hibernate的session)。延遲session的關閉,直到request結束,再自動關閉session,也就是說,直到表現層的數據全部加載完畢,再關閉Session。