I have two C# classes, say Container and Item, and a unidirectional association:
class Container {
[...]
public IList<Item> Items {get;set;}
}
The NHibernate mapping looks like this:
<class name="Container">
[...]
<list name="Items" cascade="all-delete-orphan">
<key column="ContainerId"/>
<index column="Position"/>
<one-to-many class="Item"/>
</list>
</class>
As a result, NHibernate (2.0.1) generates a ContainerId column in the Item table. Since I usually navigate this connection from the Container side, I want to place an SQL index on the ContainerId column. NHibernate doesn't seem to provide a mapping syntax for this, or at least it's not obvious to me. What is the best way to accelerate this?
I'd like to avoid having to spread this over multiple places, so I'd prefer changing the mapping document only. Is there a way to do this in NHibernate syntax? Should I embed custom SQL commands? If so, how?
Have a look at the <database-object> mapping; it will allow you to create arbitrary indexes, triggers, etc. as part of your schema creation process. Usage is described in this article.
i don't know if and when this changed but it can be done using
<index>
<column name="Position" index="Positionindex" />
</index>
Related
I have an entity that is mapped with a <join> element like this:
<class name="Entity" table="Entites">
<id name="Id">
<generator class="guid" />
</id>
<property name="SomeProperty" />
<join table="EntityData" optional="true">
<key column="entity_id" />
<property name="SomeDataProperty />
</join>
</class>
and a native SQL query that should query this entity like this (the actual query is way more complex of course and uses Oracle features that are not available in other types of NHibernate queries):
var query = session.CreateSQLQuery(
"select {entity.*} from Entities {entity}" +
"left outer join EntityData data on {entity}.Id = data.entity_id"
);
query.AddEntity("entity", typeof(Entity));
But this doesn't work because NHibernate injects the join-properties with a wrong alias ("entity_1_").
I've also tried specifying the table alias as "{data}" but then NHibernate doesn't replace it (still contains the curly braces in the plain SQL).
Adding a join result to the query (query.AddJoin("data", "entity.EntityData")) doesn't work too. I've debugged this and NHibernate ignores it because at some point it checks if the property is a collection or entity (but it is a component, so neither of the if-conditions is true).
A workaround is to specify the join alias exaclty like NHibernate generates it (in my case "entity_1_"), then everything works.
Has anyone an idea how this can be solved correctly?
edit: I'm now having the same problem when querying an entity that is mapped with inheritance mapping. I can define an alias for the base entity, but the inherited entities are named entity_1_, entity_2_ and so on.
Seems like this is not possible, so I've now created a feature request for it: NH-2822
For filtering purpose, I'd like to propose in nhibernate both the mapped property and the id.
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="BusinessObjets.ItemShopping,BusinessObjets" table="ADN_Monture" lazy="true">
<many-to-one name="Manufacturer" column="IDManufacturer" cascade="save-update" not-null="true" />
<property name="IDManufacturer" column="IDManufacturer" type="int" />
</class>
</hibernate-mapping>
In this case, it would propose either a Manufacturer property of the Manufacturer Type and a IDManufacturer (int).
The int would be a readonly property and would only be used to filter the data.
For example :
var result = from item in session.Query<ItemShopping>() select item).ToList<ItemShopping();
and then filter the result with linq to objects with an id.
Is there any drawback in the nhibernate process (insert / update) ?
Regards
Edit
Well after installing nhibernate profiler, I notice that a filter based only on the foreign key (item.Manufacturer.IdManufacturer) doesn't use the proxy. So there is no performance problem.
Can someone confirm ?
You can experience problems in saving/updating the entity. In this case just put update="false" on the additional Id property, and it should work like a charm. No problem if you are just querying. BTW you should not have performance issues even if you query for the many to one. NH known to issue a query based on the Id and does not eagerly fetch anything just for issuing the query.
What would be your approach with NHibernate to do the following:
When Comment is inserted into DB, and its property Text is inserted into column Text (due to a mapping), insert into column TextHash a hash of this property value.
It seems trivial if I map TextHash, but how should I do this without mapping it?
I do not need it in the domain model, its a DB-level optimization (I would use computed HashBytes if not for the length limit).
There are some similar questions, such as this one:
Unmapped Columns in NHibernate?
However, IInterceptor seems like an overkill for a change in a single entity insert. And EventListeners are less than perfectly documented and also somewhat too complex for a single column.
So I have decided on the solution that I see both as most reusable and most local:
<property name="Text" type="StringWithMD5HashUserType">
<column name="Text" length="20000" not-null="true" />
<column name="TextHash" length="32" not-null="true" />
</property>
Where StringWithMD5HashUserType is ICompositeUserType that reads Text from first column, but writes both Text and its Hash (I do not add the code of StringWithMD5HashUserType because it is way too long, but essentially very simple).
Well, if you don't want to use the property, declare it private and calculate it when you set the text property. It's not exactly "without mapping it", but you won't see it when you're using the class (disclaimer: advice based on Hibernate, as opposed to NHibernate).
Other than that, you could always write a trigger, although I'd personally much rather add a private field and be done with it.
Here's a suggestion:
As far as I know you can't access a column in your DB using NHibernate if it there no mapping defined for it.
To prevent other parts of your application to see this field of your class you can define its access as field in your mapping so that it can be private an no one knows that it exists but NHibernate:
In your class:
private string _textHash
In your mapping:
<property name='_textHash' column='TextHash' access='field' />
I am trying to get a collection of objects into a parent object through mapping.
I have a parent object "ScoreCard" whose primary key is a guid (Id) and a child "Score" object whose primary key is a guid (Id). I want to select the child objects for the parent based on two fields that both objects have but I can't get it to work, here's the mapping
<bag name="ScoreCard">
<key>
<column name="HoleId"/>
<column name="PlayerId"/>
</key>
<one-to-many class="Score" not-found="ignore"/>
</bag>
I didn't design the database but the ScoreCard object comes from a view that returns both column I need plus the evil guid. Whatever I've tried, NHibernate keeps throwing an exception about the foreign key not being the same as the primary key.
This seems to me to be the most simple requirement, get a collection of things given some criteria, why am I so stuck?
Thanks for your help, sorry for the bad example code (subliminal golf watching at relatives house).
Well, I found it eventually. The parent object is drawn from a view giving three columns and no key. I can map a composite key to the HoleId and PlayerId instead of the evil guid that I found when I looked at the code. This is great as I can easily map the Score objects I need and then lazy load them using NHibernateUtil.Initialize.
My mapping xml needs to look like this
<class name="ParentObject">
<composite-id>
<key-property name="HoleId" column="HoleId" />
<key-property name="PlayerId" column="PlayerId" />
</composite-id>
<property name="EvilGuid" column="Id" />
<bag name="ScoreCard">
<key>
<column name="HoleId"/>
<column name="PlayerId"/>
</key>
<one-to-many class="Score" not-found="ignore"/>
</bag>
</class>
I got my inspiration from this post, please also pay attention to Stefan's answer as I feel I had a lucky break here, and the design could be made better with more thought about DDD.
Thanks for your help.
The problem is this: NHibernate works best (but not only) for DDD, this means for creating domain classes first and make the database best fitting the domain model.
You have a composite-id relation to non-primary-key fields. So start praying that NHibernate can cope with that. Both composite-ids and relations by non-primary-keys are supported - for legacy databases - and generally discouraged for DDD.
I think the combination of both does not work. See this issue on NHibernates issue tracker:
https://nhibernate.jira.com/browse/NH-1722. You can vote for the feature there.
I have a NewsFeed object mapped as such:
<class name="NewsFeed">
<id name="NewsFeedId">
<generator class="guid"/>
</id>
<property name="FeedName" not-null="true" />
<property name="FeedURL" not-null="true" />
<property name="FeedIsPublished" not-null="true" />
</class>
And Users who can have a Set of Selected feeds that they might be intereseted in, mapped like so:
<class name="SystemUser">
<id name="SystemUserId">
<generator class="guid"/>
</id>
<set name="SelectedNewsFeeds" table="SystemUserSelectedNewsFeeds" cascade="all">
<key column="SystemUserId" />
<many-to-many column="NewsFeedId" class="NewsFeeds.NewsFeed, Domain"/>
</set>
</class>
What I want to happen is when I delete the parent NewsFeed then all of the SelectedNewsFeed references get deleted too, without having to load each SystemUser and delete the NewsFeed by hand.
What is the best way to achieve this?
UPDATE: Using cascade="all-delete-orphan" instead of "all" still results in an exception when deleting the NewsFeed:
The DELETE statement conflicted with the REFERENCE constraint "FKC8B9DF81601F04F4". The conflict occurred in database "System", table "dbo.SystemUserSelectedNewsFeeds", column 'NewsFeedId'.
JMCD
Your second approach:
Another alternative is to break the
many-to-many relationship with a join
class in the middle which nHiberate
would be able to determine
parent-child relationships and the
cascade should work.
is actually what the nHibernate folks recommend in their documentation.
Don't use exotic association mappings.
Good usecases for a real many-to-many
associations are rare. Most of the
time you need additional information
stored in the "link table". In this
case, it is much better to use two
one-to-many associations to an
intermediate link class. In fact, we
think that most associations are
one-to-many and many-to-one, you
should be careful when using any other
association style and ask yourself if
it is really neccessary.
Using two one-to-many associations adds the flexibility to easily add other attributes to the "subscription", such as notification preferences for that particular subscription.
Since the relation inside the set is many-to-many, nHibernate is not able to tell which end of the relationship is the child and which is the parent, and the quickest way for me to achieve what I wanted was just to write some SQL that I sent through my repository that deleted the respective news feeds from the collection, and then deleted the parent news feed. The next time the collection was hydrated the changes were reflected.
Another alternative is to break the many-to-many relationship with a join class in the middle which nHiberate would be able to determine parent-child relationships and the cascade should work.
change
cascade="all"
to
cascade="all-delete-orphan"
Reference