NHibernate many to many return null item in a list - nhibernate

I have a many to many relationship between Item and Booker.
Here is an example of a table :
create table Item (
id int generated by default as identity primary key,
name varchar(200) not null
);
CREATE TABLE Booker (
id int generated by default as identity primary key,
fisrtName varchar(200),
lastName varchar(200)
);
create table Item_Booker (
id int generated by default as identity primary key,
itemId int not null references Item(id),
bookerId int not null references Booker(id)
);
the corresponding model is as follows :
public class Item : AbstractPersistentEntity
{
public virtual string Name { get; set; }
public virtual IList<Booker> Bookers { get; set; }
}
And I use this mapping :
<hibernate-mapping
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="urn:nhibernate-mapping-2.2" xsi:schemaLocation="urn:nhibernate-mapping-2.2 ../../nhibernate-mapping.xsd"
namespace="BirthList.Core.Model" assembly="BirthList.Core">
<class name="Item" table="item">
<id name="Id" column="id">
<generator class="identity"/>
</id>
<property name="Name" column="name"/>
<!-- Association n-n -->
<list name="Bookers" table="item_booker">
<key column="itemId"/>
<list-index column="id"/>
<many-to-many class="Booker" column="bookerId"/>
</list>
</class>
</hibernate-mapping>
My problem is that when I retrieve my data from the database I have more items than expected in my Booker list. And the extra items are null (and are not present in the database).
I have checked the database for the Booker table and only have this item:
And for the table Item_booker I only have these:
This corresponds to my two following items :
Does anyone have any ideas?

<list-index column="id"/>
tells NHibernate to treat the id as an index. since it starts at 1 it will insert the record at index 1 so there must be null at index 0. what you probably want is orderby="id"

Related

Mapping issue: correct number of results, however every result is duplicate of first resut

I'm new to NHibernate (using latest version) and I am having an issue mapping an object in our generic dataloading application to a database.
The database is third party so we cant make changes there.
Our object is:
public class GenericObjectValue
{
public string ObjectId { get; set; }
public string ObjectTypeId { get; set; }
public string measurementId { get; set; }
public DateTime timestamp { get; set; }
public Double Value { get; set; }
}
Datasource tables we use:
Table t_data_point
(
id (PK, int, not null)
object_id (FK, Varchar(30), not null)
object_type_id (FK, Varchar(30), not null);
measurement_id (FK, Varchar(30), not null);
)
Table t_data_point_Value
(
data_point_id (PK, FK, int, not null)
timestamp (PK, FK, datetime, not null)
version (PK, FK, int, not null)
value (numeric(18,6), not null);
)
The mapping i have configured is:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Phoenix.Model" assembly="Phoenix.Common">
<class name="MeasValue" table="t_data_point_Value">
<id column="data_point_id" type="int" />
<property name="Timestamp" column="timestamp "/>
<property name="Value" column="value"/>
<join table="t_data_point">
<key column="id" />
<property name="measurementId" column="measurement_id" />
<property name="ObjectId" column="object_id" />
<property name="ObjectTypeId" column="object_type" />
</join>
</class>
</hibernate-mapping>
Not sure if i'm doing something stupid or something that just isn't possible but when i run the code for this i get back the correct number of results but the results are just duplications of the first result returned i.e. timestamp and value are the same.
If you need more information please let me know.
I've seen this when your id is mapped incorrectly, ie to a column that is not unique. Nhibernate will reuse the 1st object it finds in cache with the same id.
There is a free mapping tool that could help point you in the right direction. It wont create the join but it will create the basic columns and id http://nmg.codeplex.com.

HiLo NHibernate id generator implementation

