How do I optimize ICriteria to select in NHIbernate? - nhibernate

First of all, I'm sure there must be a simple solution to this but I just can't find it. (Yes, I have googled it)
If I run this Criteria..
IList<Team> teams = session.CreateCriteria<Team>("t")
.CreateCriteria("t.TeamMembers", "m")
.Add(Expression.Eq("m.Enabled", true))
.List<Team>();
..the generated SQL is similar to:
SELECT t.*, m.* FROM Teams t INNER JOIN TeamMembers m ON t.ID = m.TeamID
Since I only need the columns from table Teams. How do I instruct NHibernate stop fetching the unused columns from TeamMembers?
(My real implementation is quite complex which returns lots of data so this is a simplified example for brevity)
EDIT: " Added: .Add(Expression.Eq("m.Enabled", true))" to the Criteria
Here's a test I made with the same setup:
Classes:
public class Team
{
public virtual int ID { get; private set; }
public virtual Iesi.Collections.Generic.ISet<Member> Members { get; set; }
}
public class Member
{
public virtual int ID { get; private set; }
public virtual bool Enabled { get; set; }
public virtual Team Team { get; set; }
}
Mappings:
<class xmlns="urn:nhibernate-mapping-2.2" name="TestApp.Team, ClassLib" table="`Team`">
<id access="backfield" name="ID" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="ID" />
<generator class="identity" />
</id>
<set lazy="true" name="Members">
<key>
<column name="TeamID" />
</key>
<one-to-many class="TestApp.Member, ClassLib" />
</set>
</class>
<class xmlns="urn:nhibernate-mapping-2.2" name="TestApp.Member, ClassLib" table="`Member`">
<id access="backfield" name="ID" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="ID" />
<generator class="identity" />
</id>
<property name="Enabled" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="Enabled" />
</property>
<many-to-one class="TestApp.Team, ClassLib" name="Team">
<column name="TeamID" />
</many-to-one>
</class>
Generated SQL:
SELECT this_.ID as ID11_1_, m1_.ID as ID10_0_, m1_.Enabled as Enabled10_0_, m1_.TeamID as TeamID10_0_ FROM "Team" this_ inner join "Member" m1_ on this_.ID=m1_.TeamID WHERE m1_.Enabled = #p0;#p0 = True [Type: Boolean (0)]
Note for clarification: I incidentally named the TeamMembers to Members in my test example.

UPDATED FOR UPDATE QUESTION
It's probably best to make the Members collection lazy in the configuration
<set name="Members" lazy="true">
and then only set otherwise when required like
.SetFetchMode("TeamMembers", FetchMode.Eager)

Related

Hibernate: join with 2 NOT primary key

