Nhibernate one-to-one mapping - nhibernate-mapping

Hello guys I've tried searching for a solution to this problem for a period of time. Couldn't find it.
I have two classes which I will simplify. My problem is that i want a unidirectional one-to-one mapping between Player and Clan. Now I saw examples which have foreign key in ther id. But I don't understand it. This mapping is not producing a column in my Clans table for ClanLeader... Am i missing something? Thank you all for help.
public class Clan{
private Int32 id;
public virtual Int32 Id
{
get { return id; }
set { id = value; }
}
private string name;
public virtual string Name
{
get { return name; }
set { name = value; }
}
private Player clanLeader;
public virtual Player ClanLeader
{
get { return clanLeader; }
set { clanLeader = value; }
}
}
Then we have mapping for Clan:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="NHibernateSQLite"
namespace="NHibernateSQLite" >
<class name="GamingOrganizerDomainModel.Clan, GamingOrganizerDomainModel" table="Clans" lazy="false">
<id name="id" access="field" column="Clan_ID" type="Int32">
<generator class="native"></generator>
</id>
<property name="Name" column="Clan_Name" unique-key="ClanNameConstraint" type="String"/>
<one-to-one name="ClanLeader" class="GamingOrganizerDomainModel.Player, GamingOrganizerDomainModel" />
</class>
</hibernate-mapping>
Next is the class Player:
public class Player{
private Int32 id;
public virtual Int32 Id
{
get { return id; }
set { id = value; }
}
private string nickname;
public virtual string Nickname
{
get { return name; }
set { name = value; }
}
}
And mapping for Player:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="NHibernateSQLite"
namespace="NHibernateSQLite" >
<class name="GamingOrganizerDomainModel.Player, GamingOrganizerDomainModel" table="Players" lazy="false">
<id name="id" column="Player_ID" access="field" type="Int32">
<generator class="native" />
</id>
<property name="nickname" access="field" column="Nickname"/>
</class>
</hibernate-mapping>

Unidirectional one to one relation should be mapped as "many-to-one" element. "one-to-one" is used for bidirectional one to one. See this post for more details. Howerver there are ConfORM mappings as well the article is crystal clear.
You need make only one change in your Clan mapping:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="GamingOrganizerDomainModel"
namespace="GamingOrganizerDomainModel" >
<class name="Clan" table="Clans" lazy="false">
<id name="id" access="field" column="Clan_ID" type="Int32">
<generator class="native"></generator>
</id>
<property name="Name" column="Clan_Name" unique-key="ClanNameConstraint" type="String"/>
<many-to-one name="ClanLeader" class="Player" />
</class>
You do not need to write assembly qualified class name in the mapping. Assembly and namespace attributes of hibernate-mapping element specify default namespace and assembly where NH tries to find specific class.

Related

NHibernate issue : persistent class not known

