I'm having a problem with NHibernate and the mapping file when I try to save my drink object in my MVC application. My mapping file is an embedded resource and my hibernate.cfg.xml is copy always.
Here are my class.cs:
namespace FrancosPoS.DBMapping {
public class drink {
public drink() { }
public virtual int id { get; set; }
public virtual string type { get; set; }
public virtual string price { get; set; }
}
}
My XML mapping:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping assembly="FrancosPoS.DBMapping" namespace="FrancosPoS.DBMapping" xmlns="urn:nhibernate-mapping-2.2">
<class name="drink" table="drink" lazy="true" >
<id name="id">
<generator class="identity" />
<column name="id" sql-type="int(11)" not-null="true" />
</id>
<property name="type">
<column name="type" sql-type="varchar(25)" not-null="true" />
</property>
<property name="price">
<column name="price" sql-type="varchar(8)" not-null="true" />
</property>
</class>
</hibernate-mapping>
By the way, the connection opens and close fine if I don't try to save it on the database.
Here is my Solution Explorer:
Solution Explorer Image
And here is the error that is driving me nuts:
"Error: NHibernate.MappingException: FrancosPoS.DBMapping.drink.hbm.xml(6,8): XML validation error: The element 'id' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'column' in namespace 'urn:nhibernate-mapping-2.2'. ---> System.Xml.Schema.XmlSchemaValidationException: The element 'id' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'column' in namespace 'urn:nhibernate-mapping-2.2'.\r\n --- End of inner exception stack trace ---
the problem is in node id, it does not have a child, remove that node and it will simply be like this:
<id name="id">
<generator class="identity" />
</id>
the column's name is specified in id node, and you dont have to tell column has "not-null" constraint since id columns are always required
Also, in this line of your xml mapping:
<hibernate-mapping assembly="FrancosPoS.DBMapping" namespace="FrancosPoS.DBMapping" xmlns="urn:nhibernate-mapping-2.2">
"assembly" attribute seems to be wrong, it should be the name of your assembly (which i'm almost sure is "FrancosPos").
Related
Mapping file:
<?xml version="1.0"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true">
<class name="PlayHibernate.Employee, PlayHibernate" lazy="false">
<id name="id" access="field">
<generator class="native"/>
</id>
<property name="name" access="field" column="name"/>
<many-to-one access="field" name="manager" column="manager" cascade="all"/>
</class>
</hibernate-mapping>
I tried setting the mapping file as a resource, but that just changes the error message to
"Unhandled Exception: NHibernate.MappingException: PlayHibernate.Employee.hbm.xml
(5,31): XML validation error: The element 'id' in namespace 'urn:nhibernate-mapp
ing-2.2' cannot contain text. ---> System.Xml.Schema.XmlSchemaValidationExceptio
n: The element 'id' in namespace 'urn:nhibernate-mapping-2.2' cannot contain tex
t."
The first error "No persister for..." was a consequence of not setting mapping file as an embedded resource.
The current error has something to do with your id definition. It looks like you have some text within your <id> definition. Maybe some unallowed whitespaces? Try to write it as:
<id name="id" access="field" column="uid" generator="native" />
Also if that doesn't help, try to use some other name than id for a field name. Maybe Id.
In visual studio remember change the property: "Compilation action" of your .hbm.xml file to : "Embedded resource"
I am relatively new to using Nhibernate but basic things already are working.
Now I have to map the generic entity that implements the tree structure. Separately, each one (only generic or only tree) works fine.
Here's the code for the model:
public class Test<T>
{
public virtual Int64 Id { get; set; }
public string Name { get; set; }
public IList<Test<T>> Children { get; set; }
}
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="PersistencyObjectModel" namespace="PersistencyObjectModel.Domain">
<class name="Test`1[String]">
<id name="Id">
<generator class="guid"/>
</id>
<property name="Name" length="50" not-null="true" unique="true"/>
<set name="Children" table="TEST_TEST_LINK" cascade="all-delete-orphan" >
<key column="ParentId"/>
<many-to-many column="ChildId" class="Test`1[String]"/>
</set>
</class>
</hibernate-mapping>
When I use that model, I get the following Nhibernate error:
{"persistent class PersistencyObjectModel.Domain.Test`1[[PersistencyObjectModel.Domain.String,
PersistencyObjectModel]], PersistencyObjectModel not found"}
What does this error mean and how can I fix it?
Update: try this
<hibernate-mapping namespace="PersistencyObjectModel.Domain"
assembly="PersistencyObjectModel"
xmlns="urn:nhibernate-mapping-2.2">
<class name="PersistencyObjectModel.Domain.Test`1[[System.String,
mscorlib, Version=4.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089]]"
table="TestOfString">
<id name="Id">
<generator class="guid.comb"/>
</id>
<property name="Name" length="50" not-null="true" unique="true"/>
<set name="Children" table="TEST_TEST_LINK">
<key column="ParentId" />
<many-to-many column="ChildId"
class="PersistencyObjectModel.Domain.Test`1[[System.String,
mscorlib, Version=4.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089]]"/>
</set>
</class>
</hibernate-mapping>
Some things to note:
You can't use a guid generator with a long propery; change that to Guid
You must specify the name of the entity table
Use fully qualified names.
I am having an issue with saving entities with one-to-one relationships. I just want to save the parent entity and have the child be save aswell but I am having to save both separately.
This is an example of what I am having to do, otherwise the child is not saved.
var session = SessionProvider.OpenSession.Session;
using (var tx = session.BeginTransaction())
{
try
{
session.SaveOrUpdate(parent);
if (parent.Child.IsPersisted)
{
session.Update(parent.Child);
}
else
{
session.Save(parent.Child);
}
}
}
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="false" assembly="xxx">
<class name="Parent" polymorphism="explicit" table="Parent">
<id name="Id" column="JointID" type="int">
<generator class="native" />
</id>
<one-to-one name="Child" class="Child" />
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="false"
assembly="xxx">
<class name="Child" xmlns="urn:nhibernate-mapping-2.2" polymorphism="explicit" table="Child">
<id name="Id" column="JointID" type="int" unsaved-value="0">
<generator class="native" />
</id>
<many-to-one name="Parent" column="JointID" insert="false" update="false" />
</class>
</hibernate-mapping>
Any ideas on how I can just make it save without having to do two save calls?
When I set the relationship to cascade as suggested below I get foreign key constraint errors. If I analyse the queries with NHProf, its trying to use the temporary id (-1) as the JointId in the insert statement rather than the newly created parent id. The JointId in the Parent table is an Identity key, perhaps that is an issue?
You'll need to enable cascading on your <one-to-one> mapping to have this work properly.
Something like:
<one-to-one name="Child" class="Child" cascade="save-update" />
You can read up on the various cascade settings here.
I am then using Fluent NHibernate and its automapping feature to map the the following simplified POCO classes:
public class Webpage
{
public virtual int Id { get; set; }
public virtual string UrlIdentifier { get; set; }
public virtual WebpageType WebpageType { get; set; }
}
public class WebpageType
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
I am then overriding the following mapping to explicitly set no cascading from Webpage to WebpageType:
public class WebpageMap : IAutoMappingOverride<Webpage>
{
public void Override(AutoMapping<Webpage> mapping)
{
mapping.References(w => w.WebpageType).Cascade.None();
}
}
For any pur NHibernate readers, here are the xml mappings produced by fluent:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" name="EveryPage.Core.Domain.Webpage, EveryPage.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Webpage`">
<id name="Id" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" unsaved-value="0">
<column name="Id" />
<generator class="identity" />
</id>
<property name="UrlIdentifier" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="UrlIdentifier" />
</property>
<many-to-one cascade="none" class="EveryPage.Core.Domain.WebpageType, EveryPage.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="WebpageType">
<column name="WebpageType_id" />
</many-to-one>
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" name="EveryPage.Core.Domain.WebpageType, EveryPage.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`WebpageType`">
<id name="Id" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" unsaved-value="0">
<column name="Id" />
<generator class="identity" />
</id>
<property name="Name" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="Name" />
</property>
</class>
</hibernate-mapping>
The problem comes when I test that updates do not cascade to WebpageType via webpage, basically they do!!
I have the following test:
[Test]
public void Assert_SaveOrUpdate_On_Webpage_Does_Not_Cascade_Update_To_WebpageType()
{
// Get the existing webpage.
webpage = _webpageRepository.Get("~/testwebpage1.aspx");
// Update the WebpageType.
const string webpageTypeName = "qwerty test";
webpage.WebpageType.Name = webpageTypeName;
// Save the changes.
Assert.DoesNotThrow(() => _webpageRepository.SaveOrUpdate(webpage));
// We need to flush the changes to the store for it to execute the changes.
Assert.DoesNotThrow(() => NHibernateSession.Current.Flush());
// Remove the webpage and tag from the level 1 cache so we force a trip to the store on our next check.
NHibernateSession.Current.Evict(webpage);
// Check that the webpageType has not been updated.
webpageType = _webpageTypeRepository.Get(webpageType.Id);
Assert.AreNotEqual(webpageTypeName, webpageType.Name);
}
The above test is wrapped in a global transaction.
The test fails and NHibernate does execute an update to the Name of the related WebpageType. The delete and save(create new) cascades work correctly and do not cascade.
Have I missunderstood cascade and/or is there a problem with my logic/test.
Any help/advice is appreciated. Thanks.
If you are trying to stop your app from accidentally changing properties on WebPageType, I think it would be easier and safer to achieve this by marking WebPageType as ReadOnly in the mapping. Then you won't need to protect it via handling cascading in all its associations.
I think this is a misunderstanding of what cascading means.
In your example, NHibernate will update the Name property of you WebPageType no matter what you set cascading to. If you think about it, how would the NHibernate library tell if you're manipulating the property's value using the association from the WebPage instance, or if it's done "directly"?
The settings for cascading in NHibernate tells how associations between entities should be handled, not how the actual value inside each entity is handled. For example, you can set delete cascading, which will automatically delete associated entities when the entity itself is deleted.
Things blog post might make things a bit clearer, or at least work as some kind of reference: http://ayende.com/Blog/archive/2006/12/02/NHibernateCascadesTheDifferentBetweenAllAlldeleteorphansAndSaveupdate.aspx
What does your repository do? Make sure it doesn't run a saveorupdate on the webpagetype. If it isn't then I don't see any obvious explanation for this behaviour.
I am following along with the Summer of NHibernate Screencast Series and am running into a strange NHibernate Exception.
NHibernate.Hql.Ast.ANTLR.QuerySyntaxException:
Exception of type
'Antlr.Runtime.NoViableAltException' was thrown.
[select from DataTransfer.Person p where p.FirstName=:fn].
I have deviated from the Screencast Series in the following ways:
Running against an MS SQL Server Compact Database
I am using MSTest instead of MbUnit
I've tried any number of combination of queries always with the same result. My present CreateQuery syntax
public IList<Person> GetPersonsByFirstName(string firstName)
{
ISession session = GetSession();
return session.CreateQuery("select from Person p " +
"where p.FirstName=:fn").SetString("fn", firstName)
.List<Person>();
}
While not a direct query this method works
public Person GetPersonById(int personId)
{
ISession session = GetSession();
return session.Get<Person>(personId);
}
My hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory name="BookDb">
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.SqlServerCeDriver</property>
<property name="dialect">NHibernate.Dialect.MsSqlCeDialect</property>
<property name="connection.connection_string">Data Source=C:\Code\BookCollection\DataAccessLayer\BookCollectionDb.sdf</property>
<property name="show_sql">true</property>
<property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
<mapping assembly="DataTransfer"/>
</session-factory>
</hibernate-configuration>
Person.hbm.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DataTransfer" namespace="DataTransfer">
<class name="DataTransfer.Person,DataTransfer" table="Person">
<id name="PersonId" column="PersonId" type="Int32" unsaved-value="0">
<generator class="native"/>
</id>
<property name="FirstName" column="FirstName" type="String" length="50" not-null="false" />
<property name="LastName" column="LastName" type="String" length="50" not-null="false" />
</class>
</hibernate-mapping>
I was also following the Summer of NHibernate Screencast Series and came across the same problem.
The problem is in the HQL "select from User p" change that to "select p from User p" or just "from User p".
The ’shorthand’ HQL form that was used in the screencasts under NHibernate version 1.2 was deprecated in 2.0 and eliminated in 2.1.x as the default query parser was switched out to be the more strict option.
public IList<Person> GetPersonsByFirstName(string firstName)
{
ISession session = GetSession();
return session.CreateQuery("select p from Person p where p.FirstName=:fn")
.SetString("fn", firstName)
.List<Person>();
}
Since you're specifying the namespace in the <hibernate-mapping element, you could write :
<class name="Person" table="Person">
....
After you try that, if it doesn't work - I have no idea why it isn't working. I've tried pretty much the example you gave and it worked .
I have seen the new parser throw some weird errors and you just have to go by trial and error when it happens :(.
Edit
About the trial and error : you could change the query to "from Person" see if that works(if it doesn't...i'm stuck ) . Then add the filter, first try directly p.FirstName = 'x'. Then try with parameter. You could try not adding the alias.
Also, try using the latest version of NH.
Edit 2
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateTests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" namespace="NHibernateTests">
<class name="User" table="`User`" xmlns="urn:nhibernate-mapping-2.2">
<id name="Id" type="Int32" column="UserId">
<generator class="assigned" />
</id>
<property name="UserName" type="String">
<column name="UserName" not-null="true" />
</property>
<property name="FName" type="String">
<column name="FName" />
</property>
</class></hibernate-mapping>
and the query :
IList<User> users = session.CreateQuery("select from User p " +
"where p.UserName=:fn").SetString("fn", "u")
.List<User>();
Worked like a charm.