NHibernate QuerySyntaxException - nhibernate

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.

Related

NHibernate one-to-one relationship to a mapped view

I have a mapped view, which I would like to reference in another mapping.
Mapped View:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Model.TagBE, Model" table="vw_CurrentTag" lazy="true" schema="dbo">
<id name="Id" column="TAG_HISTORY_ID" type="Guid"
unsaved-value="00000000-0000-0000-0000-000000000000">
</id>
<property name="Tag" column="TAG"/>
<property name="TagStatus" column="TAG_STATUS"/>
<property name="Created" column="CREATE_DATE"/>
<many-to-one name="Defect" class="Model.DefectBE, Model" fetch="join"
not-found="ignore" cascade="none" column="DEFECT_ID" />
<many-to-one name="CreatedBy" class="Model.UserBE, Model" lazy="false"
fetch="join" cascade="none" column="CREATE_USER_ID" />
</class>
</hibernate-mapping>
I would like to use it in this mapping, to get a single TagBE:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="DefectBE, Model" lazy="true" table="DEFECT" schema="dbo">
<id name="Id" column="DEFECT_ID" type="Guid">
<generator class="assigned" />
</id>
<property name="Sn" column="SERIAL_NUMBER" />
<property name="Description" column="PART_DESCRIPTION" />
<many-to-one name="System" class="Model.SystemBE, Model" fetch="join"
cascade="none" column="SYSTEM_ID" not-found="ignore"/
</class>
</hibernate-mapping>
The above mappings have been trimmed down for the sake of an example (removed a bunch of properties and and man-to-one relationships not related to the problem at hand.
The many-to-one shown with the name "System" works as expected, and it is just another mapped table, not a view. My problem is that when I try to map the view "vw_CurrentTag", I can only access the data when I specifically use ISession and ICriteria to get specificaly something of the TagBE class.
When I try to use the following in the DefectBE mapping, it just gives gives null for the TagBE:
<many-to-one name="Tag" class="Model.TagBE, Model" fetch="join" cascade="none"
column="DEFECT_ID" not-found="ignore"/>
I have also tried using one-to-one, but i always get null when the TagBE object part of a DefectBE object.
If it's not possible I can always use a Bag with only 1 entry.
The view is just a query on a single table to return unique Tag column based on other columns.
the many-to-one "Tag" uses the column DEFECT_ID on the table DEFECT to match the primary key of the view TAG_HISTORY_ID which is never equal.
What you probably want is a one-to-one mapping on the Defect class which points to the DEFECT_ID on the view.
<one-to-one name="Tag" class="Model.TagBE, Model" fetch="join" property-ref="Defect" foreign-key="none"/>

Hibernate collection loaded by custom SQL

I have some problems trying to get a bag collection of a class to be loaded through custom sql.
Here's the xml mappings I have for my class
<hibernate-mapping>
<class name="alekso.npe.model.Utente" table="VNPEZZ_UTE_MAT" lazy="false">
<id name="matricola" column="C_UTE_MAT">
</id>
<property name="nome" column="T_NOM" />
<property name="cognome" column="T_COG" />
<property name="email" column="T_EML" />
<bag name="ruoli" table="VNPEZX_UTE_APP_TRN"
inverse="false" lazy="true" fetch="select" cascade="all" >
<key column="C_UTE_MAT" />
<one-to-many class="alekso.npe.model.Ruolo" />
<loader query-ref="rolesQuery"/>
</bag>
</class>
<class name="alekso.npe.model.Ruolo" table="VNPEZH_TIP_USR"
lazy="false" where="C_APP = 'NPE'">
<id name="codice" column="C_TIP_USR">
</id>
<property name="nome" column="T_DES_TIP_USR"/>
</class>
<sql-query name="rolesQuery">
<return alias="role" class="alekso.npe.model.Ruolo"></return>
<load-collection alias="ruoli" role="alekso.npe.model.Ruolo" ></load-collection>
<![CDATA[select {ruolo.*}
from NPEA.vnpezx_ute_app_trn permesso join NPEA.vnpezh_tip_usr ruolo
on ruolo.c_tip_usr = permesso.c_tip_usr
where permesso.c_app = 'NPE'
and permesso.c_ute_mat = :matricola ]]>
</sql-query>
</hibernate-mapping>
But when I run the application I get an error:
org.hibernate.HibernateException: Errors in named queries: rolesQuery in the buildSessionFactory phase.
Can you tell me what's wrong with this mapping?
I tried both with and without the <return> tag inside the <sql-query> but it still don't work
I found out the error... the line <load-collection alias="ruoli" role="alekso.npe.model.Ruolo" ></load-collection> inside the custom query definition gives the error.
Without that line, everything works fine (almost... the collection of roles is lazy loaded even if I set lazy="false" everywhere).
But that line I removed is on the documentation, so I'd like to know why it's wrong.
I'll leave the question open for other to give some hints on this.

Why doesnt NHibernate eager fetch my data

I am using Nhibernate for my ORM.
I have a class "Control" that has a one to many relationship with ControlDetail (ie. A control has many controlDetails).
In the control xml config it has the following
<bag name="ControlDetails" lazy="true" access="property" order-by="SortOrder asc" cascade="all-delete-orphan"
table="ControlDetail">
<key column="ControlID"/>
<one-to-many class="ControlDetail"/>
</bag>
such that I believe unless otherwise told it would lazy load the controldetails of a control.
I am running NHProf to try and fix some performance issues we are having and it has identfied a Select N + 1 issue around these classes.
We are using a repository DA layer and I have tried to see if I can add in a way to eagerly fetch the data when required and came up with this.
public T GetById<T>(Int32 id, List<string> fetch) where T : BaseObject
{
T retObj = null;
ISession session = EnsureCurrentSession();
{
ICriteria criteria = session.CreateCriteria(typeof (T));
criteria.SetCacheable(true);
criteria.Add(Expression.Eq("Id", id));
foreach(var toFetch in fetch)
{
criteria.SetFetchMode(toFetch, FetchMode.Eager);
}
retObj = criteria.List<T>().FirstOrDefault();
}
return retObj;
}
*Note: I'm not a fan of how the repository is setup but it was done before I came to the project so we have to stick with this pattern for now.
I call this method like so
public Control GetByIDWithDetail(int controlID)
{
return DataRepository.Instance.GetById<Control>(controlID, new List<string>() {"ControlDetail"});
}
When I debug the GetByID method and look at the retObj I can see that the ControlDetails list has been populated (although strangely enough I also noticed without the setfetchmode set the list was being populated)
Even with this fix NHProf identifies a Select N+1 issue with the following line
List<ControlDetail> details = control.ControlDetails.ToList();
What exactly am I missing and how can I stop this N+1 but still iterate over the controlDetails list
EDIT: the xml configs look like so (slightly edited to make smaller)
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DomainObjects" assembly="DomainObjects">
<class name="Control" lazy="false" table="Control" optimistic-lock="version" select-before-update="true" >
<id name="Id" type="int" column="ControlID" access="property">
<generator class="native" />
</id>
<version name="Version" column="Version" />
<property name="AdministrativeControl" column="AdministrativeControl" access="property" not-null="true" />
<property name="Description" column="ControlDescription" access="property" />
<property name="Title" column="Title" access="property" />
<property name="CountOfChildControls" access="property" formula="(select count(*) from Control where Control.ParentControlID = ControlID)" />
<bag name="ControlDetails" lazy="true" access="property" order-by="SortOrder asc" cascade="all-delete-orphan"
table="ControlDetail">
<key column="ControlID" />
<one-to-many class="ControlDetail" />
</bag>
</class>
</hibernate-mapping>
and this
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DomainObjects" assembly="DomainObjects">
<class name="ControlDetail" lazy="false" table="ControlDetail" select-before-update="true" optimistic-lock="version">
<id name="Id" type="int" column="ControlDetailID" access="property">
<generator class="native" />
</id>
<version name="Version" column="Version" />
<property name="Description" column="Description" access="property" not-null="true" />
<property name="Title" column="Title" access="property" />
<many-to-one name="Control" lazy="false" class="Control" column="ControlID" access="property"/>
</class>
</hibernate-mapping>
There is a substantial difference between eager fetching and eager loading. Fetching means: putting into the same query. It has several side effects and may break it. Eager loading means forcing NH to load it immediately, not waiting until it is accessed the first time. It is still loaded using additional queries, which leads to the N+1 problem.
NH probably does not eagerly fetch because the property is called ControlDetails, but you pass ControlDetail as argument.
On the other side, this isn't a good way to avoid the N+1 problem. Use batch-size instead. It is fully transparent to the application and reduces the amount of queries by the given factor (values from 5 to 50 make sense, use 10 if you don't know what to use).
One option, you can reduce the select n+1 problem, keep lazy loading and forget about eager loading....
All you need to do is to add one simple attribute batch-size to your XML
<bag name="ControlDetails" batch-size="25" ..>
I also noticed that you have lazy="true" in your mapping, did you try changing to false?

