Witam, mam następujący problem, który wychodzi na prostym przykładzie. Korzystam z JSF, Hibernate i Spring. Chcę na stronie zaprezentować w h:dataTable drzewo, w którym krawędź oznacza relację parent-child. Strona składa się tylko z textInputu, gdzie podaję się id korzenia, przycisku który odpali metodę search() w backing-beanie oraz wspomnianego dataTable o kolumnach id oraz parent_id. Do bazy odwołuję się tylko raz, pobrane encje zapisuję na listę w obiekcie o application scope. Pobieram wszystkie encje, jakie są w bazie. Korzystam ze springowego filtru org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.

Okazuje się, że przy pierwszym wyszukaniu i pobraniu encji z bazy drzewo jest pełne. Po ponownym odpaleniu metody wyszukania (nawet bez zmiany id) drzewo jest obcięte tylko do pierwszego poziomu zagłębienia, dalsze encje mają puste kolekcje dzieci.

Dlaczego kolekcje dzieci są opróżniane, skoro za pierwszym razem drzewo jest pełne (czyli wszyscy rodzice znają wszystkie swoje dzieci). Na liście znajdują się wszystkie encje, jakie są w bazie, więc niczego nie brakuje. Beany mają co najmniej session scope.

Zamieszczam trochę kodu.

Person.hbm

<hibernate-mapping>
  <class name="business.Person" schema="public" table="person">
    <id name="id" type="int">
      <column name="id"/>
      <generator class="sequence">
        <param name="sequence">person_id_seq</param>
      </generator>
    </id>
    <set fetch="join" name="children">
      <key>
        <column name="parent_id"/>
      </key>
      <one-to-many class="business.Person"/>
    </set>
  </class>
</hibernate-mapping>

home.jsp - strona z formularzem

<f:view>
    <h:form>
( ... )
                <h:panelGroup>
                    <h:inputText value="#{controller.personId}"/>
                </h:panelGroup>
                <h:panelGroup>
                    <h:commandButton value="SEARCH" actionListener="#{controller.searchAction}"/>
( ... )
                <h:dataTable value="#{controller.succesorsList}" var="entry">
                    <h:column>
                        <f:facet name="header">
                            <h:outputText value="ID"/>
                        </f:facet>
                        <h:outputText value="#{entry.first.id}"/>
                    </h:column>
                    <h:column>
                        <f:facet name="header">
                            <h:outputText value="PARENT ID"/>
                        </f:facet>
                        <h:outputText value="#{entry.second.id}"/>
                    </h:column>
                </h:dataTable>
( ... )
    </h:form>
</f:view>

application-context.xml

<beans xmlns="http://www.springframework.org/schema/beans"
( ... )
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="mappingResources">
            <list>
                <value>business/Person.hbm.xml</value>
            </list>
        </property>

        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.cglib.use_reflection_optimizer">true</prop>
                <prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</prop>
            </props>
        </property>

        <property name="dataSource" ref="dataSource"/>
    </bean>
( ... )
    <bean id="modelLocator" class="business.ModelLocator" init-method="init" destroy-method="destroy">
        <property name="hibernateTemplate" ref="hibernateTemplate"/>
    </bean>
</beans>

faces-config.xml

<faces-config version="1.2" 
( ... )
    <managed-bean>
        <managed-bean-name>controller</managed-bean-name>
        <managed-bean-class>bean.Controller</managed-bean-class>
        <managed-bean-scope>session</managed-bean-scope>
    </managed-bean>

( ... )
</faces-config>

Controller.java - backing bean

public class Controller {
    private ModelLocator modelLocator;

    private int personId;
    private List<Pair<Person, Person>> succesorsList = new ArrayList<Pair<Person, Person>>();

    public Controller() {
        ApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
        modelLocator = (ModelLocator)ctx.getBean("modelLocator");
    }

( ... )

    public List<Pair<Person, Person>> getSuccesorsList() {
        return succesorsList;
    }

    public void searchAction(ActionEvent e) {
        Person root = modelLocator.getPersonById(personId);
        succesorsList.clear();

        /**
         * child --> parent
         */
         LinkedList<Pair<Person,Person>> stack = new LinkedList<Pair<Person,Person>>();
         succesorsList.add(new Pair(root, Person.empty()));
            for (Person p : root.getChildren()) {
                stack.add(new Pair<Person,Person>(p, root));
            }

        if (stack.size() == 0)
            return;

        Set<Person> succ = new HashSet<Person>();
        Person child = null;
        Person parent = null;

        while (stack.size() > 0) {

            Pair<Person, Person> item = stack.getFirst();
            child = item.getFirst();
            parent = item.getSecond();

            succ.clear();
            
            succ = child.getChildren();

            if (succ.size() != 0) {
                for (Person p : succ)
                           stack.add(new Pair<Person, Person>(p, child));
                
            } 

            stack.removeFirst();

            succesorsList.add(new Pair(child, parent));
        }
    }
}

ModelLocator.java - zarządzany przez Spring

public class ModelLocator extends HibernateDaoSupport {

    List<Person> all = new LinkedList<Person>();

    public Person getPersonById(int id) {

        Person result = null;
        if (all.size() == 0) {
            try {
                all = getHibernateTemplate().loadAll(Person.class);
            } catch (DataAccessException ex) {
            }
        }
        for (Person p : all) {
            if (p.getId() == id) {
                result = p;
                break;
            }
           
        }
        return result;
    }
}