I have an existing database with the table Transactions in it. I have added a new table called TransactionSequence where each transaction will ultimately have only one record. We are using the sequence table to count transactions for a given account. I have mapped this as a one-to-one mapping where TransactionSequence has a primary key of TransactionId.
The constraint is that there is an instead of trigger on the transaction table does not allow updates of cancelled or posted transactions.
So, when the sequence is calculated and the transaction is saved, NHibernate tries to send an update on the transaction like 'UPDATE Transaction SET TransactionId = ? WHERE TransactionId = ?'. But this fails because of the trigger. How can I configure my mapping so that NHibernate will not try to update the Transaction table when a new TransactionSequence table is inserted?
Transaction mapping:
<class name="Transaction" table="Transaction" dynamic-update="true" select-before-update="true">
<id name="Id" column="ID">
<generator class="native" />
</id>
<property name="TransactionTypeId" access="field.camelcase-underscore" />
<property name="TransactionStatusId" column="DebitDebitStatus" access="field.camelcase-underscore" />
<one-to-one name="Sequence" class="TransactionSequence" fetch="join"
lazy="false" constrained="false">
</one-to-one>
</class>
And the sequence mapping:
<class name="TransactionSequence" table="TransactionSequence" dynamic-update="true">
<id name="TransactionId" column="TransactionID" type="Int32">
<generator class="foreign">
<param name="property">Transaction</param>
</generator>
</id>
<version name="Version" column="Version" unsaved-value="-1" access="field.camelcase-underscore" />
<property name="SequenceNumber" not-null="true" />
<one-to-one name="Transaction"
class="Transaction"
constrained="true"
foreign-key="fk_Transaction_Sequence" />
</class>
Any help would be greatly appreciated...
One to one mapping in nhibernate doesn't work the way you think it does. It's designed so that you have two classes, which when persisted to their corresponding tables have the same primary keys.
However you can make it work, but it's not pretty. I'll show you how then offer up some alternatives:
In your Transaction hbml:
<one-to-one name="Sequence" class="TransactionSequence" property-ref="Transaction"/>
In your Sequence html:
<many-to-one name="Transaction" class="Transaction" column="fk_Transaction_Sequence" />
This should do what you want it to do. Note the property-ref.
The next question you're going to post on is going to ask how you get lazy loading on one-to-one associations. The answer is, you can't... well you can, but it probably won't work. The problem is that you have your foreign key on the sequence table, which means that nhibernate has to hit the database to see if the target exists. Then you can try playing around with constrained="true/false" to see if you can persuade it to lazily load the one-to-one association.
All in all, it's going to result in a total waste of your time.
I suggest either:
Have two many-to-one associations.
Have a many-to-one association with a collection on the other end.
This will save you a lot of headaches in the long run.
Turns out that for my situation a <join table> mapping worked best. I just had to make sure that I made the properties that came from the second table were nullable types, or it would do an insert on save even if nothing had changed. Since I did not need lazy loading for the second table, this works great. I am sure that I could have gotten paired many-to-one mappings to work, but it was not intuitive and seems more complicated than the join table option, however <join table> is only available in NHibernate 2.0 and up.
Related
I'm looking at upgrading a legacy application from NHibernate 4 to NHibernate 5 and one of the changes is that now columns cannot be mapped multiple times. However I have an existing mapping which does exactly that and I'm not sure how to resolve it; I need to map the same column for two separate many-to-one relationships from the same entity.
Here is the existing mapping (which obviously works fine with NH 4):
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="..." namespace="...">
<class name="..." table="...">
<id name="Id">
<generator class="identity" />
</id>
<!-- various other properties -->
<many-to-one name="Board" column="Board_Code" />
<many-to-one name="Option">
<column name="Option_Code" />
<column name="Board_Code" />
</many-to-one>
</class>
</hibernate-mapping>
The problem here being the reuse of the Board_Code column.
The column 'Board_Code' has already been added in this SQL builder
It seems to be possible to work around this as detailed in this answer and in this question when both a property and a many-to-one relationship need to be defined. The approach seems to be to just add insert="false" update="false" to the property. However given I'm dealing with two relationships rather than a relationship and a property, I'm not clear on if there's any similar solution (and if so, what the implications would be).
Appreciate any help/advice.
this might work
<many-to-one name="Board" column="Board_Code" update="false" insert="false"/>
I have two tables: "Libros" and "Autores". The relationship is many-to-many.
When I delete an entry from Libros (HibernateTemplate.delete(libro)) this operation delete the record on the intermediate table "Libros_autores" (which is right). But its delete a record on Autores table which is not right for me.
In the mapping file changed the cascade from "all" to "save-update" but it fails because there is an id on table libros_autores which is FK from Libros. So, if Libros record is deleted the row in libros_autores have to be deleted as well to avoid contraints problems (which is absolutly logic for me).
Then my problem is: If I delete a record on table Libros its delete on table autores as well and i only want to be deleted on tables: Libros and libros_autores.
Mapping files are:
Libro.hbm.xml
<class name="app.modelo.Libro" table="libros">
<id name="isbn" column="ISBN" type="string" length="20">
<generator class="assigned" />
</id>
<set name="autores" table="autores_libros" inverse="true" cascade="all,delete-orphan">
<key column="ISBN"/>
<many-to-many column="ID_AUTOR" class="app.modelo.Autor"/>
</set>
</class>
Autor.hbm.xml
<class name="app.modelo.Autor" table="autores">
<id name="id" column="ID_AUTOR" type="integer" length="20">
<generator class="native" />
</id>
<set name="libros" table="autores_libros" cascade="all">
<key column="ID_AUTOR"/>
<many-to-many column="ISBN" class="app.modelo.Libro"/>
</set>
</class>
I understand you should not user cascade="all". I think it means it will really exclude the autor as you exclude the libro. I understand the relationship will be implicity deleted anyway.
See this from the book "Hibernate in Action":
We’ve chosen cascade="save-update" for both ends of the collection; this isn’t unreasonable. On the other hand, cascade="all", cascade="delete", and cas-cade="all-delete-orphans" aren’t meaningful for many-to-many associations, since an instance with potentially many parents shouldn’t be deleted when just one parent is deleted.
I am currently using NHibernate to read a block of 50k rows. It takes almost 20 minutes.
Data which is to be read is prepared by joining more than 5 tables in SQLSERVER 2005.
My main concern is NHibernate prepares a new query for each row retrieval, which is quite time consuming.
I know it's necessary as NHibernate has to map each row to an object, but this approach is useless where reading is main concern.
I cannot use Stateless sesstion as I am using collections to store data
I also need to apply some filter through parameters. I have also implemented Stored Procedure, but somehow it didn't help.
Is there any other way to handle large collection of data.
------Edit-------
Here are my two config file which I was using to map
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="myNameSpace" assembly="myNameSpace">
<class name="MyClass" table="TableNameFromDb">
<id name="ID" column="ID">
<generator class="native" />
</id>
<property name="prop1"/>
<property name="prop2"/>
<bag name="bag1" cascade="all-delete-orphan" inverse="false" fetch="join" lazy="false">
<key column="prop1"/>
<one-to-many class="MyClass2" />
</bag>
</class>
</hibernate-mapping>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="myNameSpace" assembly="myNameSpace">
<class name="MyClass2" table="TableNameFromDb2">
<id name="prop2" column="ID">
<generator class="native" />
</id>
<property name="prop2"/>
<property name="prop3"/>
</class>
</hibernate-mapping>
Now the main concern here is NHibernate is creating a new query for each row and sending to the database which is an overhead.
First of all, I think you'll have to verify what's causing the performance issue.
As Felice Pollano already mentionned, it could be that you're facing a select n+1 issue. This can possibly be solved by modifying your mapping, or, by specifying the FetchMode that must be used in the Criteria that you're using to retrieve the data.
Another thing that you should contemplate: is it really necessary to retrieve all those rows in one go? What are you trying to do? Do you really need all the data that's represented by your entities, or can you use a projection which only loads those columns that you're interested in?
Can you use Paging?
Perhaps you can use a MultiCriteria...
However, in order to solve this issue, I think more input from you is needed.
The problem could be due to the reason; by default, NHibernate does not fully qualify table names within its queries sent to SQL Server. To leverage sp_execsql to its fullest, requests must include fully qualified table names.
To fully qualify NHibernate queries, in app/web.config file, add the following key in NHibernate settings:
<add value="my_DB_name.dbo" key="hibernate.default_schema" />
(replace dbo as necessary.)
Try it and tell us if it gives you any performance boost?
Problem
When executing a get for an entity with a many-to-one relationship with outer join set to true, not-found set to ignore and the entity row on the one side does not exist, an extra select is executed by NHibernate trying to load it even though the previous select which was just executed could not find it.
Question
Why is this extra select executed and is there any way to suppress it?
Context
I am working with a legacy database with no foreign key constraints where I have zero control over the schema. Modifying it is unfortunately out of the question. Keep in mind my actual mappings are more complex than this. This occurs several times for the actual entity and they are often loaded by ICriteria.List<T>() and not Session.Load<T>(id), resulting in alot of unnecessary queries and a big performance hit.
Simplified Example
Code
ISession Session = ...
...
Session.Get<Bid>(1);
...
Executed SQL
SELECT bid.Id, bid.ItemId, item.Id FROM Bid bid left outer join Item item on bid.ItemId=item.Id WHERE bid.Id=#p0;#p0 = 1
SELECT item.Id FROM Item item WHERE item.Id=#p0;#p0 = 222
Mapping
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Example" namespace="Example">
<class name="Bid">
<id name="Id">
<generator class="native"/>
</id>
<many-to-one name="Item" column="ItemId" outer-join="true" not-found="ignore" />
</class>
<class name="Item">
<id name="Id">
<generator class="native"/>
</id>
</class>
</hibernate-mapping>
Bid Table Contents
Id ItemId
1 222
Item Table Contents
Id
333
444
OK, looks like it's definitely a bug in NHibernate, as you can see here. At the moment, it's marked as minor, so I guess voting it up might raise it's profile and get a fix going.
Without setting this up, I would guess that the key is to replace the outer-join="true" with fetch="join" in the many-to-one association. By default the fetch mode of a many-to-one association is "select", and I suspect that this is causing the select to be executed.
If you modify that mapping to be:
<many-to-one name="Item" column="ItemId" fetch="join" not-found="ignore" />
it should behave more like you expect it to.
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