How to get nhibernate to cache tables referenced via many-to-one - is my config correct?

I've been trying to get this working for a while now with no luck. I want to enable the 2nd level cache to prevent fetching from some lookup tables.
I set up my configuration in code
cfg = new Configuration();
cfg.Properties[NHibernate.Cfg.Environment.ConnectionProvider] = "NHibernate.Connection.DriverConnectionProvider";
string connectionString = connection.ConnectionString;
cfg.Properties[NHibernate.Cfg.Environment.ConnectionString] = connectionString;
cfg.Properties[NHibernate.Cfg.Environment.ConnectionDriver] = "NHibernate.Driver.SqlClientDriver";
cfg.Properties[NHibernate.Cfg.Environment.Dialect] = "NHibernate.Dialect.MsSql2005Dialect";
cfg.Properties[NHibernate.Cfg.Environment.CommandTimeout] = "720";
cfg.Properties[NHibernate.Cfg.Environment.CacheProvider] = "NHibernate.Cache.HashtableCacheProvider";
cfg.Properties[NHibernate.Cfg.Environment.UseSecondLevelCache] = "true";
cfg.Properties[NHibernate.Cfg.Environment.UseQueryCache] = "true";
cfg.AddAssembly("APPName.PersistentEntities");
factory = cfg.BuildSessionFactory();
Then in my xml configuration I added the cache attribute:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="APPName.PersistentEntities.LockStatus, APPName.PersistentEntities" table="LockStatus" lazy="false">
<meta attribute="class-description">lockstatus</meta>
<cache usage="read-only" />
<id name="Id" column="Id" type="Int32" unsaved-value="0">
<generator class="native"></generator>
</id>
<property name="Name" column="Name" type="String" length="100" not-null="true"/>
</class>
</hibernate-mapping>
This entity is then referenced from other uncached entities
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="APPName.PersistentEntities.Entity, APPName.PersistentEntities" table="Entity" lazy="false">
<meta attribute="class-description">entity</meta>
<id name="Id" column="Id" type="Int32" unsaved-value="0">
<generator class="native">
</generator>
</id>
<property name="Name" column="Name" type="String" length="500" not-null="true">
<meta attribute="field-description">name</meta>
</property>
<many-to-one name="LockStatus" column="LockStatusId" class="APPName.PersistentEntities.LockStatus, APPName.PersistentEntities" not-null="false" lazy="false">
</many-to-one>
</class>
</hibernate-mapping>
>
I query this other entity with something like:
session = factory.OpenSession();
IList<T> s = session.CreateSQLQuery(query).AddEntity(typeof(T)).SetCacheable(true).List<T>();
session.Clear();
session.Close();
The query and mappings run fine so to make sure its using the cache I try updating the names in the database. When I click again in the application I see the updated names so I assume it is not using the cache re-querying.
you need to add a cache declaration on the relation as well (LockStatus).
also-
you can use nHibernate's logging to see exactly the sql sent on every call.
I don't see why you'd want to use an SQLQuery in your case; you can simply use Query or QueryOver.

