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' />
Related
I am developping a ASP.NET with VB using NHIBERNATE to map the tables of a pre-existing database (SQL Server 2005). I have a many-to-many relationship between to entities, that I map like this:
<list name="PropName" table="TableHoldingRelation" lazy="false" >
<key column="idEntity1"></key>
<index column ="orderingColumn" ></index>
<many-to-many class="Entity2" column="idEntity2"></many-to-many>
</list>
The mapping works perfectly and the list(of Entity2) its ordered by the selected column.
The problem is that this column is not continuous, as there might be some values missing (ie: 0,1,3,8). NHibernate is leaving those spaces as null/nothing elements. I would want to have the list "compacted", only containing existing elements, ordered by that column.
Can I achieve this without having to update the database? (updating is not a good solution as it probably will happen in the future that some elements get removed)
Thanks in advance for your help.
EDIT: A bit more info in the problem.
The tables/entities in this case refer to Menus and MenuItems. The application that I am working is is a very complex website, with lots of diferent roles. Each role has his unique menus congfiguration, with their unique items. There are even single users with unique settings. My task is to rewrite the .NET clases and mappings, as they are really messy and other things not relevant for this question. So the database design I am mapping is (for this question, obviously there are other tables):
One table holding menus and their attributes(like wich role do they correspond to)
One table holding menuitems and their attributes (like a link they point to)
One table holding de relation menu-menuitem and a "position"/order column inside that menu.
Just in case more insight on the problem was needed.
As you can read here NHibernate Mapping - <list/> - by Ayende, this behaviour is by design. An extract from comments (close to your question):
...Because in general, having NH doing something like that for you can be
bad. There is a meaning to null values.
But broadly, it is because it is not the responsibility of NHibernate
to do so. If you want something like that, you don't need a list, you
need an ordered set...
With this we can try to change your mapping (see the order-by attribute):
<bag name="PropName" table="TableHoldingRelation"
order-by="orderingColumn"
lazy="false" >
<key column="idEntity1"></key>
<many-to-many class="Entity2" column="idEntity2"></many-to-many>
</bag>
But, this mapping won't allow you to insert into that column orderingColumn.
This documentation 24. Best Practices says:
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.
So, maybe introduce the man-in-the middle pairing object, put the management of the OrderBy property there, and use the sorted list..
In my application I have many articles, each of which belongs to one or more categories. On my homepage I would like to list all categories with the total number of articles it contains underneath which I would like to show the top X article descriptions (based on when the article was posted). It would looks something like the following
Home Decorating (105)
- How to fix Windows
- How to fix Curtains
- How to fix Doors
- How to fix Counter tops
- How to fix sofas
My mappings are pretty simple and can be traversed either from the article or the category - both of which have nhibernate mapping files.
I can do this pretty easily from a stored proc, but for learning purposes would like to accomplish this via NHibernate and was wondering if there is an efficient way to do this or if i'm always going to end up with multiple queries.
Does anyone know how this could be accomplished via NHibernate?
Update
Here are the mappings:
<class name="MyProj.News.Category, MyProj.News">
<id name="Id" column="ID"/>
<bag name="Articles" table="Category_Article" lazy="true">
<key column="CATEGORY_ID"/>
<many-to-many class="MyProj.News.Article, MyProj.News" column="Article_ID"/>
</bag>
</class>
<class name="MyProj.News.Article, MyProj.News">
<id name="id" column="ID"/>
<!-- inverse end -->
<bag name="categories" table="Category_Article" inverse="true" lazy="true">
<key column="ARTICLE_ID"/>
<many-to-many class="MyProj.News.Category, MyProj.News" column="CATEGORY_ID"/>
</bag>
</class>
Yes, it's possible, but with some SQL magic. I'll not give a complete solution here, but hopefully you'll figure this out.
For count, it's best to add new Count property to your Category entity and map it using formula with subquery "select count(*) from Items ...".
For top 5 items, you'll either need to modify your Category.Items collection mapping or create another one, i.e. Category.TopItems. Then add where mapping to filter your result to top 5 rows (for SQL Server you can use something like "row_number() over(order by DateAdded) < 5". There are equivalents for other databases for sure, too.
Perhaps you could consider an alternate approach; rather than trying to create your entire resultset in one query - how about sending multiple queries in a single round-trip?
NHibernate has support for this in the form of multi-queries, multi-criteria, and futures. You could then assemble the results of these smaller queries into a form that makes sense for display.
I should note that hql has recently had support for skip and take constructs added, which you could likely use to construct your top(x) in each category query.
Even with straight SQL, it's not easily done in an efficient manner. To solve this problem in the proper manner, you should be caching the count of articles on the category entity. Any time you can eliminate unnecessary queries to the database, you should.
Doing this is simple enough. When a new article is added, or one is removed, trigger an update for the total count on the category.
This solution is all around the best approach because now the total count of articles exists on the category entity. You can then simply make one call to get the list of top n articles for the category.
I'm using schemaExport to create an in-memory database for my automatic tests. I have several (5) classes mapping their HiLo identity to the same database table, using one column per class.
This gives me a table, hibernate_unique_key, w. 5 columns. When generating the database using scemaexport, however, the table only gets a single column (IPoolActivation), thereby making my querys fail, since the mappings in the model are now invalid. I've tried manually querying the in memory database to create the table, but I would rather know how to make schemaexport do it right.
A snippet from one of my mapping files;
<id name="Id" column="Id" type="Int32">
<generator class="hilo">
<param name="column">IENPool</param>
</generator>
</id>
What is the proper way to do this?
I believe there is a bug (or it is by design) in SchemaExport, and it only looks at the hibernate_unique_key table once when it sees the first entity with HiLo. As a result all HiLo entities must use the same column.
However, I recently needed a custom IdGenerator which I based off the same base class in NHibernate that HiLo inherits from. For this database I needed one column per table (it was for a legacy database that was setup with a HiLo style generator that way).
You can see what I did here if it helps:
Implementing a custom id Generator for nHibernate
Generate custom DDL for a custom id Generator
Entire post on nhibernate.info
I have a User object/mapping in my application. Each user has a list of contact information (phone, email etc)
the mapping for the user contains:
<bag name="ContactInfo" table="contact_info" lazy="true" cascade="all">
<key column="contact_id"/>
<one-to-many class="...ContactInfo, ..."/>
</bag>
this works fine but i get the n+1 select problem so i need to optimize it a little bit. But for some reason, when I change this to a join and perform some db operation, NH starts updating ALL contact_info objects in the database. When i say db operation i dont mean changinf a contact. i mean anything.
Anyone knows why? thx
EDIT: Just realized that it does it for lazy="true" as well but the second time, after the objects have been loaded. the question of why remains
I'm wondering if your cascades are causing the issue. Do you have cascade=all on your entire graph? If so you may want to re-evaluate your lifecycle strategy.
Here's a though from section 9.9 of NHibernate 1.2 reference (emphasis added)
Mapping an association (many-to-one,
or collection) with cascade="all"
marks the association as a parent/
child style relationship where
save/update/deletion of the parent
results in save/update/deletion of the
child(ren). Futhermore, a mere
reference to a child from a persistent
parent will result in save / update of
the child.
it turns out that an enum field in ContactInfo was the problem. i didnt mind if that particular filed was a string so changing it resolved this issue.
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>