延遲加載問題 OpenSessionInView方案(no session異常)

結合上一篇的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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章