延迟加载问题 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。

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