I would like to use "hilo" generator but there is no any complete example how to create "specific" table as NH documentation says, and which values pass to it.
The following code fragments taken from NH tutorial
public class Cat
{
private Int64 id;
private string name;
private char sex;
private float weight;
public Cat()
{}
public virtual Int64 Id
{
get { return id; }
set { id = value; }
}
....
}
Mapper
<hibernate-mapping ...>
<class name="Cat" table="Cat">
<id name="Id" >
<column name="CatId" sql-type="Int64" not-null="true"/>
<generator class="hilo"/>
</id>
<property name="Name">
<column name="Name" length="16" not-null="true" />
</property>
....
</class>
</hibernate-mapping>
DB table "Cat"
CatId bigint NOT NULL
Name varchar(16) NOT NULL
Sex char(1) NULL
Weight real NULL
doesn't create anything in the database by default.
Parameters in the "id" node
<param name="table">hi_value</param>
<param name="column">next_value</param>
<param name="max_lo">100</param>
gives "Invalid object name 'hi_value'" error message, without them I'm getting "Invalid object name 'hibernate_unique_key'."
Cuid.Comb that is shown in their tutorial works good but gives 99.12% of fragmentation when I added in a loop 20K cat objects.
Can somebody point me to an example of "hilo" implementation or give a tip what I'm missing?
Thanks.
This solution solved my problem. It's fairly simple, don't know why on nhibernate site there is no tiny example like that.
You may be running into NH-2687.

NHibernate One-To-One