I have two tables Person and PassportInfo with a structure as given below:
Table Person
(
PersonID uniqueidentifier not null, (PK)
Name varchar(100) not null,
Email varchar(100) not null
)
Table PassportInfo
(
ID int identity(1,1) not null Primary Key,
personID uniqueidentifier null, (FK to PersonID in Person table)
PassportNumber varchar(100) not null
)
Also this is the mapping for Person
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Project" namespace="Project">
<class name="classperson" table="Person" >
<id name="ID" type="System.Guid" column="personID">
<generator class="Guid"/>
</id>
<property name="Name" column="Name" type="System.String" length="100" not-null="true" />
<property name="Email" column="Email" type="System.String" length="100" not-null="true" />
<one-to-one name="classpassportinfo" class="classpassportinfo" constrained="true" />
</class>
</hibernate-mapping>
This is the mapping for PassportInfo
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Project" namespace="Project">
<class name="classpassportinfo" table="PassportInfo" >
<id name="ID" type="System.Int32" column="ID">
<generator class="identity"/>
</id>
<property name="PassportNumber" column="PassportNumber" type="System.String" length="100" not-null="true" />
<one-to-one name="classperson" class="classperson" />
</class>
</hibernate-mapping>
This is the Object Class for Person
namespace Project
{
[Serializable]
public class classperson : Base<System.Guid>
{
private System.String _Name;
private System.String _Email;
private classpassportinfo _classpassportinfo;
public classperson()
{
}
public classperson(System.Guid id)
{
base.ID = id;
}
public virtual System.String Name {
get { return _Name; }
set { _Name = value;}
}
public virtual System.String Email {
get { return _Email; }
set { _Email = value;}
}
public virtual classpassportinfo classpassportinfo {
get { return _classpassportinfo; }
set { _classpassportinfo = value;}
}
}
}
Finally this is the object class for PassportInfo
namespace Project
{
[Serializable]
public class classpassportinfo :Base<Systme.Int32>
{
private System.String _PassportNumber;
private classpassportinfo _classpassportinfo;
public classpassportinfo()
{
}
public classpassportinfo(System.Int32 id)
{
base.ID = id;
}
public virtual System.String PassportNumber {
get { return _PassportNumber; }
set { _PassportNumber = value;}
}
public virtual classperson classperson {
get { return _classperson; }
set { _classperson = value;}
}
}
}
When I execute above code, i am getting and error saying persistent class not known: Project.classpassportinfo. I am new to nhibernate. Any help in this appreciated.
To elaborate and clarify #Fran's comment for anyone else struggling with this problem...
For me, in Visual Studio this issue was resolved by:
Open the solution explorer (Project structure navigator)
Locate the mapping file passportinfo.hbm.xml
Right click and choose properties
Under the advanced tab, make sure "Build Action" is set to "Embedded Resource"
The issue should be resolved now. Good luck

Cascade Save - StaleObjectStateException: Row was updated or deleted by another transaction

