Нужна помощь с Spring / Hibernate Lazy-loading - PullRequest
2 голосов
/ 04 июня 2010

Я знаю, что это обсуждалось много раз. Я просто не могу понять, как это работает или где моя ошибка.
Я думаю, что приводить вам сокращенный пример - это лучший способ показать вам, что я пытаюсь сделать и какие предположения я делаю ...

У меня есть класс Product с именем. Имя является строковым свойством, которое является ленивым.

Мой DAO:

public abstract class HibernateProductDAO extends HibernateDaoSupport implements ProductDAO
{
    public List getAll()
    {
        return this.getHibernateTemplate().find("from " + this.getDomainClass().getSimpleName());
    }
}

Мой сервисный интерфейс:

public interface ProductService {
    //This methods are Transactional, but same exception error is thrown if there weren't
    @Transactional
    public Product getProduct();
    @Transactional
    public String getName(Product tp);
}

Реализация моего сервиса:

public class ProductServiceImpl implements ProductService  {
    private ProductDAO productDAO;
    public Product getProduct() {
        List ps = this.productDAO.getAll();
        return (Product) ps.get(0);
    }
    public String getName(Product p){
        return p.getName();
    }
}

Мой основной класс:

public class Main {
    private ProductService productService;
    public static void main(String[] args) {
        Main main= new Main();          
        main.productService= (ProductService)(new ClassPathXmlApplicationContext("applicationContext.xml")).getBean("productProxy");
        //load the product without the name
        Product p = main.productService.getProduct();
        //load the lazy name
        System.out.println(main.productService.getName(p));  //EXCEPTION IS THROWN IN THIS LINE
    }
    public void setProductService(ProductService productService) {
        this.productService= productService;
    }

    public ProductService getProductService() {
        return productService;
    }

Мое приложениеContext.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:util="http://www.springframework.org/schema/util"
   xmlns:jee="http://www.springframework.org/schema/jee"
   xmlns:lang="http://www.springframework.org/schema/lang"
   xmlns:aop="http://www.springframework.org/schema/aop"
   xmlns:tx="http://www.springframework.org/schema/tx"
   xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca">

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName"><value>oracle.jdbc.driver.OracleDriver</value></property>
        <property name="url"><value>jdbc:oracle:thin:@${hostname}:${port}:${schema}</value></property>
        <property name="username"><value>${username}</value></property>
        <property name="password"><value>${password}</value></property>
    </bean>

    <!-- Hibernate SessionFactory -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource"><ref local="dataSource"/></property>
        <property name="configLocation"><value>hibernate.cfg.xml</value></property>
        <property name="configurationClass"><value>org.hibernate.cfg.AnnotationConfiguration</value></property>
    </bean>

    <!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory"><ref local="sessionFactory"/></property>
    </bean>

    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <property name="sessionFactory"><ref bean="sessionFactory"/></property>
        <property name="allowCreate" value="true"/>
    </bean> 
    <bean id="productDAO" class="product.model.data.ProductDAO" >
        <property name="sessionFactory" ref="sessionFactory"/>
</bean>
    <bean id="hibernateInterceptor"
      class="org.springframework.orm.hibernate3.HibernateInterceptor">
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property>
    </bean>
    <bean id="productService"
      class="product.services.ProductServiceImpl">
        <property name="productDAO">
            <ref bean="ProductDAO"/>
        </property>
    </bean>
    <bean id="productProxy"
      class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target">
             <ref bean="productService"/>
        </property>
        <property name="proxyInterfaces">
             <value>product.services.ProductService</value>
        </property>
        <property name="interceptorNames">
            <list>
                <value>hibernateInterceptor</value>
            </list>
        </property>
    </bean>
</beans>

Фрагмент исключения:

11:59:57,775 [main] DEBUG org.springframework.orm.hibernate3.SessionFactoryUtils  - Opening Hibernate Session
11:59:57,775 [main] DEBUG org.hibernate.impl.SessionImpl  - opened session at timestamp: 12749723977
11:59:57,777 [main] ERROR org.hibernate.LazyInitializationException  - could not initialize proxy - no Session 
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:108)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:150)
    at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:150)

Прав ли я, если предположить, что HibernateInterceptor сохраняет Hibernate Session Open среди различных вызовов? Если я так, почему сессия закрывается после загрузки объекта продукта?

Я где-то читал, что я также могу использовать OpenSessionInViewInterceptor, но не могу заставить его работать. Как бы вы добавили этот перехватчик в этот маленький пример?

Есть ли какая-либо ошибка в коде или недоразумение относительно того, как это работает?

Знаете ли вы какой-нибудь простой пример кода, который я могу загрузить, чтобы проверить, как это работает?

Заранее спасибо, Neuquino

1 Ответ

6 голосов
/ 04 июня 2010

проблема в том, что HibernateInterceptor закрывает сессию после возврата из ProductService getProduct()

Из API DOCs :

Этот перехватчик связывает новый сеанс Hibernate с потоком перед вызовом метода, закрывая и удаляя его впоследствии в случае какого-либо результата метода. Если уже существует предварительно связанный сеанс (например, из HibernateTransactionManager или из окружающего метода, перехваченного Hibernate), перехватчик просто участвует в нем.

и открывает новый для следующего вызова ProductService.getProductName(). Вновь созданный сеанс не знает сущности продукта, которую вы извлекли из БД в предыдущем сеансе.

у вас есть различные возможности решить эту проблему:

1.) Добавить метод, который охотно загружает имена, и использовать его в определенных контекстах, это obviuos;)

2.) Вы можете присоединить объект к активному сеансу, используя Session.update(myProductEntity) перед вызовом Product.getName() в ProductService.getProductName()

3.) Вы можете заключить все это в транзакцию, в которой участвует обернутый метод для ProductService.getProduct() и ProductService.getProductName(). См. Распространение транзакции

4.) В веб-приложении вы можете использовать OpenSessionInViewFilter, чтобы держать сеанс открытым, пока представление создается в ваших контроллерах

5.) Я думаю, вы также можете использовать АОП напрямую. у вас может быть аспект держать сеанс открытым на произвольной точке соединения, но у меня нет реального опыта там, и я не могу быть более конкретным;)

надеюсь, что помогло ...

...