Sorry to bump this thread but when I posted this I thought I got it to
work. But it required some other changes in the system so I did not
implement the solution at the time. Now when I had time to implement
it I cannot get it to work anymore :-(.

So I have isolated the problem to a small test project. The goal is to
do like in the typed dataset system, use a single SELECT to select a
whole hierarchy of objects into memory and then traverse them in
memory without generating further SELECT.

Is there some way of doing this in NHibernate. Perhaps some extension
pint I can use?

Here are the files for the testproject:

SQL
--------

CREATE TABLE Test1(
        Id int NOT NULL,
        ParentId int NULL,
        Data varchar(50) NULL,
  PRIMARY KEY(Id)
)


INSERT INTO Test1(Id, ParentId, Data) VALUES(1, NULL, 'Node 1')
INSERT INTO Test1(Id, ParentId, Data) VALUES(11, 1, 'Node 1.1')
INSERT INTO Test1(Id, ParentId, Data) VALUES(111, 11, 'Node 1.1.1')
INSERT INTO Test1(Id, ParentId, Data) VALUES(2, NULL, 'Node 2')

Test1.hbm.xml
-----------------------

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="NHibernateTest1" namespace="NHibernateTest1">

  <class name="Test1" table="Test1">

    <id name="Id" unsaved-value="-1">
      <column name="Id" />
      <generator class="assigned" />
    </id>

    <property name="Data" />

    <!-- Parent -->
    <many-to-one name="Parent"
                 class="Test1"
                 cascade="none"
                 insert="false"
                 update="false">
      <column name="ParentId" />
    </many-to-one>

    <!-- Children -->
    <bag name="Children"
     inverse="true"
     cascade="all-delete-orphan"
     access="field.camelcase-underscore">
      <key>
        <column name="ParentId" />
      </key>
      <one-to-many class="Test1"/>
    </bag>

  </class>

</hibernate-mapping>


Test1.vb
-------------

Imports System.Collections.ObjectModel
Imports System.Diagnostics
Imports System.Collections.Generic

Public Class Test1

  Private _id As Integer
  Private _parentId As Integer
  Private _data As String
  Private _parent As Test1
  Private _children As IList(Of Test1) = New List(Of Test1)

  Public Overridable Property Id() As Integer
    Get
      Return _id
    End Get
    Set(ByVal value As Integer)
      _id = value
    End Set
  End Property

  Public Overridable Property Parent() As Test1
    Get
      Return _parent
    End Get
    Set(ByVal value As Test1)
      _parent = value
    End Set
  End Property

  Public Overridable Property Data() As String
    Get
      Return _data
    End Get
    Set(ByVal value As String)
      _data = value
    End Set
  End Property

  Public Overridable ReadOnly Property Children() As ReadOnlyCollection
(Of Test1)
    Get
      Return New ReadOnlyCollection(Of Test1)(_children)
    End Get
  End Property

End Class


Form1.vb
--------------

Imports System
Imports System.Collections
Imports NHibernate
Imports NHibernate.Cfg

Public Class Form1

  Private _sessionFactory As ISessionFactory

  Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
    'Create factory
    Dim cfg As New Configuration()
    cfg.SetProperty("connection.provider",
"NHibernate.Connection.DriverConnectionProvider")
    cfg.SetProperty("dialect", "NHibernate.Dialect.MsSql2005Dialect")
    cfg.SetProperty("connection.driver_class",
"NHibernate.Driver.SqlClientDriver")
    cfg.SetProperty("connection.connection_string",
"Server=localhost;Database=Test01;Integrated Security=SSPI")
    cfg.AddAssembly("NHibernateTest1")
    cfg.SetProperty("show_sql", "true")
    _sessionFactory = cfg.BuildSessionFactory()
  End Sub

  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click

    Dim session As ISession = _sessionFactory.OpenSession()
    Try

      'This generates a single SELECT and puts all objects in a flat
list
      Dim crit = session.CreateCriteria(GetType(Test1))
      Dim list = crit.List(Of Test1)()

      'This generates another 2 SELECT but the same objects and all
its children are already loaded in the first list
      Dim x = list(0).Children(0).Children(0)

      'When there are 3000 objects and 10 levels, searching the
hierarchy recursivly generates a lot of SELECT
      'How can we load all object into RAM with one SELECT?

      Stop

    Finally
      session.Close()
    End Try

  End Sub

End Class


On Mar 19, 8:10 pm, Fabio Maulo <[email protected]> wrote:
> I mean do the equivalent in HQL or Criteria
>
> 2009/3/19 kello <[email protected]>
>
>
>
>
>
>
>
> > I actually tried to use such a query and return only the root entity
> > only I am not using DAOs but a Repository which directly uses the
> > nhibernate session to create a SQL Query. Much simplified It goes
> > something like this:
>
> >     Dim query = Session.CreateSQLQuery("SELECT * FROM BomLines WHERE
> > BomId=foo ORDER BY foo")
> >     Dim list = query.List()
> >     Return list(0)
>
> > What happened was that the children collections were still loaded by a
> > SELECT each when traversing the tree from the root entity. I think it
> > is because the query only returns a flat list of all the entities, it
> > does not load the children collection in each entity?
>
> > /Jonas
>
> > On Mar 19, 4:13 am, Fabio Maulo <[email protected]> wrote:
> > > You can use a similar select you are using in the old system and then you
> > > can return only the root from your DAO.BTW 3000 entities in RAM will have
> > > some cost.
>
> > > 2009/3/18 kello <[email protected]>
>
> > > > I am working on a system that has Bill Of Materials (BOM) which
> > > > contains about 1000-3000 parts organized in a hierarchy about 10
> > > > levels deep. The system was using Typed Datasets but I have converterd
> > > > parts of it to use an object model and NHibernate for persistance.
>
> > > > The problem is that the NHibernate version is currently much slower
> > > > than the dataset version because of the time it takes to load a BOM
> > > > into memory. In the old system the entire BOM is loaded into a dataset
> > > > in less than a second using a single SELECT (then there is of course a
> > > > lot of ugly code working with the dataset in-memory).
>
> > > > Now in the NHibernate version the BOM is loaded as a root entity which
> > > > has a persistent collection of its parts which are entities of the
> > > > same class and they have collection of their parts etc. This causes
> > > > the loading to happen with one SELECT per collection load. I have read
> > > > the performance part of the reference docs and have added "batch-size"
> > > > to get more collections loaded with each level and it helped some but
> > > > it is still generating a lot of SELECT since the BOM is so large and
> > > > have lots of levels. Using a very large batch-size does not seem to
> > > > work either....
>
> > > > It is quite easy to make a special query using a single SELECT to get
> > > > all the objects in the BOM back in a single list but then I loose the
> > > > hierarchical organization since the collection of children in each
> > > > object are not loaded and when the collections are accessed they will
> > > > each generate a new SELECT to get filled anyway.
>
> > > > I thought about making a special repository method to load all objects
> > > > in a single list with a single SELECT and then using some sorting code
> > > > to put the objects from this list into each BOM objects childrens
> > > > collection. The problem with this approach is that since the children
> > > > collection list is a mapped persistant collection the proxy object
> > > > will fill the collection when I access it. I would have to find some
> > > > way to temporarily bybass the proxy object's collection loading and
> > > > fill the childrens collection manually but I don't know how... or am I
> > > > on the wrong track here? Is there some other way to fill all
> > > > collections in a single SELECT but still have them mapped so they
> > > > cacade save etc.?
>
> > > > Another problem is the deletion of a BOM. What happens is that first a
> > > > lot of SELECT is generated to sort out which objects belong to the BOM
> > > > and then a lot of DELETE is generated. It would be more effective if I
> > > > could do a single DELETE and then find and evict any BOM objects that
> > > > might be loaded in the session for the BOM being deleted. Could this
> > > > be done?
>
> > > --
> > > Fabio Maulo- Hide quoted text -
>
> > > - Show quoted text -
>
> --
> Fabio Maulo- Hide quoted text -
>
> - Show quoted text -
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"nhusers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/nhusers?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to