Having an issue with updating the NHibernate version. Current version is 3.3.1.4000 and trying to update to 4.
After updating the unit test which does save with cascade fails with:
NHibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [NHibernateTests.TestMappings.ProductLine#cdcaf08d-4831-4882-84b8-14de91581d2e]
The mappings:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateTests" namespace="NHibernateTests.TestMappings">
<class name="Product" lazy="false" table="UserTest">
<id name="Id">
<generator class="guid"></generator>
</id>
<version name="Version" column="Version" unsaved-value="0"/>
<property name="Name" not-null="false"></property>
<property name="IsDeleted"></property>
<bag name="ProductLines" table="ProductLine" inverse="true" cascade="all" lazy="true" where="IsDeleted=0" >
<cache usage="nonstrict-read-write" />
<key column="UserId" />
<one-to-many class="ProductLine" />
</bag>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateTests" namespace="NHibernateTests.TestMappings">
<class name="ProductLine" where="IsDeleted=0" lazy="false">
<cache usage="nonstrict-read-write" />
<id name="Id">
<generator class="guid"></generator>
</id>
<version name="Version" column="Version" unsaved-value="0"/>
<property name="IsDeleted"></property>
<many-to-one name="Product" class="Product" column="UserId" not-null="true" lazy="proxy"></many-to-one>
</class>
</hibernate-mapping>
Classes:
public class Product
{
public Guid Id { get; set; }
public int Version { get; set; }
public bool IsDeleted { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public IList<ProductLine> ProductLines { get; private set; }
public Product()
{
ProductLines = new List<ProductLine>();
}
}
public class ProductLine
{
public Guid Id { get; set; }
public int Version { get; set; }
public bool IsDeleted { get; set; }
public Product Product { get; set; }
}
The test:
[TestMethod]
public void CascadeSaveTest()
{
var product = new Product
{
Id = Guid.NewGuid(),
Name = "aaa",
IsActive = true
};
var productLine = new ProductLine
{
Id = Guid.NewGuid(),
Product = product,
};
product.ProductLines.Add(productLine);
using (var connection = new RepositoryConnection())
{
using (var repositories = new Repository<Product>(connection))
{
repositories.Create(product);
//the below just calls the Session.Transaction.Commit();
connection.Commit(); //NH3.3.1.400 passes, NH4 fails
}
}
}
Thanks for you ideas in advance.
Well, I guess I have now understood what causes the error with NH 4. And If I am right, that is a bit contrived case causing this behavior to be hard to qualify as a bug.
In your example, products lines are mapped through a bag. A bag can contains duplicates, which requires an intermediate table between Product and ProductLine. (Something like a ProductProductLine table with an UserId (ProductId) column, and a ProductLineId column.)
You have set this intermediate table as being the ProductLine table. I suspect the commit to db causes NHibernate to insert the product, then the product line, then to try to insert the relationship by inserting again in ProductLine table. (You may check that by profiling SQL queries on your db.)
Things are a bit muddy, since doc states (emphasis is mine):
table (optional - defaults to property name) the name of the
collection table (not used for one-to-many associations)
But then, how to honor the bag semantics allowing duplicates in the collection? From the same doc:
A bag is an unordered, unindexed collection which may contain the same
element multiple times.
Anyway, within your example, you really should map your one-to-many with a set, as shown in doc.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateTests" namespace="NHibernateTests.TestMappings">
<class name="Product" lazy="false" table="UserTest">
<id name="Id">
<generator class="guid"></generator>
</id>
<version name="Version" column="Version" unsaved-value="0"/>
<property name="Name" not-null="false"></property>
<property name="IsDeleted"></property>
<set name="ProductLines" inverse="true" cascade="all" lazy="true" where="IsDeleted=0" >
<cache usage="nonstrict-read-write" />
<key column="UserId" />
<one-to-many class="ProductLine" />
</set>
</class>
</hibernate-mapping>
And change your collection type for using .Net fx 4 System.Collections.Generic.ISet<T>.
public ISet<ProductLine> ProductLines { get; private set; }
public Product()
{
ProductLines = new HashSet<ProductLine>();
}
If this causes your trouble to disappear, it would mean something has changed in bag handling in NH 4. But should we consider this change as being a bug? Not sure, since using a bag in this case does not look right for me.
Drilling it further down turned out that NHibernate4 has problems identifying whether it is a new entity or an already existent when concerned with Cascade.
With the scenario in question it was calling a SQL Update for the ProductLine, rather than Create.
It works fine with the below changes, however I'm quite puzzled with such a changes in between NHibernate versions.
Change to ProductLine Mapping
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateTests" namespace="NHibernateTests.TestMappings">
<class name="ProductLine" where="IsDeleted=0" lazy="false">
<cache usage="nonstrict-read-write" />
<!-- here comes the updated line -->
<id name="Id" type="guid" unsaved-value="00000000-0000-0000-0000-000000000000">
<generator class="guid"></generator>
</id>
<version name="Version" column="Version" unsaved-value="0"/>
<property name="IsDeleted"></property>
<many-to-one name="Product" class="Product" column="UserId" not-null="true" lazy="proxy"></many-to-one>
</class>
</hibernate-mapping>
Change to test method
[TestMethod]
public void CascadeSaveTest()
{
var product = new Product
{
Id = Guid.NewGuid(),
Name = "aaa",
IsActive = true
};
var productLine = new ProductLine
{
Id = Guid.Empty, //The updated line
Product = product,
};
product.ProductLines.Add(productLine);
using (var connection = new RepositoryConnection())
{
using (var repositories = new Repository<Product>(connection))
{
repositories.Create(product);
connection.Commit();
}
}
}

Fluent NHibernate - joined subclass ForeignKey Name

I am looking at moving to Fluent NHibernate - the only issue I have encounter so far is that you can not specify a foreign key name on a joined sub class mapping.
Has anyone got a solution for this, or a workaround?
I found this post but the suggestion clearly was not added to the code.
I would like to avoid customising the code myself if possible.
Any help would be great...
Example:
public class Product
{
public string Name { get; set; }
}
public class Hammer : Product
{
public string Description { get; set; }
}
public class ProductMap : ClassMap<Product, long>
{
public ProductMap()
{
Polymorphism.Implicit();
Map(x => x.Name);
}
}
public class HammerMap : SubclassMap<Hammer>
{
public HammerMap()
{
Extends<Product>();
}
}
This generates something like:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="field.camelcase-underscore" auto-import="false" default-cascade="none" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" dynamic-insert="true" dynamic-update="true" mutable="true" polymorphism="implicit" optimistic-lock="version" name="Domain.Product, Domain" table="Product">
<id name="Id" type="System.Int64">
<column name="Id" />
<generator class="native">
<param name="sequence">ProductId</param>
</generator>
</id>
<property name="Name" type="System.String">
<column name="Name" />
</property>
<joined-subclass name="Domain.Hammer, Domain" table="Hammer">
<key>
<column name="Product_Id" />
</key>
<property name="Description" type="System.String">
<column name="Description" />
</property>
</joined-subclass>
</class>
</hibernate-mapping>
Note that there is no foreign key name specified in the mapping hbm file - as in:
<joined-subclass name="Domain.Hammer, Domain" table="Hammer">
<key column="Product_Id" foreign-key="FK_Hammer_Product"/>
</joined-subclass>
Try something like this
public class JoinedSubclassForeignKeyConvention : IJoinedSubclassConvention
{
public void Apply(IJoinedSubclassInstance instance)
{
instance.Key.ForeignKey(string.Format("FK_{0}_{1}",
instance.EntityType.Name, instance.Type.Name));
}
}

Nhibernate get collection by ICriteria

colleagues. I've got a problem at getting my entity. Mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Clients.Core"
namespace="Clients.Core.Domains">
<class name="Sales, Clients.Core" table='sales'>
<id name="Id" unsaved-value="0">
<column name="id" not-null="true"/>
<generator class="native"/>
</id>
<property name="Guid">
<column name="guid"/>
</property>
<set name="Accounts" table="sales_users" lazy="false">
<key column="sales_id" />
<element column="user_id" type="Int32" />
</set>
</class>
Domain:
public class Sales : BaseDomain
{
ICollection<int> accounts = new List<int>();
public virtual ICollection<int> Accounts
{
get { return accounts; }
set { accounts = value; }
}
public Sales() { }
}
I want get query such as
SELECT *
FROM sales s
INNER JOIN sales_users su on su.sales_id=s.id
WHERE su.user_id=:N
How can i do this through ICriterion object?
Thanks a lot.
This is what I think the answer should be:
public IEnumerable<Sales> GetSalesForUser(int userId)
{
return session.CreateCriteria<Sales>()
.CreateAlias("Accounts", "accounts")
.Add(Restrictions.Eq("accounts.UserId", "userId"))
.List<Sales>();
}
But I'm confused by your model. It appears that Accounts has a many-to-many relationship with Sales, but you haven't mapped it that way. I'm not sure how to filter an int collection (HashSet in this case). You could try:
public IEnumerable<Sales> GetSalesForUser(int userId)
{
return session.CreateCriteria<Sales>()
.Add(Restrictions.Eq("Accounts", userId))
.List<Sales>();
}
var sales = session.CreateCriteria(typeof(Sales))
.SetFetchMode("Accounts", FetchMode.Join)
.SetResultTransformer(Transformers.DistinctRootEntity)
.List<Sales>();

Doubly connected ordered tree mapping using NHibernate

We need to map simple class using NHibernate:
public class CatalogItem
{
private IList<CatalogItem> children = new List<CatalogItem>();
public Guid Id { get; set; }
public string Name { get; set; }
public CatalogItem Parent { get; set; }
public IList<CatalogItem> Children
{
get { return children; }
}
public bool IsRoot { get { return Parent == null; } }
public bool IsLeaf { get { return Children.Count == 0; } }
}
There are a batch of tutorials in the internet on this subject, but none of them cover little nasty detail: we need order to be preserved in Children collection. We've tried following mapping, but it led to strange exeptions thrown by NHibernate ("Non-static method requires a target.").
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Domain.Model" assembly="Domain">
<class name="CatalogItem" lazy="false">
<id name="Id" type="guid">
<generator class="guid" />
</id>
<property name="Name" />
<many-to-one name="Parent" class="CatalogItem" lazy="false" />
<list name="Children" cascade="all">
<key property-ref="Parent"/>
<index column="weight" type="Int32" />
<one-to-many not-found="exception" class="CatalogItem"/>
</list>
</class>
</hibernate-mapping>
Does anyone have any thoughts?
I'm no expert, but <key property-ref=...> looks strange to me in this usage. You should be able to do <key column="ParentID"/>, and NHibernate will automatically use the primary key of the associated class -- itself, in this case.
You may also need to set the list to inverse="true", since the relationship is bidirectional. [See section 6.8 in the docs.]