-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 
Author Message
 Post subject: Search with Criteria
PostPosted: Wed Dec 19, 2007 9:06 am 
Beginner
Beginner

Joined: Thu Oct 25, 2007 3:45 am
Posts: 22
Hello,

I have a case like this:

#region description - may not read :)

Two classes - let's call them Person and Address. The relation between them is one-to-many. I am searching persons by a criteria ( criteria.List() ) with added Alias, Criterions, etc...

The criteria is from type Person, the class Person has a collecion of type Address and I create an Alias ( criteria.CreateAlias() ) to Address. Than I add an SimpleExpression to the criteria, which corresponds to the Address.

What's my trouble? Example: There is one Person with two addresses and both of them satisfy the expression (like address.City == London). Then the criteria returns two times the same Person. Of cource, I need it only once - it is the same Person.

#endregion

---------------------

Let me give a simple example:

First class:

public class Client
{
...

private string name;
public string Name
{
get { return name; }
set { name = value; }
}
}

Second class:

public class Organization
{
...

private IList<Client> _clients;
public IList<Client> Clients
{
get { return (IList<Client>)_clients; }
}
}

And this code:

Client client1 = new Client("Client");
Client client2 = new Client("Client"); // the same name

Organization organization1 = new Organization("Organization1");
organization1.Clients.Add(client1);
organization1.Clients.Add(client2);

Save the organization.

Then search for organizations:

ICriteria criteria = session.CreateCriteria(typeof(Organization));

criteria.CreateAlias("Clients", "clients");
criteria.Add(Expression.Eq("clients.Name", "Client"));

IList loadedOrganizations = criteria.List();

And the result is 2 organizations, in fact, two times the same organization. But I need it only once.

---------------------

Please, help :).


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 09, 2008 8:13 am 
Beginner
Beginner

Joined: Thu Oct 25, 2007 3:45 am
Posts: 22
My solution at this time is very stupid - removing equal objects from the result :). There should be much better way...


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 09, 2008 8:32 am 
Beginner
Beginner

Joined: Tue Mar 27, 2007 4:54 am
Posts: 47
NHibernate will return duplicate instances if outer join is used, just like the original result set from the data source.

One simple solution is telling NHibernate to remove duplicates like this...
[ICriteria].SetResultTransformer(CriteriaUtil.DistinctRootEntity)

/Roger


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 09, 2008 8:38 am 
Beginner
Beginner

Joined: Thu Oct 25, 2007 3:45 am
Posts: 22
Thanks, Roger. Exactly what I needed.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 09, 2008 8:50 am 
Regular
Regular

Joined: Wed Jan 25, 2006 1:11 am
Posts: 118
Location: Copenhagen, Denmark
Remember that SetMaxResult is a no go when using DistinctRootEntity ResultTransformer :-)


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 09, 2008 9:01 am 
Beginner
Beginner

Joined: Thu Oct 25, 2007 3:45 am
Posts: 22
jta wrote:
Remember that SetMaxResult is a no go when using DistinctRootEntity ResultTransformer :-)


But why, I just tried it and it works fine.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 09, 2008 10:36 am 
Regular
Regular

Joined: Wed Jan 25, 2006 1:11 am
Posts: 118
Location: Copenhagen, Denmark
Depending on your database combinations of SetMaxresults and SetFirstsresult gives freaky results with Distinctrootentity for instance given this mapping:

Code:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Sample" assembly="Sample">
  <class name="Company">
    <id name="Id">
      <generator class="identity" />
    </id>
    <property name="Name" />
    <set name="Employees" cascade="save-update">
      <key column="CompanyID"/>
      <one-to-many class="Employee"/>
    </set>
  </class>
  <class name="Employee">
    <id name="Id">
      <generator class="identity" />
    </id>
    <property name="Name" />
    <many-to-one name="Company" class="Company" not-null="true" column="CompanyID" />
  </class>
</hibernate-mapping>


And filling some data in:


Code:
Company c1 = new Company();
c1.Name = "Company1";
Company c2 = new Company();
c2.Name = "Company2";

Employee e1 = new Employee();
e1.Name = "Employee1";
Employee e2 = new Employee();
e2.Name = "Employee2";
Employee e3 = new Employee();
e3.Name = "Employee3";

c1.Employees.Add(e1);
e1.Company = c1;

c2.Employees.Add(e2);
c2.Employees.Add(e3);
e2.Company = c2;
e3.Company = c2;

sess.Save(c1);
sess.Save(c2);



And executing this code:

Code:
IList<Company> companies =
                    sess.CreateCriteria(typeof (Company)).SetFetchMode("Employees", FetchMode.Join).Add(Expression.Eq("Name", "Company2")).SetResultTransformer(CriteriaUtil.DistinctRootEntity).SetMaxResults(1).List<Company>();
                Debug.Assert(companies.Count == 1, "The number of companies was not 1");
                Debug.Assert(companies[0].Employees.Count == 2, "The number of employees were not 2 but " + companies[0].Employees.Count);


Vil result in the second assertion fail on MySQL, on MSSQL 2005 however et seems to work, but if you throw a SetFirstResult in there it will give you wrong number of elements back in some cases too.

The reason is quite simple in the mySQL case (the MSSQL is a little complicated because nHibernate uses ROWNUMBER function to do some magic) in mySQL the generated join query is appended with a "LIMIT 1" which cuts of the resultset at this point before we have retrieved all our duplicate Company rows with different Employee information.

For your viewing pleasure is here the complete SQL generated by nHibernate:

Code:
SELECT this_.Id as Id0_1_, this_.Name as Name0_1_, employees2_.CompanyID as CompanyID__3_, employees2_.Id as Id3_, employees2_.Id as Id1_0_, employees2_.Name as Name1_0_, employees2_.CompanyID as CompanyID1_0_ FROM Company this_ left outer join Employee employees2_ on this_.Id=employees2_.CompanyID WHERE this_.Name = ?p0 limit ?p1; ?p0 = 'Company2', ?p1 = '1'


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 09, 2008 10:52 am 
Beginner
Beginner

Joined: Thu Oct 25, 2007 3:45 am
Posts: 22
Thank you for the worning. I don't use MySQL for this project but I'll have it in mind.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 09, 2008 11:00 am 
Regular
Regular

Joined: Wed Jan 25, 2006 1:11 am
Posts: 118
Location: Copenhagen, Denmark
Its also a problem on MSSQL 2000 i assume and in fewer cases 2005, its a bit hard to debug. A coworker of mine learned this the hard way ;-)


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.