nhibernate: Class Mapping with no table - nhibernate

I have an entity class that I use to represent the results of an sql-query. The mapping for the class shown below. Yet as far as I can tell nhiberate treats the mapping as if there is a real database table - when in fact there is not. In this case there is nothing in the database that represents this entity at all. I am using this to map a query through, but the same would be true of a view. Is there no way to indicate to nhibernate that there isn't a table represented by the mapping?
<class name="Models.UserTransaction"> <!-- Defaults table name same as Entity even though table doesn’t exist -->
<id name="Id">
<column name="Id" not-null="true" unique="true" />
<generator class="native" />
</id>
<property name="TransType" />
<property name="Date" />
<property name="Amount" />
<property name="Balance" />
</class>
This is the query I am mapping, which uses a user defined table. I couldn't get it working without having a mapping even though the example I copied appeared to.
<sql-query name="UserTransactions">
<query-param name="userId" type="string" />
<return class="Models.UserTransaction" alias="userTx">
<return-property name="TransType" column="TransType" />
<return-property name="Id" column="Id" />
<return-property name="Date" column="TransDate" />
<return-property name="Amount" column="Amount" />
<return-property name="Balance" column="Balance" />
</return>
<![CDATA[
SELECT userTx.[Type] as TransType, userTx.[Id] as Id, userTx.[Date] as TransDate, userTx.[Amount] as Amount, userTx.[Balance] as Balance
FROM dbo.User_AccountStatement(:userId) userTx
]]>
</sql-query>

If you have a db view, you can use nhibernate to map to that, but if all you are doing is storing the projection fields of the query there doesn't need to be a map at all.
How are you querying this data?
if you are using the criteria API, you can use the resultstransformer to map the returned object array to your class. the types have to match between your class that the projection.
if you are using the linq provider you can project directly into your class. so you'd have something like this
from s in Session.Query
where s.some-property== "some-value"
select new your-type
{
some-property-on-your-type = s.some-property,
some-other-property-on-your-type = s.some-other-property
}
There is no need to write a mapping to the database since you aren't mapping to an object in the database.