NHibernate and sqlite - Could not compile the mapping document

I'm trying to run an NHibernate over sqlite.
i have two projects:
1. Orange.Database - holds the pocos and daos and everything else
2. Orange.GUI - holds the gui...
when the program reach to the reach the:
Configuration config = new Configuration();
config.AddAssembly("Orange.Database");
sessionFactory = config.Configure().BuildSessionFactory();
an exception is thrown: "Could not compile the mapping document: Orange.Database.Pocos.City.hbm.xml "
inner exception: "Could not find the dialect in the configuration"
city.hbm.xml:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Orange.Database.Pocos">
<class name="City" table="Cities">
<id name="Id">
<column name="Id" sql-type="int" not-null="true"/>
<generator class="identity"/>
</id>
<property name="Name">
<column name="name" not-null="true"/>
</property>
<property name="IsTaxFree">
<column name="is_tax_free" not-null="true"/>
</property>
</class>
</hibernate-mapping>
I tried writting the assembly, and then removed it..
the app.config file:
<?xml version="1.0" encoding="utf-8" ?>
<configSections>
<section name="hibernate-configuration"
type="NHibernate.Cfg.ConfigurationSectionHandler,NHibernate"/>
</configSections>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.SQLiteDriver</property>
<property name="connection.connection_string">
Data Source=C:\Users\Nadav\Documents\Visual Studio 2005\Projects\orange\DB\OrangeDB\OrangeDB.db;Version=3
</property>
<property name="dialect">NHibernate.Dialect.SQLiteDialect</property>
<property name="query.substitutions">true=1;false=0</property>
</session-factory>
</hibernate-configuration>
</configuration>
I've tried different location of the db file..
i have tried to remove the configSections
and some other ideas i found on the web...
I'm using vs 2005
NHibernate version is 2.0.1.4000
Any suggestions?
Do this in your code:
Configuration config = new Configuration();
config.Configure();
config.AddAssembly("Orange.Database");
sessionFactory = config.BuildSessionFactory();
First, download the latest stable version of NHibernate, 2.1.2.
Second, try creating a configuration file named hibernate.cfg.xml, containing:
<?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="connection.driver_class">NHibernate.Driver.SQLiteDriver</property>
<property name="connection.connection_string">
Data Source=C:\Users\Nadav\Documents\Visual Studio 2005\Projects\orange\DB\OrangeDB\OrangeDB.db;Version=3
</property>
<property name="dialect">NHibernate.Dialect.SQLiteDialect</property>
<property name="query.substitutions">true=1;false=0</property>
<property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
</session-factory>
</hibernate-configuration>
I got the same "could not compile error" but I'm not using sqlite.
I got it because I was calling the configure method from the nhibernate configuration instance twice.