I am using NHibernate 2.1.0.4000 in one of the projects. I have set adonet.batch_size to 100 in the cfg file however I still see that insert statement is treated as single statement. Update seems to work fine. What's going on?
Updated:
Is it because I've chosen identity as the primary key generator?
<id name="Id" column="Id" unsaved-value="0" type="Int32">
<generator class ="identity"></generator>
</id>
I don't know of any issues with that particular NHibernate version.
Are your using native as the ID generator for your entities? Because this will force every insert to happen alone, selecting back the generated ID. This is because the database needs to generate every ID. This would also explain why batching seems to work on updates.
If possible, you should switch to e.g. the hilo strategy, or even guid if you don't care about (easily) readable IDs.
Fabio has han interesting post here regarding this topic.
Related
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.
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'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
Is there an easy way to make NH INSERT or UPDATE an entity depending on whether there is already an entity with same <natural-id />?
The entity is mapped to another (root) one using <many-to-one cascade="save-update" />.
Not automatically. You will have to read from the database to see if a record for that natural-id already exists and then determine if you need to do an insert or update. Which of course means that the cascade won't work.
I was looking for something similar in How do I Insert or Update (or overwrite) a record using NHibernate?
I'm writing an application and started to test my domain model entities. If I Create an instance of entity Company like this var company = new Company("my company"); I should get a valid entity, meaning the company should at this moment have an Id correct?
So the problem is that at the moment I have the Id generation made in the DB defined in an hbm file like this:
<id name="ObjectIdentity" column="CompanyId" type="System.Guid" unsaved-value="00000000-0000-0000-0000-000000000000">
<generator class="guid.comb"/>
</id>
This causes a problem when writing unittests as I do not have en entity with an Id as it dosen't touch the db in the test, aka I have an invalid entity.
Now should I assign Id in the application and not let nhibernate be in charge of this or is this the wrong way to do it?
In most cases you should be letting NHibernate do its job which is to handle persistence. This is important since it allows you to easily change things (we went from identity to hilo mid-project).
I would question why you care that a newly created object has an id or not? From a business point of view a persistence ID is not relevant and shouldn't be checked with via unit tests. This as mentioned is the domain of integration tests. You should be careful in how you are using an objects persistence Id throughout the rest of your application. Remember this should NOT be treated as the objects business id/key.
You need to call Session.Save on the entity before a guid is generated. You can call Session.Save to generate the entity without actually saving it to the database. This article does a pretty decent job of explaining it
Neither NHibernate nor the Application should be handling identifiers. Leave it to the database since this is the only concrete store of your data, and it is the only part of your application that knows what IDs have already been assigned and what ones are available.
Make an identity primary key column on your database table:
CREATE TABLE dbo.sample (
id int primary key identity(1,1),
...
...
...)
map your entities like this:
<id name="ID" column="id">
<generator class="identity" />
</id>
The Primary key will be generated automatically by the database when you save a new entity for the first time. IDENTITY(1,1) means "give new rows an Identity start at '1' then each subsequent row gets incremented by 1": so 1,2,3,4,5,6,7
You need to save the entity to get an id if you're using db generated identity,
if you wish to create your own identities - it is fine.
do what suits you, just remember what you have chosen when you test.
I usually decide on each class which is more suitable for me - either DB generated or my own.
Nhibernate should generate Id's for you. Id property of the entity must be protected. The best way is to use hilo