I've an issue using one-to-one mapping. I've searched internet and found many solutions but none was satisfying. Most of the examples carry overhead of storing parent instance in child class.
I want to use only parent Id in child class having foreign key constraint relationship but dont want to keep any parent instance in child.
When I try to load the records from database it throws exception "No row with the given identifier exists [AssemblyName.]". But, the record exists in Table "B" properly.
Any solutions for this issue?
The class structure:
class A {
public virtual string Id {get;set;}
public virtual B B {get;set;} // properties...... }
class B { public virtual string Id {get;set;} // properties......
public virtual string ParentId { get;set;} // class A Id }
The database structure:
CREATE TABLE [A](
[Id] [nvarchar](45) PRIMARY KEY
) ON [PRIMARY]
CREATE TABLE [B](
[Id] [nvarchar](45) PRIMARY KEY,
[ParentId] [nvarchar](45) NOT NULL
) ON [PRIMARY]
The mapping:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="A,AssemblyName" table="A" lazy="true">
<id name="Id" column="Id" type="string">
<generator class="assigned"/>
</id>
<one-to-one name="_B" cascade="all" fetch="join" foreign-key="None" constrained="true" class="B"/>
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="B,AssemblyName" table="B" lazy="true">
<id name="Id" column="Id" type="string"> <generator class="assigned"/> </id>
<property name="_Name" column="Name"/> </class>
</hibernate-mapping>
One-to-one associations are always bidirectional in NHibernate. Mapping of class B is incomplete:
<class name="B,AssemblyName" table="B" lazy="true">
<id name="Id" column="Id" type="string">
<generator class="assigned"/>
</id>
<many-to-one name="A" unique="true" column="A" />
<property name="_Name" column="Name"/>
</class>
This is foreign key association. For more information see this detailed Ayende's blog post or NHibernate documentation.
Read this. A "real" one-to-one needs the same primary key in both tables (and no ParentId column).

Querying a many-to-many collection or how to include a many-to-many table in a criteria query?

I am quite new to the world of NHibernate and I can't seem to get this to work with the use of a criteria query: query a many-to-many relationship or query a collection (set/bag) on an entity. I've searched the internet and checked all the NHibernate books we have, but I can't find a specific answer to my "challenge".
I have made a simplified example of the problem I'm trying to solve. I have a table with books, a table with categories and a many-to-many table with the categories per book. Here are some of the technicalities:
data structure:
create table tableBook
(
BkId integer not null default autoincrement,
BkTitle char(40) not null,
BkWriter char(40) not null,
primary key (BkId)
);
create table tableCategory
(
CatId integer not null default autoincrement,
CatCode char(3) not null,
CatDesc char(40),
primary key (CatId)
);
create table tableCategoriesPerBook
(
CpbId integer not null default autoincrement,
CpbBkId integer not null, /*foreign key to tableBook*/
CpbCatId integer not null, /*foreign key to tableCategory*/
primary key (CpbId)
);
alter table tableCategoriesPerBook add foreign key FK_CpbBkId (CpbBkId) references tableBook (BkId) on update Restrict on delete Cascade;
alter table tableCategoriesPerBook add foreign key FK_CpbCatId (CpbCatId) references tableCategory (CatId) on update Restrict on delete Cascade;
create unique index idx_CpbCatId_CpbBkId on tableCategoriesPerBook (CpbCatId, CpbBkId);
C# classes:
public class BookEntity
{
public virtual Int32 BookId { get; set; }
public virtual string BookTitle { get; set; }
public virtual string BookWriter { get; set; }
private readonly IEnumerable<CategoryEntity> _categories = new ObservableCollection<CategoryEntity>();
public virtual IEnumerable<CategoryEntity> Categories
{
get { return _categories; }
}
}
public class CategoryEntity
{
public virtual Int32 CategoryId { get; set; }
public virtual string CategoryCode { get; set; }
public virtual string CategoryDesc { get; set; }
}
NHibernate mappings:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping namespace="Domain" assembly="Domain" xmlns="urn:nhibernate-mapping-2.2">
<class name="Domain.BookEntity" table="tableBook">
<id name="BookId" column="BkId" type="Int32">
<generator class="native" />
</id>
<property name="BookTitle" column="BkTitle" type="string" length="40"/>
<property name="BookWriter" column="BkWriter" type="string" length="40"/>
<idbag name="_categories" access="field" table="tableCategoriesPerBook">
<collection-id type="Int32" column="CpbId">
<generator class="native"/>
</collection-id>
<key column="CpbBkId" property-ref="BkId"/>
<many-to-many column="CpbCatId" class="Domain.CategoryEntity, Domain" />
</idbag>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping namespace="Domain" assembly="Domain" xmlns="urn:nhibernate-mapping-2.2">
<class name="Domain.CategoryEntity" table="tableCategory">
<id name="CategoryId" column="CatId" type="Int32">
<generator class="native" />
</id>
<property name="CategoryCode" column="CatCode" type="string" length="3" />
<property name="CategoryDesc" column="CatDesc" type="string" length="40" />
</class>
</hibernate-mapping>
My question: is it possible to query (using ICriteria and/or detached criterias) the database in such a way that I get the books which is in one of the categories I specify (for instance: in catA or catB, could be "and" as well)? I want to optimize this in the query, not in C# (as I need to read all books from the database before I can filter the objects based on their collection of tags). If I'd write the SQL by hand, I would produce something like this:
SELECT * FROM tableBook
WHERE EXISTS
(
SELECT 1
FROM tableCategoriesPerBook
INNER JOIN tableCategory on (CpbCatId = CatId and CpbBkId = BkId)
WHERE CatCode in ('001', '002')
)
Since I don't have an entity for tableCategoriesPerBook, I don't see a way to get to this table with a criteria query. And I'd rather not add some handwritten piece of SQL expressions using:
criteria.Add(Expression.Sql("exists(.....)");
One last important factor: I am using a brownfield database, so I can't change the structure! this is what I'll have to work with database-wise.
This is pretty straight forward. You can use a detached criteria.
DetachedCriteria bookCategoryCriteria = DetachedCriteria.For<BookEntity>("bookCat");
bookCategoryCriteria
.CreateAlias("Categories", "cat", JointType.LeftOuterJoin)
.Add(Restrictions.In("cat.CategoryCode", categories)
.Add(Restrictions.Eq("bookCat.BookId", "book.BookId")
.SetProjection(Projections.Id());
Session.CreateCriteria<BookEntity>("book")
.Add(Subqueries.Exists(bookCategoryCriteria));

NHibernate filter collection

Using NHibernate I want to filter a collection in a class to contain ONLY a subset of possible objects. Below I am including a sample table data to help explain. I can find no way to do this using NHibernate.
Table:DataObject
DataObjectId(PK) / Name / CurrentVersion
11 "data.txt" 2
12 "info.txt" 3
Table:DataObjectVersion
Id / Comment / VersionNumber / DataObjectId(FK)
31 "Genesis" 1 11 <= Ignore this object
32 "Changed data" 2 11 <= Get this object
34 "Genesis" 1 12 <= Ignore this object
35 "Changed info" 2 12 <= Ignore this object
36 "Added info" 3 12 <= Get this object
I want to join on a non-foreign key DataObject.CurrentVersion = DataObjectVersion.VersionNumber for each DataObject in one command.
Here are the classes and mapping files:
public class DataObject
{
public virtual int DataObjectId { get; set; }
public virtual string Name { get; set; }
public virtual int CurrentVersionNumber { get; set; }
public virtual IList<DataObjectVersion> Versions { get; set; }
}
<class name="DataObject" table="DataObject" lazy="false">
<id name="DataObjectId" column="DataObjectId" type="int">
<generator class="assigned" />
</id>
<property name="Name" column="Name" type="String(512)" />
<property name="CurrentVersionNumber" column="CurrentVersionNumber" type="int" />
<bag name="Versions" cascade="all-delete-orphan" inverse="true" lazy="false" >
<key column="DataObjectId" />
<one-to-many class="DataObjectVersion" />
</bag>
</class>
public class DataObjectVersion
{
public virtual int DataObjectVersionId { get; set; }
public virtual string Comment { get; set; }
public virtual int VersionNumber { get; set; }
public virtual int DataObjectId { get; set; }
}
<class name="DataObjectVersion" table="DataObjectVersion" lazy="false">
<id name="Id" column="DataObjectVersionId" type="int">
<generator class="assigned" />
</id>
<property name="Comment" column="Comment" type="String(512)" />
<property name="VersionNumber" column="VersionNumber" type="int" />
<property name="DataObjectId" column="DataObjectId" type="int" />
</class>
if you want to filter the collection on demand, using a filter is a valid choice.
You would need to declare the filter on both the Version class and in the bag element and apply the filter from the NHibernateSession.EnableFilter method
if you always want to fetch a single Version in the bag then implement a 'where' in the mapping of the bag:
<bag name="Versions" cascade="all-delete-orphan" inverse="true" lazy="false" where="CurrentVersionNumber = Versions.VersionNumber" >
<key column="DataObjectId" />
<one-to-many class="DataObjectVersion" />
</bag>
note that in the 'where' you write proper SQL not HQL and as such the proper SQL i write above probably has to be changed to reflect your schema
Additionally if a single object is to be fetched setting up a bag and the according IList may be an overkill.
Applying a formula property and a DataObjectVersion object in the class may be more appropriate
in the class DataObject replace the IList with
public virtual DataObjectVersion Version { get; set; }
and in the mapping replace the 'bag' with something in the lines of
<property name="Version" type="DataObjectVersion" update="false" insert="false" formula="(select v.DataObjectVersionId, v.Comments, v.VersionNumber, v.DataObjectId from DataObjectVersion v where v.VersionNumber = CurrentVersionNumber)" />
again only proper SQL is allowed
i've used computed properties with native datatypes (datetime, string etc) and fetching an Entity may (or may not) need something more or different
Last but not least, you could apply a filter on the collection after you have fetched the primary object DataObject by creating a filter on the collection
IList<DataObjectVersion> fVersion =
NHibernateSession.CreateFilter(do.Versions, "where VersionNumber = :ver")
.SetParameter("ver", do.CurrentVersionNumber)
.List<DataObjectVersion>();
where the do.Versions collection is not initialized, only the results fetched in the separate fVersion collection and this is a second SELECT after already having made the round-trip to the db for the DataObject fetch.
Presumably your VersionNumber increments as the user changes the data and you're trying to get the latest one. If you consider the VersionNumber as an "Age" field instead (i.e. where 0 is the latest / youngest version, 1 is the next oldest and so on) then your problems becomes how to get all the entities with an Age of 0. This can be done using a filter: http://nhibernate.info/doc/nh/en/index.html#objectstate-filters