Comparing the following output with samples I've seen here at Stack Overflow and over the internet makes me wonder what am I doing wrong, as apparently both the show_sql and format_sql properties are correctly set?
Output
NHibernate:
SELECT
book0_.Isbn as Isbn0_0_,
book0_.Title as Title0_0_,
book0_.Author as Author0_0_,
book0_.Publisher as Publisher0_0_,
book0_.Published as Published0_0_,
book0_.Pages as Pages0_0_,
book0_.InStock as InStock0_0_,
book0_.Description as Descript8_0_0_
FROM
Books book0_
WHERE
book0_.Isbn=#p0;
#p0 = '0596800959' [Type: String (0)]
Book.cs
public class Book
{
public string Isbn { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public string Publisher { get; set; }
public DateTime Published { get; set; }
public int? Pages { get; set; }
public bool InStock { get; set; }
public string Description { get; set; }
}
Book.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Dotnet.Samples.NHibernate"
namespace="Dotnet.Samples.NHibernate">
<class name="Book" table="Books" lazy="false">
<id name="Isbn" />
<property name="Title" />
<property name="Author" />
<property name="Publisher" />
<property name="Published" />
<property name="Pages" />
<property name="InStock" />
<property name="Description" />
</class>
</hibernate-mapping>
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="dialect">NHibernate.Dialect.MsSqlCeDialect</property>
<property name="connection.driver_class">NHibernate.Driver.SqlServerCeDriver</property>
<property name="connection.connection_string">Data Source=res/Catalog.sdf</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>
</session-factory>
</hibernate-configuration>
Any advice will be really appreciated. Thanks much in advance.
UPDATE
In response to comments I'm adding how the expected output should look like (basically a valid SQL statement):
NHibernate:
SELECT
Isbn,
Title,
Author,
Publisher,
Published,
Pages,
InStock,
Description
FROM
Books
WHERE
Isbn = '0596800959'
Are you using the latest version of NHibernate? This is the original task. This blog implies that it is only supported in 3.0.
Related
I've spent a couple of days researching this on Google, StackOverflow and reading various blogs on this but to no avail. My question is if a collection is updated within an entity then would this cause NHibernate to update all properties in the entity of the modified collections? In this case I've added a user to a role and once I call session.SaveOrUpdate then 2 updates occur (NHibernate updates user and role) then the INSERT occurs. Is this the default behavior? I've tried to do the following to see if I can get NHibernate to just issue the INSERT statement:
Ran a Ghostbuster test on these entities based on code by Jason Dentler and Fabio Maulo but everything comes back ok and there are no dirty properties.
I made properties nullable that are defined as null in the database.
Set Inverse true on one of the entites.
Any help or insight is much appreciated.
Here are the mapping and class files.
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" namespace="Custom.NHibernateLib.Model" assembly="Custom.NHibernateLib" xmlns="urn:nhibernate-mapping-2.2">
<class name="Users">
<id name="UserId" type="Int64">
<generator class="hilo" />
</id>
<property name="ApplicationName" type="String" length="50" />
<property name="Username" type="String" length="15" />
<property name="Email" type="String" length="15" />
<property name="Password" type="String" length="50" />
<property name="PasswordSalt" type="String" length="128" />
<property name="PasswordQuestion" type="String" length="15" />
<property name="PasswordAnswer" type="String" length="50" />
<property name="IsApproved" type="YesNo" />
<property name="LastActivityDate" type="DateTime" />
<property name="LastLoginDate" type="DateTime" />
<property name="LastPasswordChangedDate" type="DateTime" />
<property name="CreationDate" type="DateTime" />
<property name="IsOnline" type="YesNo" />
<property name="IsAnonymous" type="YesNo" />
<property name="IsLockedOut" type="YesNo" />
<property name="LastLockedOutDate" type="DateTime" />
<property name="FailedPasswordAttemptCount" type="Int32" />
<property name="FailedPasswordAttemptWindowStart" type="DateTime" />
<property name="FailedPasswordAnswerAttemptCount" type="Int32" />
<property name="FailedPasswordAnswerAttemptWindowStart" type="DateTime" />
<property name="Comment" type="String" length="4001" />
<bag name="RoleList" table="UserRoles" lazy="true" cascade="save-update, persist" batch-size="10">
<key column="UserId" not-null="true" />
<many-to-many class="Roles" foreign-key="FK_Roles_UserRoles_RoleId">
<column name="RoleId" not-null="true" />
</many-to-many>
</bag>
<one-to-one name="Profiles" />
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" namespace="Custom.NHibernateLib.Model" assembly="Custom.NHibernateLib" xmlns="urn:nhibernate-mapping-2.2">
<class name="Roles">
<id name="RoleId" type="Int64">
<generator class="hilo" />
</id>
<property name="ApplicationName" type="String" length="50" />
<property name="RoleName" type="String" length="50" />
<bag name="UserList" table="UserRoles" lazy="true" inverse="true" cascade="save-update, persist" batch-size="10">
<key column="RoleId" not-null="true" />
<many-to-many class="Users" foreign-key="FK_User_UserRoles_UserId">
<column name="UserId" not-null="true" />
</many-to-many>
</bag>
</class>
</hibernate-mapping>
Here are the class files:
using System;
using System.Collections.Generic;
namespace Custom.NHibernateLib.Model
{
public class Users : Entity
{
public Users (){}
public virtual long UserId { get; set; }
public virtual string ApplicationName { get; set; }
public virtual string Username { get; set; }
public virtual string Email { get; set; }
public virtual string Password{ get; set; }
public virtual string PasswordSalt { get; set; }
public virtual string PasswordQuestion { get; set; }
public virtual string PasswordAnswer { get; set; }
public virtual bool? IsApproved { get; set; }
public virtual DateTime? LastActivityDate { get; set; }
public virtual DateTime? LastLoginDate { get; set; }
public virtual DateTime? LastPasswordChangedDate { get; set; }
public virtual DateTime? CreationDate { get; set; }
public virtual bool? IsOnline { get; set; }
public virtual bool? IsAnonymous { get; set; }
public virtual bool? IsLockedOut { get; set; }
public virtual DateTime? LastLockedOutDate { get; set; }
public virtual int? FailedPasswordAttemptCount { get; set; }
public virtual DateTime? FailedPasswordAttemptWindowStart { get; set; }
public virtual int? FailedPasswordAnswerAttemptCount { get; set; }
public virtual DateTime? FailedPasswordAnswerAttemptWindowStart { get; set; }
public virtual string Comment { get; set; }
public virtual IList<Roles> RoleList { get; set; }
public virtual Profiles Profiles { get; set; }
}
}
using System;
using System.Collections.Generic;
namespace Custom.NHibernateLib.Model
{
public class Roles : Entity
{
public Roles(){}
public virtual long RoleId { get; set; }
public virtual string ApplicationName { get; set; }
public virtual string RoleName { get; set; }
public virtual IList<Users> UserList { get; set; }
}
}
Adding the user and the role to their respective collections.
usr.RoleList.Add(role);
role.UserList.Add(usr);
When this is called session.SaveOrUpdate(role) then this occurs in NHibernate.
-- statement #1
UPDATE Users...WHERE UserId = 32768
-- statement #2
UPDATE Roles...WHERE RoleId = 65536
-- statement #3
INSERT INTO UserRoles...
Ok well I can conclude the my issue was caused by the way I was handling the session and commit after the save and update. I was closing the session then recreating it within multiple methods in my custom membership library code. For example, each method I was calling I was wrapping it around a using statement for the session and transaction. I should have known better and not go by my assumptions and just spend the time to RTM.
The NHibernate in Action book about session management and using current session context is what guided me. I coded that up to use in my unit test and everything worked fine. Though the book is a bit dated it still had some good basic info.
This is my domain class:
public class User
{
public Guid id { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public string mailAddress { get; set; }
}
This is my mapping file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Test" namespace="Test.model">
<class name="User" >
<id name="id">
<generator class="guid"/>
</id>
<property name="firstName" />
<property name="lastName" />
<property name="mailAddress" />
</class>
</hibernate-mapping>
This is My hibernate-cfg file:
<?xml version="1.0" encoding="utf-8" ?>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="dialect">NHibernate.Dialect.MySQLDialect</property>
<property name="connection.driver_class">NHibernate.Driver.MySqlDataDriver</property>
<property name="connection.connection_string">Server=localhost;Database=vbook;User ID=root;Password=ziben</property>
<!--<property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>-->
<property name="hbm2ddl.auto">update</property>
<property name="generate_statistics">true</property>
<property name="show_sql">true</property>
<mapping file="data/mapping/User.hbm.xml" />
This is how I create session factory:
var configuration = new Configuration();
configuration.Configure();
_sessionFactory = configuration.BuildSessionFactory();
I get the following exception:
Could not compile the mapping document: data/mapping/User.hbm.xml
I can't understand why.
Help please.
Any C# property of your entiy must be declared as virtual. Change your class this way:
public class User
{
public virtual Guid id { get; set; }
public virtual string firstName { get; set; }
public virtual string lastName { get; set; }
public virtual string mailAddress { get; set; }
}
Hope someone can point me in the right direction. I'm just starting out with nHibernate and a little confused over this one. It's running within a .Net Web Application.
Basically I've got 2 classes - Coupon and Publisher.
As a test, and to make sure NH was set up correctly, I accessed the PublisherRepository and pulled a publisher out by it's name. That works fine and reports success.
IPublisherRepository repo = new PublisherRepository();
Response.Write(repo.GetByName("Publisher 5"));
As a second test, I then used the CreateQuery method to get all of the Publishers, like so:
IQuery query = session.CreateQuery("from CartManData.Domain.Publisher pub");
This returns no data - the list is empty. Same goes using Linq:
session.Query<Publisher>().Where(x=>x.Name == "Publisher 4").ToList<Publisher>()
Using Sql Profiler I can see that the first test hits the database, and retrieves the set (called Coupons belonging to a Publisher) as lazy loading is off. However, the second 2 approaches don't hit the database at all - and I'm stumped as to why.
Here's the mapping file for Publisher and Coupon. They're embedded and I know they're working otherwise the repo wouldn't work either :
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="CartManData"
namespace="CartManData.Domain">
<class name="Publisher" lazy="false">
<id name="Id">
<generator class="guid"></generator>
</id>
<property name="Name"></property>
<property name="AddressLine1"></property>
<property name="AddressLine2"></property>
<property name="AddressLine3"></property>
<property name="Town"></property>
<property name="PostCode"></property>
<property name="Telephone"></property>
<property name="Email"></property>
<property name="Enabled"></property>
<property name="CommissionRate"></property>
<set name="Coupons" cascade="none" lazy="false">
<key column="PublisherId" ></key>
<one-to-many class="Coupon" />
</set>
</class>
</hibernate-mapping>
And Coupons:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="CartManData"
namespace="CartManData.Domain">
<class name="Coupon">
<id name="Id">
<generator class="guid"></generator>
</id>
<property name="Name"></property>
<property name="EffectiveFrom"></property>
<property name="EffectiveTo"></property>
<property name="UnitPrice"></property>
<property name="OriginalPrice"></property>
<property name="CouponImage"></property>
<property name="Enabled"></property>
<property name="PublisherId" not-null="false"></property>
</class>
</hibernate-mapping>
Any help on this really appreciate - sure it's something I've missed.
Cheers,
Tony
Additional Info
The Session object is retrieved via a HttpModule, where the session object is bound to CurrentSessionContext. That seems to be working fine as if you check to see if session is open, it reports it is.
PublisherRepository.GetByName() looks like this:
using (ISession session = NHibernateHelper.OpenSession())
{
return session.CreateCriteria(typeof(Publisher))
.Add(NHibernate.Criterion.Restrictions.Eq("Name", name))
.UniqueResult<Publisher>();
}
Log4Net Output
During the call via CreateQuery (the 2nd example above), this is what NHibernate is reporting:
2012-08-22 16:22:28,075 [15] DEBUG rollingFile - START of retrieval
2012-08-22 16:22:28,081 [15] DEBUG NHibernate.Engine.Query.QueryPlanCache - unable to locate HQL query plan in cache; generating (from CartManData.Domain.Publisher pub)
2012-08-22 16:22:28,128 [15] DEBUG NHibernate.Hql.Ast.ANTLR.HqlParseEngine - parse() - HQL: from CartManData.Domain.Publisher pub
2012-08-22 16:22:28,174 [15] DEBUG NHibernate.Hql.Ast.ANTLR.ErrorCounter - throwQueryException() : no errors
2012-08-22 16:22:28,200 [15] DEBUG NHibernate.Engine.Query.QueryPlanCache - unable to locate HQL query plan in cache; generating (from CartManData.Domain.Publisher pub)
2012-08-22 16:22:28,201 [15] DEBUG NHibernate.Hql.Ast.ANTLR.HqlParseEngine - parse() - HQL: from CartManData.Domain.Publisher pub
2012-08-22 16:22:28,202 [15] DEBUG NHibernate.Hql.Ast.ANTLR.ErrorCounter - throwQueryException() : no errors
2012-08-22 16:22:28,206 [15] DEBUG NHibernate.Engine.Query.HQLQueryPlan - enumerable: from CartManData.Domain.Publisher pub
2012-08-22 16:22:28,208 [15] DEBUG NHibernate.Engine.QueryParameters - named parameters: {}
2012-08-22 16:22:28,210 [15] DEBUG rollingFile - End of retrieval
You seem to have problems with your mappings.
I've tried to recreate your situation and I have changed your hbm files a little bit
Publisher
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="CartManData"
namespace="CartManData.Domain">
<class name="Publisher" lazy="false">
<id name="Id">
<generator class="guid"></generator>
</id>
<property name="Name"></property>
<property name="AddressLine1"></property>
<property name="AddressLine2"></property>
<property name="AddressLine3"></property>
<property name="Town"></property>
<property name="PostCode"></property>
<property name="Telephone"></property>
<property name="Email"></property>
<property name="Enabled"></property>
<property name="CommissionRate"></property>
<set name="Coupons" cascade="all-delete-orphan" inverse="true" lazy="false">
<key column="PublisherId" />
<one-to-many class="CartManData.Domain.Coupon, CartManData" />
</set>
</class>
</hibernate-mapping>
Coupon
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="CartManData"
namespace="CartManData.Domain">
<class name="Coupon">
<id name="Id">
<generator class="guid"></generator>
</id>
<property name="Name"></property>
<property name="EffectiveFrom"></property>
<property name="EffectiveTo"></property>
<property name="UnitPrice"></property>
<property name="OriginalPrice"></property>
<property name="CouponImage"></property>
<property name="Enabled"></property>
<many-to-one class="CartManData.Domain.Publisher, CartManData" name="Publisher">
<column name="PublisherId" not-null="true" />
</many-to-one>
</class>
</hibernate-mapping>
As you can see I've used a many-to-one relationship on Coupon.
You can read how these relationships work here
I've used the inverse="true" in the set defined for the Publisher mapping. Some more information about inverse.
If you want to have a look at the 2 classes:
public class Publisher
{
public Publisher()
{
this.Coupons = new HashSet<Coupon>();
}
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual string AddressLine1 { get; set; }
public virtual string AddressLine2 { get; set; }
public virtual string AddressLine3 { get; set; }
public virtual string Town { get; set; }
public virtual string PostCode { get; set; }
public virtual string Telephone { get; set; }
public virtual string Email { get; set; }
public virtual bool Enabled { get; set; }
public virtual decimal CommissionRate { get; set; }
public virtual ICollection<Coupon> Coupons { get; set; }
}
and
public class Coupon
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual DateTime EffectiveFrom { get; set; }
public virtual DateTime EffectiveTo { get; set; }
public virtual double UnitPrice { get; set; }
public virtual double OriginalPrice { get; set; }
public virtual string CouponImage { get; set; }
public virtual bool Enabled { get; set; }
public virtual Publisher Publisher { get; set; }
}
You can download a working example (NHVariousTests) here.
I am getting "illegal access to loading collection" exception while trying to populate "IList" property in Supplier Domain using NHibernate . I have tried all suggestions I got by googling but nothing seems to help :(
Here are my domain objects and .HBM files. I would greatly appreciate your help/suggestions.
Supplier Domain Object
namespace Inventory.DomainObjects
{
[Serializable]
public class Supplier
{
public virtual string SupplierID { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual IList<Address> Address { get; set; }
}
}
Address Domain Object
namespace Inventory.DomainObjects
{
[Serializable]
public class Address
{
public virtual int AddressID { get; set; }
public virtual string SupplierID { get; set; }
public virtual string Line1 { get; set; }
public virtual string Line2 { get; set; }
public virtual string Line3 { get; set; }
}
}
Supplier.HBM
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Inventory.DomainObjects"
assembly="Inventory">
<class name="Supplier" table="Inv_Supplier">
<id name="SupplierID" column="SupplierId" type="string"/>
<property name="SupplierCode" column="Code" type="string"/>
<property name="Name" column="SupplierName" type="string"/>
<property name="Description" column="SupplierDescription" type="string"/>
<bag name="Address" cascade="all" inverse="true" lazy="true">
<key column="SupplierID" not-null="true"/>
<one-to-many class="Address" not-found="ignore"/>
</bag>
</class>
</hibernate-mapping>
Address.HBM
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Inventory.DomainObjects"
assembly="Inventory">
<class name="Address" table="Inv_Supplier_Address" lazy="false">
<id name="AddressID" column="AddressId" type="integer"/>
<property name="Line1" column="Line1" type="string"/>
<property name="Line2" column="Line2" type="string"/>
<property name="Line3" column="Line3" type="string"/>
<many-to-one name="SupplierID" column="SupplierId" not-null="true" class="Supplier" />
</class>
</hibernate-mapping>
This looks suspicious:
<many-to-one name="SupplierID" column="SupplierId"
not-null="true" class="Supplier" />
Could you try removing the above line to see if the problem goes away?
If this fixes the problem you should add the many-to-one back as follows:
namespace Inventory.DomainObjects
{
[Serializable]
public class Address
{
public virtual int AddressID { get; set; }
// CHANGED: reference supplier object instead of ID
public virtual Supplier Supplier { get; set; }
public virtual string Line1 { get; set; }
public virtual string Line2 { get; set; }
public virtual string Line3 { get; set; }
}
}
then change your hbm mapping file like this (to reference the Supplier property instead of SupplierId
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Inventory.DomainObjects"
assembly="Inventory">
<class name="Address" table="Inv_Supplier_Address" lazy="false">
<id name="AddressID" column="AddressId" type="integer"/>
<property name="Line1" column="Line1" type="string"/>
<property name="Line2" column="Line2" type="string"/>
<property name="Line3" column="Line3" type="string"/>
<many-to-one name="Supplier" column="SupplierId"
not-null="true" class="Supplier" />
</class>
</hibernate-mapping>
I have a following class:
MyClass
public virtual int Id { get; set; }
public virtual int Code { get; set; }
public virtual int Description { get; set; }
public virtual int Name { get; set; }
with the following mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="TestApplication" assembly="TestApplication">
<class name="MyClass" table="MyTable">
<id name="Id" column="id">
<generator class="native"/>
</id>
<property name="Code" column="code"/>
<property name="Description" column="description"/>
<property name="Name" formula="(SELECT b.translation FROM translations b WHERE b.translation_id = translation_id AND b.language_id = :TranslationFilter.LanguageId)"/>
</class>
<filter-def name="TranslationFilter">
<filter-param name="LanguageId" type="Int32"/>
</filter-def>
</hibernate-mapping>
I'm trying to load entity through spring with:
Session.EnableFilter("TranslationFilter").SetParameter("LanguageId", 1);
return Session.Get<MyClass>(1);
but I'am getting adoexception. I see (in a profiler) that variable :TranslationFilter.LanguageId is not replaced with ? and that parameter value is not send to the server?
Is it this possible (to have filters in formula) and how?
Many thanks!
This feature is not officially supported. As such oren's blog post about this combination of 2 different features (formulas and filters) should be taken with a grain of salt...