I guess you should at least specify a view as the tablename of your mapping.
The view should have the same resulting columns as your query (and hopefully return any row that your query could return)
Then you will be able to :
map your properties to your view/SQL query result columns
set your class as mutable="false"
declare your query as a named query (see : http://ayende.com/blog/3948/nhibernate-mapping-named-queries-query-and-sql-query )

Related

NHibernate with Second Level Cache Not Rehydrating Properties Marked insert="false" update="false"?

Having trouble with implementing second level cache in Nhibernate. I have a class mapped as follows:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Data" namespace="Data">
<class name="Account" table="Accounts" lazy="false">
<cache region="Standard" usage="read-write" include="all"/>
<id column="ID" name="ID">
<generator class="assigned" />
</id>
<version name="VersionStamp" column="VersionStamp" type="integer" unsaved-value="0" />
<property name="Name" not-null="true" />
<property name="Type" not-null="true" />
<property name="ClientID" not-null="true" insert="false" update="false" />
<property name="DateCreated" not-null="true" type="UtcDateTime" />
<property name="LastUpdatedDate" not-null="true" type="UtcDateTime" />
<property name="IsActive" not-null="true" />
<many-to-one name="Client" class="Client" column="ClientID" not-found="exception" not-null="true" />
</class>
</hibernate-mapping>
The property "ClientID" is a foreign key into the Clients table and the Client many-to-one property uses it to look up the associated client object.
When I add a new Account, I look up the Client object from the database with a Session.Get and assign it to my Account object's Client property. Behind the scenes, this also automatically populates the ClientID property when the object is written to the database, and the ID is correctly stored in the database. When I retrieve the Account object from the database by ID using Session.Get, all the fields are populated correctly when the object is retrieved.
However, when I implement the second level cache using the settings shown above, the ClientID property is NOT populated when the Account object is retrieved using Session.Get, but the Client property is populated correctly. Is there some reason why this will not work with second level cache? Or have I done something wrong in my mapping/configuration?
For now I am just using SysCache as my caching provider, and both query and second level cache are turned on. The Client class mapping contains a corresponding one-to-many property for the Accounts.
I like the convenience of having the ClientID property on my Account class, so that I can read it without using the Client property, and it seems to work fine without caching.
Thanks for any help.
Rich
I tried to reproduce your situation locally. With your mapping (used the same as the snippet above) I was able to get incorrect behaviour only on UPDATE. In that case, the ClientID was cached, and while the Client reference was changed, the ClientID remained unchanged. In other cases caching was working as expected.
The solution is to change the mapping. The below suggested mapping is the most suitable for read-only properties like ClientID. (I am using that approach as well).
<property name="ClientID" formula="[ClientId]"
not-null="true" insert="false" update="false" />
So the trick is in the formula mapping instead of Column (the default when none is provided)

Inheritance: Criterion on entity type and sub class properties

I have a inheritance hierarchy which I have mapped in NHibernate using Table-per-class. My mappping file looks like the one below (lots of properties omitted).
To query this hierarchy, I am building a dynamic DetachedCriteria for Message based on filter input from the user. Messages (of any type in the hierarchy) should be returned to the user in one list.
I would like to build a criteria based on the type of message, ie. the user could specify to get all messages of type SMSMessage or EmailMessage with a ReceivedDate > '2009-01-01'. How would I go about to do that?
In the same query, the user could specify that if the Message is an InternalMessage, it should have Priority = 2. How would I specify such type-specific predicates?
All this is possible to do in LINQ, so I am hoping I can do it in NHibernate as well.
<class name="Message" table="Message" abstract="true" discriminator-value="null">
<id name="MessageId">
<generator class="identity" />
</id>
<discriminator column="Type" type="byte" />
<property name="ParentId" />
<property name="ReceivedDate" />
...
<subclass name="SMSMessage" discriminator-value="0">
<property name="Text" column="Text" />
...
</subclass>
<subclass name="MMSMessage" discriminator-value="1">
<property name="Subject" />
...
</subclass>
<subclass name="EmailMessage" discriminator-value="2">
<property name="BodyPlainText" />
...
</subclass>
<subclass name="InternalMessage" discriminator-value="4">
<property name="Priority" />
...
</subclass>
</class>
I kind of figured this out myself, but in the end I ended up reverting to pure SQL since I hit too many roadblocks with HQL/Criterias. Anyways, I can share how I did this.
Maybe not pretty, but I solved it by adding the discriminator column as a regular property to the top level class in the hierarchy (Message) and employed restrictions against that column.
It turns out that you can specify restrictions against properties for subclasses even in the top-level query, so this was easier than I thought. It was just a matter of specifying the restrictions.

Multi Table mapped on one class in NHibernate

we use an external Database where we cant edit table designs only add own tables to extend the core tables.
So I need map two tables on one class, i try this:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="DaVinci"
namespace="DaVinci.Domain">
<class name="Vorgang" table="VORGANGSKOPF">
<id name="Id" column="ID">
<generator class="native" />
</id>
<property name="Vorgangsnummer" column="VORGANG" />
...
<join table="OWN_VORGANG_WAEHRUNG" optional="true">
<key column="VOR_ID" property-ref="Vorgangsnummer" />
<property name="WaehrungVK_Internet" column="WAEHRUNG" />
<property name="WaehrungsKursVK_Internet" column="KURS" />
<property name="Preis_Internet" column="BETRAG_EURO" />
<property name="PreisFremdWaehrung_Internet" column="BETRAG_FREMD" />
</join>
...
After testing i know now that "property-ref" for joins dosn't work.
Bugreport here
Does anyone know an other way to map two tables on one class?
(I'm struggling a little with the table and column names here.)
Could you use a joined subclass for this? Even if the tables don't follow the logical relationship, if there's a 1-to-1 database relationship between the two tables, then you'd get what you want by ignoring the parent, and doing all your operations on the subclass.

NHibernate: Where clause on one-to-many relationships doesn't work when column name is ambiguous

It is possible to specify an arbitrary SQL where clause for collection mappings. For example:
<map name="myEntity" where="foo = 1" />
However if the column name is ambiguous for some reason, the sql fails. For example, this can occur if you are trying to use joins for example.
Given that the table aliases are automatically generated, you can't qualify the column name. This makes the feature seem rather silly. Does anyone know if there is a work around?
NHibernate should figure out the correct alias for the property you are referencing. Is foo a mapped property of the item entity type (the item type that is in the map collection) ?
For example this works:
<class name="Category" table="Category">
<id name="Id">
<generator class="guid.comb" />
</id>
<property name="Name" not-null="true" length="255" />
<bag name="ProductList" table="Product" cascade="none" where="Name like '%test%'" fetch="join">
<key column="CategoryId" />
<one-to-many class="Product" />
</bag>
</class>
There is a property on both Category and the Product class named "Name" but nhibernate will in this case use the on defined on the Product class.

Hibernate Next/Previous Sibling Mapping

I'm using nHibernate to map an object very similar to .NET's System.Web.SiteMapNode. In order to keep my object similar to this .NET object I would like to have it contain a ParentNode, PreviousSibling, NextSibling, and ChildNodes complex properties.
The table looks somewhat like this and is open to be changed:
ID (int)
Title (string)
Description (string)
Key (string)
ParentNodeId (int)
OrdinalPosition (int)
ReadOnly (bool)
Url (string)
I may have some other properties that are not needed to mimic the .NET SiteMapNode object (like an isExternal bool), but I think those are inconsequential to this question.
My current mapping looks like this:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="AthletesCafe.Core.Domain.System.SiteMap" assembly="AthletesCafe.Core">
<class name="SiteMapNode" table="SiteMapNode" lazy="true" >
<id name="ID" type="Int32" unsaved-value="0">
<column name="ID" not-null="true" unique="true" index="PK_SiteMapNode"/>
<generator class="identity" />
</id>
<property name="Title" column="Title" type="String" length="255" not-null="true" />
<property name="Description" column="Description" type="String" not-null="false" />
<property name="Url" column="Description" type="String" not-null="true" />
<property name="SiteMapKey" column="SiteMapKey" type="String" not-null="true" length="255" />
<property name="OrdinalPosition" column="OrdinalPosition" type="Int32" not-null="true" />
<property name="ReadOnly" column="ReadOnly" not-null="true" type="System.Boolean" />
<property name="IsExternal" column="IsExternal" not-null="true" type="System.Boolean" />
<many-to-one name="ParentNode" column="ParentNodeId" class="AthletesCafe.Core.Domain.System.SiteMap.SiteMapNode, AthletesCafe.Core"
access="field.pascalcase-underscore" not-null="false" />
<many-to-one name="PreviousNode" column="ParentNodeId" class="EatMyTrainer.Core.Domain.SiteMap.SiteMapNode, EatMyTrainer.Core" not-null="false" /></hibernate-mapping>
The ParentNode mapping is easy as it should be just a simple many-to-one mapping. This is the code I have for it (untested, but I believe it to be correct):
<many-to-one name="ParentNode" column="ParentNodeId" class="AthletesCafe.Core.Domain.System.SiteMap.SiteMapNode, AthletesCafe.Core"
access="field.pascalcase-underscore" not-null="false" />
The mapping for the child nodes should just be a simple bag which will bring back all SiteMapNode objects that have the ParentNodeId equal to the current ID. I haven't written this bag yet, but I believe it to be not such a big deal.
The issue that I cannot seem to resolve is how to do the Next/Previous Sibling properties. This objects can be derived from the following formula for each node:
PreviousSibling: Has the same ParentNode (ParentNodeId) as the current object and its OrdinalPosition should be one less than the current object's OrdinalPosition.
NextSibling: Has the same ParentNode (ParentNodeId) as the current object and its OrdinalPosition should be one more than the current object's OrdinalPosition.
I think this is achievable through the formual attribute on a many-to-one mapping. Is this possible? I haven't found a good example of how this works.
I don't think what you're asking for is strictly possible (although I would be very interested to see the solution if it is). There would be a relatively simple workaround, but NHibernate does not support bidirectional one-to-many mappings with indexed collections on the many end.
The only thing that comes to mind is a bit ugly: have the parent object keep its own index map (keyed off the OrdinalPosition) to each child object. On the child do something like:
public SiteMapNode NextSibling()
{
return this.Parent.NextSibling(this);
}
I believe Stuart is correct in this situation. It is impossible to do for the many-to-one mapping. If NHibernate provided a way to do where clausing on this mapping then I may have a chance.
Another possible solution although inefficient is to create a bag that uses a field setter. The public property that would be Next/Previous setting would still return an object reference (as opposed to an enumerable). In the getter it would just reference the first position of the enumerable in the field. Lazy loading would be ideal because NHibernate wouldn't be able to load this object in one get with the initial load of the object. You would have a penalty for accessing this object every time.
I guess both solutions have a similar penalty.