To put it simple I have these 3 classes
class Bag { int Id; }
class Candy { int Id; }
class CandyBag
{
int Id;
Candy candy;
Bag bag;
}
I need to list all Bags that contains certain type of Candy, Im THINK it goes something like this:
session.QueryOver<Bag>(() => bagAlias)
.JoinQueryOver<CandyBag>()
.Where(candyBag => candgyBag.Bag.Id == bagAlias.Id)
.And(candyBag => candgBag.Candy.Id == userSelectedCandy.Id)
.List();
The thing is, I can't have a property of CandyBag nor Candy in the Bag class, because I have more items that a bag can hold (like Cloth/ClotheBag Food/FoodBag). And I'm saying this because I can't do
session.QueryOver<Bag>(() => bagAlias)
.JoinQueryOver<CandyBag>(bag => bag.CandyBag, () => candyBagAlias)
.Where(candyBag => candyBag.Bag.Id == bagAlias.Id)
.List();
Thanks in advance.
Based on your example I've created this classes:
public class Bag {
public int Id {get; set;}
}
public class Candy {
public int Id { get; set; }
}
public class CandyBag
{
public int Id { get; set; }
public Candy Candy { get; set; }
public Bag Bag { get; set; }
}
and this NHibernate mappings
<class name="Bag" table="Bag" lazy="false" >
<id name="Id">
<generator class="identity" />
</id>
<!--<property name="CreationDate" />-->
</class>
<class name="Candy" table="Candy" lazy="false" >
<id name="Id">
<generator class="identity" />
</id>
<!--<property name="CreationDate" />-->
</class>
<class name="CandyBag" table="CandyBag" lazy="false" >
<id name="Id">
<generator class="identity" />
</id>
<many-to-one name="Candy" column="CandyId" lazy="false" />
<many-to-one name="Bag" column="BagId" lazy="false" />
</class>
To obtain Bag collection of a certain Candy:
IList<Bag> bags = null;
using (var session = OpenSession())
{
CandyBag candyBagAlias = null;
bags = session.QueryOver<CandyBag>(() => candyBagAlias)
.Where(() => candyBagAlias.Candy.Id == userSelectedCandyId )
.List()
.Select(cb => cb.Bag).ToList();
// or with LINQ
bags = (
from bag in session.Query<Bag>()
join candyBag in session.Query<CandyBag>() on bag equals candyBag.Bag
where candyBag.Candy.Id == userSelectedCandyId
select bag
).ToList();
}
return bags;
I prefer the LINQ way because it is really simple to understand.
Related
public class Company_Product
{
public virtual Int32 Id { get; set; }
public virtual DateTime SalesDate { get; set; }
public virtual Company Company{ get; set; }
public virtual Product Product { get; set; }
}
public class Company
{
public virtual Int32 Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Company_Product> company_product { get; set; }
}
public class Product
{
public virtual Int32 Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Company_Product> company_product { get; set; }
}
Company.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="BusinessObjectApp"
namespace="BusinessObjectApp.Modal">
<!-- more mapping info here -->
<class name="Company" table="[Company]">
<id name="Id" column="Id">
<generator class="native" />
</id>
<property name="Name" column="Name" />
<bag name="company_product" table="[Company_Product]" inverse="true" lazy="true">
<key column="CompanyID" />
<one-to-many class="Company_Product" />
</bag>
</class>
</hibernate-mapping>
Company_Product.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="BusinessObjectApp"
namespace="BusinessObjectApp.Modal">
<!-- more mapping info here -->
<class name="Company_Product" table="[Company_Product]">
<id name="Id" column="Id">
<generator class="native" />
</id>
<property name="SalesDate" column="SalesDate" />
<!-- Many to many -->
<many-to-one class="Company" name="Company" column="CompanyID" />
<many-to-one class="Product" name="Product" column="ProductID" />
</class>
</hibernate-mapping>
Product.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="BusinessObjectApp"
namespace="BusinessObjectApp.Modal">
<!-- more mapping info here -->
<class name="Product" table="[Product]">
<id name="Id" column="Id">
<generator class="native" />
</id>
<property name="Name" column="Name" />
<bag name="company_product" table="[Company_Product]" inverse="true" lazy="true">
<key column="ProductID" />
<one-to-many class="Company_Product" />
</bag>
</class>
</hibernate-mapping>
I able to retrieve all item if using the code below:
IList<Company_Product> test = session.QueryOver<Company_Product>()
.List<Company_Product>();
However the code below produce error. I know that is because Company_Product class does not have the properties. I want to map them to the corresponding object like the output above. Is it possible?
string query = "SELECT C.Name, P.Name, CP.SalesDate FROM [Company_Product] CP " +
"LEFT JOIN [Company] C ON CP.CompanyID = C.Id " +
"LEFT JOIN [Product] P ON CP.ProductID = P.Id";
var test = session.CreateSQLQuery(query)
.SetResultTransformer(Transformers.AliasToBean<Company_Product>())
.List<Company_Product>();
UPDATE:
Now I able to retrieve the whole object.
Company_Product company_product = null;
Company company = null;
Product product = null;
IList<Company_Product> test = session.QueryOver<Company_Product>()
.Left.JoinAlias(cp => cp.Company, () => company)
.Left.JoinAlias(cp => cp.Product, () => product)
.SelectList(list => list
.Select(cp => cp.Company).WithAlias(() => company_product.Company)
.Select(cp => cp.Product).WithAlias(() => company_product.Product)
.Select(cp => cp.SalesDate).WithAlias(() => company_product.SalesDate)
)
.TransformUsing(Transformers.AliasToBean<Company_Product>())
.List<Company_Product>();
But I cannot set the nested property.
Company_Product company_product = null;
Company company = null;
Product product = null;
IList<Company_Product> test = session.QueryOver<Company_Product>()
.Left.JoinAlias(cp => cp.Company, () => company)
.Left.JoinAlias(cp => cp.Product, () => product)
.SelectList(list => list
.Select(cp => cp.Company.Name).WithAlias(() => company_product.Company.Name)
.Select(cp => cp.Product.Name).WithAlias(() => company_product.Product.Name)
.Select(cp => cp.SalesDate).WithAlias(() => company_product.SalesDate)
)
.TransformUsing(Transformers.AliasToBean<Company_Product>())
.List<Company_Product>();
Can anyone help me? I am new to Nhibernate and sorry for my bad English.
You do not need to use a native SQL query to achieve this.
You can use QueryOver but will have to use projections.
session.QueryOver<Company_Product>()
.JoinQueryOver<Company>(cp => cp.Company, ()=>companyAlias)
.JoinQueryOver<Product>(cp => cp.Product, ()=>productAlias)
.SelectList( l => l.Select( cp => companyAlias.Name)
.Select( cp => productAlias.Name)
.Select( cp => cp.SalesDate));
I haven't tested it, and it's been more than over a year since I've used NHibernate, but it should point you in the right direction. :)
Also, why are you using a bag ? I think a set might be more appropriate.
I have the following mapping
UserProfile.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="AngusBook.Domain"
namespace="AngusBook.Domain">
<class name="UserProfile">
<id name="UserId" type="int">
<generator class="identity" />
</id>
<natural-id mutable="false">
<property name="UserName" />
</natural-id>
<set name="Companies" table="Users_Companies">
<key column="UserId"/>
<many-to-many column="CompanyId" class="Company" />
</set>
</class>
</hibernate-mapping>
Company.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="AngusBook.Domain" namespace="AngusBook.Domain" >
<class name="Company">
<id name="Id">
<generator class="hilo" />
</id>
<natural-id mutable="false">
<property name="CompanyName" />
</natural-id>
<set name="Users" table="Users_Companies">
<key column="CompanyId"/>
<many-to-many column="UserId" class="UserProfile" />
</set>
</class>
</hibernate-mapping>
Table design
With this mapping I have, I can have two identical rows in Users_Companies table (ie: two rows with the same pair of foreign keys that belong to the UserProfile and Company table). Using mappings, how do I cause NHibernate or SQL to throw an error/exception when an attempt is made to insert a pair of foreign keys into Users_Companies that already exist in the table? I would like each row in Users_Companies to be unique and not have repetitive data.
I found out the problem. I didn't implement the Equals() and GetHashCode() correctly.
I ended up refactored my entities to use the following base entity class and that fixed the issue. No error is thrown when a duplicated entity is added to the set but the set will simply not add the duplicated entity.
Entity.cs
public abstract class Entity
{
public virtual TId Id { get; protected set; }
protected virtual int Version { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as Entity<TId>);
}
private static bool IsTransient(Entity<TId> obj)
{
return obj != null &&
Equals(obj.Id, default(TId));
}
private Type GetUnproxiedType()
{
return GetType();
}
public virtual bool Equals(Entity<TId> other)
{
if (other == null)
return false;
if (ReferenceEquals(this, other))
return true;
if (!IsTransient(this) &&
!IsTransient(other) &&
Equals(Id, other.Id))
{
var otherType = other.GetUnproxiedType();
var thisType = GetUnproxiedType();
return thisType.IsAssignableFrom(otherType) ||
otherType.IsAssignableFrom(thisType);
}
return false;
}
public override int GetHashCode()
{
if (Equals(Id, default(TId)))
return base.GetHashCode();
return Id.GetHashCode();
}
}
public abstract class Entity : Entity<Guid>
{
}
UserProfile.cs
public class UserProfile : Entity<int>
{
public UserProfile()
{
Companies = new HashedSet<Company>();
}
public virtual string UserName { set; get; }
public virtual ISet<Company> Companies { set; get; }
}
Company.cs
public class Company : Entity<int>
{
public Company()
{
Users = new HashedSet<UserProfile>();
}
public Company(string name) :this()
{
this.CompanyName = name;
}
public virtual string CompanyName { set; get; }
public virtual ISet<UserProfile> Users { set; get; }
}
I have a table called person_skills like so:
person_id, skill_type_id, base_score, misc_score
There is a lookup table that contains id, name for skill_types.
Now the tricky thing is that I have a composite key for person_id, skill_type_id. There will be many entries within this table as a person may have 5 skills.
Currently I have got a class like so:
public class skill
{
int BaseScore {get;set;}
int MiscScore {get;set;}
}
Then I have a class to contain all this like below:
public class person_skills
{
int person_id {get;set;}
IDictionary<skill_type, skill> skills {get;set;}
}
Now im not sure if this is the best way to handle this relationship, ultimately I need to be able to give people a link to skills, there is one person to many skills.
I was thinking about just putting in an auto incrememnt id column and use that as the PK, but it doesn't seem ideal. I can change the models and the DB if required, but as this is used within an ajax part of a page I need to be able to change the skills and then update them into the database.
I did not find an actual question but I'll answer anyway. :)
You do not need a surrogate key for the person_skills table. Your composite key, consisting of person_id and skill_type_id, should be sufficient. I believe the following classes and mappings reflect what you are trying to accomplish here.
Classes:
public class Person
{
public virtual int PersonId { get; set; }
public virtual String Name { get; set; }
public virtual IList<PersonSkills> Skills { get; set; }
}
public class SkillType
{
public virtual int SkillTypeId { get; set; }
public virtual String SkillName { get; set; }
public virtual IList<PersonSkills> Persons { get; set; }
}
public class PersonSkills
{
public virtual int PersonId { get; set; }
public virtual int SkillTypeId { get; set; }
public virtual int BaseScore { get; set; }
public virtual int MiscScore { get; set; }
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj == null || !(obj is PersonSkills))
{
return false;
}
PersonSkills o = obj as PersonSkills;
return (this.PersonId == o.PersonId
&& this.SkillTypeId == o.SkillTypeId);
}
public override int GetHashCode()
{
int hash = 13;
hash = hash + this.PersonId.GetHashCode();
hash = hash + this.SkillTypeId.GetHashCode();
return hash;
}
}
Mappings: (FluentNhibernate)
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Id(x => x.PersonId);
Map(x => x.Name);
HasMany(x => x.Skills)
.KeyColumn("PersonId")
.Cascade.All();
}
}
public class SkillTypeMap : ClassMap<SkillType>
{
public SkillTypeMap()
{
Id(x => x.SkillTypeId);
Map(x => x.SkillName);
HasMany(x => x.Persons)
.KeyColumn("SkillTypeId")
.Cascade.All();
}
}
public class PersonSkillsMap : ClassMap<PersonSkills>
{
public PersonSkillsMap()
{
CompositeId()
.KeyProperty(x => x.PersonId)
.KeyProperty(x => x.SkillTypeId);
Map(x => x.BaseScore);
Map(x => x.MiscScore);
}
}
Mappings (hbm, generated by FluentNHibernate - I removed output that is not required):
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" >
<class xmlns="urn:nhibernate-mapping-2.2" name="Person" table="Person">
<id name="PersonId" type="int">
<column name="PersonId" />
<generator class="identity" />
</id>
<bag cascade="all" name="Skills" mutable="true">
<key>
<column name="PersonId" />
</key>
<one-to-many class="PersonSkills" />
</bag>
<property name="Name" type="String">
<column name="Name" />
</property>
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" >
<class xmlns="urn:nhibernate-mapping-2.2" name="SkillType" table="SkillType">
<id name="SkillTypeId" type="int">
<column name="SkillTypeId" />
<generator class="identity" />
</id>
<bag cascade="all" name="Persons">
<key>
<column name="SkillTypeId" />
</key>
<one-to-many class="PersonSkills" />
</bag>
<property name="SkillName" type="String">
<column name="SkillName" />
</property>
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" >
<class xmlns="urn:nhibernate-mapping-2.2" name="PersonSkills" table="PersonSkills">
<composite-id mapped="false" unsaved-value="undefined">
<key-property name="PersonId" type="int">
<column name="PersonId" />
</key-property>
<key-property name="SkillTypeId" type="int">
<column name="SkillTypeId" />
</key-property>
</composite-id>
<property name="BaseScore" type="int">
<column name="BaseScore" />
</property>
<property name="MiscScore" type="int">
<column name="MiscScore" />
</property>
</class>
</hibernate-mapping>
I have a class:
public class User
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IDictionary<string, string> Attributes { get; set; }
}
and a mapping file:
<class name="User" table="Users">
<id name="Id">
<generator class="hilo"/>
</id>
<property name="Name"/>
<map name="Attributes" table="UserAttributes">
<key column="UserId"/>
<index column="AttributeName" type="System.String"/>
<element column="Attributevalue" type="System.String"/>
</map>
</class>
So now I can add many attributes and values to a User.
How can I query those attributes so I can get ie.
Get all the users where attributename is "Age" and attribute value is "20" ?
I don't want to do this in foreach because I may have millions of users each having its unique attributes.
Please help
You can do it using HQL.
For example:
from User u join u.Attributes attr
where index(attr) = 'Age' and attr = '20'
When i map columns from the inspected table, i do this:
<property name="InstanceName" type="MyNameUserType, MyApp.MyNamespace">
<column name="Name"/>
<column name="Name2"/>
</property>
How can I make property mapping initialize a UserType with data retrieved by the formula's sql query?
<property name="InstanceName" type="MyNameUserType, MyApp.MyNamespace" formula="(...)"/>
fails with an exception "wrong number of columns".
Thanks in advance!
MyUserNameType should be a class level mapping so that you can map the result of the SQL function to a class. See these two posts for some possible help:
Class and SQL Function example: http://thoughtspam.spaces.live.com/blog/cns!253515AE06513617!478.entry
NHibernate Mapping with formula mapping example:
http://thoughtspam.spaces.live.com/blog/cns!253515AE06513617!477.entry
I'm the author of the articles referenced by Michael. I had no idea people where still interested and I'm not sure it's applicable with the latest NHibernate.
Here's a fresh link though: http://thoughtspam.wordpress.com/2007/12/19/nhibernate-property-with-formula/
example, using Northwind...
Mapping:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="PropertyFormulaExample.Shipper, PropertyFormulaExample" table="Shippers" lazy="false" >
<id name="ShipperID" column="ShipperID" unsaved-value="0">
<generator class="native" />
</id>
<property name="CompanyName" column="CompanyName" />
<property name="Phone" column="Phone" />
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="PropertyFormulaExample.Order, PropertyFormulaExample" table="Orders" lazy="false">
<id name="OrderID" column="OrderID" unsaved-value="0">
<generator class="native" />
</id>
<property name="CustomerID" column="CustomerID" />
<property name="ShipVia" type="PropertyFormulaExample.Shipper, PropertyFormulaExample" formula="dbo.GetShipper(shipvia)" />
</class>
</hibernate-mapping>
Entities:
public class Order
{
public int OrderID { get; set; }
public string CustomerID { get; set; }
public Shipper ShipVia { get; set; }
}
public class Shipper : ILifecycle
{
public int ShipperID { get; set; }
public string CompanyName { get; set; }
public string Phone { get; set; }
#region ILifecycle Members
public LifecycleVeto OnDelete(NHibernate.ISession s)
{
throw new NotImplementedException();
}
public void OnLoad(NHibernate.ISession s, object id)
{
}
public LifecycleVeto OnSave(NHibernate.ISession s)
{
throw new NotImplementedException();
}
public LifecycleVeto OnUpdate(NHibernate.ISession s)
{
throw new NotImplementedException();
}
#endregion
}
And finally the SQL function:
CREATE FUNCTION dbo.GetShipper(#shipperId int)
RETURNS int
AS
BEGIN
RETURN #shipperId
END
Obviously, you’ll want the function to do something meaningful, but the idea is you return the PK for the entity and implement ILifecycle.