i want to do a left outer join from 2 table, but with 2 key that are not primary key.
This is the native sql query used:
Maybe there is a better way to do it?
SQLQuery query = session.createSQLQuery("
select {A.*},{B.*} from A_TABLE A left outer join B_TABLE B on A.QUOTE_ID = B.QUOTE_NUM ")
.addEntity("A", A_Class.class)
.addJoin("B", "A.bDocs");
for(Object result : query.list())
{
....
}
The mapping A file:
<class name="A_Class" table="A_TABLE" schema="S">
<id name="rowId" type="string">
<column name="ROW_ID" length="60" />
<generator class="assigned" />
</id>
<set name="BDocs" inverse="true" fetch="select" lazy="false">
<key>
<column name="QUOTE_NUM" length="60" not-null="true" />
</key>
<one-to-many class="B_Class" />
</set>
A_Class.java
public class A_Class implements java.io.Serializable
{
private String rowId;
private String QuoteId;
private Set BDocs= new HashSet(0);
// omitted all the set and get
}
The mapping B file:
<hibernate-mapping>
<class name="B" table="B_TABLE" schema="S">
<id name="rowId" type="string">
<column name="ROW_ID" length="60" />
<generator class="assigned" />
</id>
<many-to-one name="A" class="A_Class" fetch="select" lazy="false" outer-join="true" foreign-key="QuoteId" property-ref="QuoteId">
<column name="QUOTE_NUM" length="60" not-null="true" />
</many-to-one>
B_Class.java
public class B_Class implements java.io.Serializable
{
private String rowId;
private String quoteNum;
// omitted all the set and get
}
From this query i obtain 2 objects, one of A type and the other of B Type with a lot of correct datas but the set BDocs in the object of A type isn't filled. The goal is to get only the A Object with the variable BDocs filled with the B Objects.
I don't understand if the problem is in the query or in the mapping files. Anyone can help me?

Oracle issue: ORA-00972: identifier is too long / NHibernate.Exceptions.GenericADOException: could not execute query

This is the query performed by NHibernate against the Oracle database:
select
compteurra0_.NO_SEQ_CPTE_RAPP_ACCES_INFO_DSQ as NO1_2_,
compteurra0_.TXT_INFO_COMPL as TXT2_2_,
compteurra0_.TYP_CPTE_RAPP_ACCES_DSQ as TYP3_2_,
compteurra0_.VAL_CPTE_RAPP_ACCES_DSQ as VAL4_2_,
compteurra0_.VAL_CPTE_ATNDU as VAL5_2_,
compteurra0_.ID_UTIL_CREAT_OCC as ID6_2_,
compteurra0_.DHC_OCC as DHC7_2_,
compteurra0_.NO_SEQ_RAPP_ACCES_INFO_DSQ as NO8_2_
from
ESO.ESO_V_CPTE_RAPP_ACCES_DSQ compteurra0_
When I perform this query against the database, it returns an Oracle error:
ORA-00972: identifier is too long
I have searched the Internet and found that a bug was reported back in 2005 using NHibernate and Oracle: Oracle issue: ORA-00972: identifier is too long
Has this really been solved?
I have found two other related SO questions stating resolutions using Hibernate in Java.
Can the same be done using NHibernate?
If so, how to make it work?
hibernate oracle identifier is too long ORA-00972
Fluent Nhibernate Oracle Identifier Too Long - Alias Name Issue
CompteurRapportAcces.cs
public class CompteurRapportAcces : AuditableEntity {
public virtual string InformationComplementaire { get; set; }
public virtual RapportAccesInformation Rapport { get; set; }
public virtual TypeCompteur Type { get; set; }
public virtual int Valeur { get; set; }
public virtual int ValeurAttendue { get; set; }
public enum TypeCompteur {
Ordonnance = 1,
Delivrance = 2,
OrdonnanceElectronique = 3,
InscriptionRegistreDesRefus = 4
}
}
CompteurAccesRapport.hbm.xml
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="QueContientMonDSQ.Model" assembly="QueContientMonDSQ">
<class name="CompteurRapportAcces" table="ESO_V_CPTE_RAPP_ACCES_DSQ" schema="ESO">
<id name="Id" column="NO_SEQ_CPTE_RAPP_ACCES_INFO_DSQ" type="Int32" unsaved-value="0">
<generator class="sequence-identity">
<param name="sequence">ESO_NO_SEQ_CPTE_RAPP_ACCES_DSQ</param>
<param name="schema">ESO</param>
</generator>
</id>
<property name="InformationComplementaire" column="TXT_INFO_COMPL" type="String" length="1000" />
<property name="Type" column="TYP_CPTE_RAPP_ACCES_DSQ" type="Int32" />
<property name="Valeur" column="VAL_CPTE_RAPP_ACCES_DSQ" type="Int32" />
<property name="ValeurAttendue" column="VAL_CPTE_ATNDU" type="Int32" />
<property name="Creator" column="ID_UTIL_CREAT_OCC" type="String" length="15" />
<property name="Created" column="DHC_OCC" />
<many-to-one name="Rapport" class="RapportAccesInformation" column="NO_SEQ_RAPP_ACCES_INFO_DSQ" />
</class>
</hibernate-mapping>
hibernate.cfg.xml
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >
<session-factory name="QueContientMonDSQ">
<property name="connection.driver_class">NHibernate.Driver.OracleClientDriver</property>
<property name="format_sql">true</property>
<property name="show_sql">true</property>
<property name="dialect">NHibernate.Dialect.Oracle10gDialect</property>
<property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
</session-factory>
</hibernate-configuration>
<id name="Id" column="NO_SEQ_CPTE_RAPP_ACCES_INFO_DSQ" type="Int32" unsaved-value="0">
<generator class="sequence-identity">
<param name="sequence">ESO_NO_SEQ_CPTE_RAPP_ACCES_DSQ</param>
<param name="schema">ESO</param>
</generator>
</id>
The length of identifier(column name) NO_SEQ_CPTE_RAPP_ACCES_INFO_DSQ must be <= 30.

NHibernate - mapping connection-table & back-references

Hey,
I have to map the following entities:
class Document
{
public int DocumentId { get; set; }
public DocumentList ContainingList { get; set; }
}
class DocumentList
{
public int DocumentListId { get; set; }
public DateTime LastUpdateTime { get; set; }
public IList<Doucment> Documents { get; set; }
}
With the constraint that only one DocumentList can own a specific document (altough a collection-table exists here).
Mapping has to rely on the following tables (which cannot be changed, for the sake of simplicity):
TB_DOC
------
DOC_ID (int, PK)
DOC_CONTENT (blob)
TB_DOC_LIST
-----------
DOC_LIST_ID (int, PK)
DOC_LIST_UPDATE_TIME (datetime)
TB_LIST_AND_DOCS
----------------
DOC_LIST_ID
DOC_ID
So the mapping i tohught of would be like this:
enter code here
<class name="DocumentList" table="TB_DOC_LIST">
<id name="DocumentListId">
<column name="DOC_LIST_ID"/>
<generator class="assigned" />
</id>
<property name="LastUpdateTime" column="DOC_LIST_UPDATE_TIME ">
<set name="Documents" table="TB_LIST_AND_DOCS">
<key column="DOC_ID"></key>
<one-to-many class="Document" />
</set>
</class>
and:
<class name="Document" table="TB_DOC">
<id name="DocumentId">
<column name="DOC_ID"/>
<generator class="assigned" />
</id>
[ ??? ] - property to reference the "owner" document list
</class>
Now, following the known patterns, i can't figure out how should the back-link from Document to the DocumentList be mapped, since i have a "weired| one-to-many relation here, broken by a third table.
I also don't want a Document object to reference an IList to solve this with back-referencing many-to-many, since each Document has only one such "owner" DocumentList.
Any elegant idea? what am i mispercepting here?
cant test it right now, but a join could be used to get the reference id.
<class name="Document" table="TB_DOC">
<id name="DocumentId">
<column name="DOC_ID"/>
<generator class="assigned" />
</id>
<join table="TB_LIST_AND_DOCS">
<key column="DOC_ID"/>
<many-to-one class="DocumentList">
<column name="DOC_LIST_ID" />
</many-to-one>
</join>
</class>

Nhibernate one-to-many with table per subclass

I am customizing N2CMS's database structure, and met with an issue. The two classes are listed below.
public class Customer : ContentItem
{
public IList<License> Licenses { get; set; }
}
public class License : ContentItem
{
public Customer Customer { get; set; }
}
The nhibernate mapping are as follows.
<class name="N2.ContentItem,N2" table="n2item">
<cache usage="read-write" />
<id name="ID" column="ID" type="Int32" unsaved-value="0" access="property">
<generator class="native" />
</id>
<discriminator column="Type" type="String" />
</class>
<subclass name="My.Customer,My" extends="N2.ContentItem,N2" discriminator-value="Customer">
<join table="Customer">
<key column="ItemID" />
<bag name="Licenses" generic="true" inverse="true">
<key column="CustomerID" />
<one-to-many class="My.License,My"/>
</bag>
</join>
</subclass>
<subclass name="My.License,My" extends="N2.ContentItem,N2" discriminator-value="License">
<join table="License" fetch="select">
<key column="ItemID" />
<many-to-one name="Customer" column="CustomerID" class="My.Customer,My" not-null="false" />
</join>
</subclass>
Then, when get an instance of Customer, the customer.Licenses is always empty, but actually there are licenses in the database for the customer. When I check the nhibernate log file, I find that the SQL query is like:
SELECT licenses0_.CustomerID as CustomerID1_,
licenses0_.ID as ID1_,
licenses0_.ID as ID2_0_,
licenses0_1_.CustomerID as CustomerID7_0_,
FROM n2item licenses0_
inner join License licenses0_1_
on licenses0_.ID = licenses0_1_.ItemID
WHERE licenses0_.CustomerID = 12 /* #p0 */
It seems that nhibernate believes that the CustomerID is in the 'n2item' table. I don't know why, but to make it work, I think the SQL should be something like this.
SELECT licenses0_.ID as ID1_,
licenses0_.ID as ID2_0_,
licenses0_1_.CustomerID as CustomerID7_0_,
FROM n2item licenses0_
inner join License licenses0_1_
on licenses0_.ID = licenses0_1_.ItemID
WHERE licenses0_1_.CustomerID = 12 /* #p0 */
Could any one point out what's wrong with my mappings? And how can I get the correct licenses of one customer? Thanks in advance.
I'm not sure whether the SQL is incorrect, because the parent class mapping uses a discriminator so I'd expect all properties to be stored in the same table as the base class (n2item). However I'm not familiar with the "join table" syntax, I generally use joined-subclass so I might be misunderstanding.
Assuming the subclass mapping is correct, could the problem with the licenses be something to do with no Cascade setting being set for that collection?

NHibernate bidirectional many-to-many association

I have a class with following description:
public class Customer {
public ISet<Client> Contacts { get; protected set;}
}
I want to map Contacts property onto following table:
CREATE TABLE user_contacts (
user1 uuid NOT NULL,
user2 uuid NOT NULL
)
I want it to map bidirectionally, i.e. when Customer1 added to Customer2's Contacts, Customer1's Contacts collection should contain Customer2 (maybe only after entity reload). How could I do that?
Update Sure I can map left-to-right and right-to-left sets and then combine then at runtime, but it'll... hmm... untasty... Is there other solution? Any way, thank you very match, FryHard!
Take a look at this link on what hibernate calls unidirectional many-to-many associations. In Castle ActiveRecord I make use of HasAndBelongsToMany links, but I am not sure how exactly it is mapped in nhibernate.
Though taking a look at your question a little deeper, it looks like you will be linking bidirectionally from customer to user_contacts, which could break the many-many link. I will play with an example and see what I can come up with.
An Export of the hbm files from ActiveRecord shows this
<?xml version="1.0" encoding="utf-16"?>
<hibernate-mapping auto-import="true" default-lazy="false" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:nhibernate-mapping-2.2">
<class name="NHibernateMapping.Customer, NHibernateMapping" table="Customer" schema="dbo">
<id name="Id" access="property" column="Id" type="Int32" unsaved-value="0">
<generator class="identity">
</generator>
</id>
<property name="LastName" access="property" type="String">
<column name="LastName" not-null="true"/>
</property>
<bag name="ChildContacts" access="property" table="user_contacts" lazy="false">
<key column="user1" />
<many-to-many class="NHibernateMapping.Customer, NHibernateMapping" column="user2"/>
</bag>
<bag name="ParentContacts" access="property" table="user_contacts" lazy="false" inverse="true">
<key column="user2" />
<many-to-many class="NHibernateMapping.Customer, NHibernateMapping" column="user1"/>
</bag>
</class>
</hibernate-mapping>
ActiveRecord example:
[ActiveRecord("Customer", Schema = "dbo")]
public class Customer
{
[PrimaryKey(PrimaryKeyType.Identity, "Id", ColumnType = "Int32")]
public virtual int Id { get; set; }
[Property("LastName", ColumnType = "String", NotNull = true)]
public virtual string LastName { get; set; }
[HasAndBelongsToMany(typeof(Customer), Table = "user_contacts", ColumnKey = "user1", ColumnRef = "user2")]
public IList<Customer> ChildContacts { get; set; }
[HasAndBelongsToMany(typeof(Customer), Table = "user_contacts", ColumnKey = "user2", ColumnRef = "user1", Inverse = true)]
public IList<Customer> ParentContacts { get; set; }
}
Hope it helps!