I am pretty new to NHibernate and I'm trying to create a mapping file to extend a data model project. The particular table I am mapping is called AttributeDef in the following image, the column ControlType actually relates to a lookup in the table called Code (yes, I know - there should be an FK constraint but this sort of thing is quite common in this project so please ignore the obvious howlers and focus on the question). In most cases tables which reference Code also have a column which contains the ID from the table CodeSet as the key in Code is, almost inevitably, a composite key, but not in this case presumably because the original author figured "Hey they're all from the same codeset so what's the point?".
Now if there was a column in AttributeDef which contained the CodeSet value then the mapping wouldn't be much of a problem. The mapping for the Code entity looks like this:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Activus.DataModel" namespace="Activus.DataModel">
<class name="Code" table="Code" mutable="false">
<composite-id name="CompositeCodeId" class="CompositeCodeId">
<key-property name="CodeId" column="CodeId"/>
<key-property name="CodeSet" column="CodeSet"/>
</composite-id>
<property name="Description" column="Description" type="string" length="100" not-null="true"/>
<property name="ExternalRef" column ="ExternalRef" type ="string" length ="256" not-null ="true"/>
<property name="InternalRef" column ="InternalRef" type ="string" not-null ="false"/>
<many-to-one name="CodeSet" class="CodeSet" column="CodeSet" not-null="true" insert="false" update="false"/>
</class>
</hibernate-mapping>
Therefore if there was a column in AttributeDef for the CodeSet value (notionally called FormControlCodeSet in this example) then in my AttributeDef mapping file I would include
<many-to-one name="ControlType" class="Code" not-null="false">
<column name="ControlType" />
<column name="FormControlCodeSet" />
</many-to-one>
And all should be well. The problem is that to add that column to AttributeDef would be very invasive as I would then have to make a LOT of other changes to accommodate this and that would increase the risk factor of the change I'm making to a point which might well be unacceptable (from the client's point of view given their time frame).
So, much as it's a horrible, horrible thing to contemplate, Is it possible to substitute the line
<column name="FormControlCodeSet" />
With a (whisper it) hard coded value? That value hasn't changed in a decade and isn't likely to anytime soon but it would get us past this change and would highlight the need to scope out and implement the inclusion of the extra column. I recognise how dreadful this is but unfortunately a lot of this database isn't really that well suited to ORM despite it's being shoe-horned in anyway.
You do not have to whisper requirements, when working with NHiberante. Because cooperation with legacy DB (i.e. fixed DB schema) is pretty standard, NHibernate does support many different settings.
One of these is a pair of (substitutional) settings: "column" vs "formula". The first takes the column as is, the second could do whatever we need. Take column, columns, pass a constant. So:
<many-to-one name="ControlType" class="Code" not-null="false">
<column name="ControlType" />
<!-- let's replace this -->
<!--<column name="FormControlCodeSet" />-->
<!-- 1) with a constant number 123-->
<formula>123</formula>
<!-- 2) with a constant string 'constantString' -->
<formula>'constantString'</formula>
</many-to-one>
Not sure if the FormControlCodeSet constant should be int or string, but as shown above, either option is possible.
Related
I'm trying to create a DB Table, using an NHibernate *hbm.xml mapping file, that will have a Versioning Column for concurrency check. The Versioning column should be a nullable Integer.
Although the Database is created just fine, using the mapping file as reference, the following happen:
* The first record is inserted with a NULL value as the Version
* The update of the previously inserted records fails with a "Stale Data" exception
In other words, no matter what I do, the Version column is always NULL.
I'm somewhat new to the Concurrency Control using NHibernate, so I don't quite understand what I'm doing wrong..
If I use a Timestamp as a Version, everything works just fine. However, my requirement is to use an Integer.. Hence my problem.
This is my Mapping File:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="true" auto-import="false" assembly="New1.Backend" namespace="New1.BO">
<class name="Natrio" table="`Natrios`" schema="`dbo`">
<cache usage="read-write" />
<id name="Id" column="`Id`" type="System.Int32">
<generator class="NHibernate.Id.Enhanced.TableGenerator">
<param name="increment_size">200</param>
<param name="segment_value">Natrios</param>
<param name="optimizer">pooled-lo</param>
</generator>
</id>
<version name="Version" column="`Version`" type="System.Nullable`1[[System.Int32, mscorlib]], mscorlib" generated="always" unsaved-value="0">
<column name="`Version`" not-null="false" sql-type="int" />
</version>
<property name="Attribute" column="`Attribute`" type="String" not-null="false" length="100" />
</class>
</hibernate-mapping>
Any thoughts and/or suggestions would be greatly appreciated!
Why do you need nullable version column? In any case I believe the issue is caused by unsaved-value="0" in your mapping. As default value for nullable column is null - NHibernate thinks that value is already generated and so it's never assigned. You need to set it to null - unsaved-value="null" to make it work with nullable columns. And unsaved-value="0" makes sense for not-nullable types. But better omit this attribute completely and let NHibernate to s
Another issue with generated attribute. It's about DB generation - so always means that this value is generated automatically by DB. You should remove it or specify it as generated="never".
I believe the following mapping should work for you:
<version name="Version">
<column name="`Version`" not-null="false" sql-type="int" />
</version>
Still a n00b in NHibernate :(
I want to add a composite index to the hbm.xml of one of my POCOs, for performance purposes. It does not relate directly to a class, but rather two common values I will be querying against.
I think I need to do a <map></map> entry in the XML, but the XSD is asking for a class name on the composite-key element, and there is no direct relationship, per se... plus it's asking for more information than I think I would need to provide. How do I do this?
<map name="PropertyKeys">
<key>
<column name="StockID" />
<column name="PropertyName" />
</key>
<composite-index class="Something?">
<key-property name="What goes here?" />
</composite-index>
<what else goes here?>
</map>
Have you read this http://ayende.com/blog/4045/nhibernate-mapping-map It explains when you would use a map and further on it explains the composite-index
However without more background info it is not 100% certain that you need a map!
I have been successfully using NHibernate for quite some time now and
have been able to solve a lot of pitfalls with an application that I
developed with it and that is running in production. The recent hurdle
really has me scratching my head, though.
Recently I had to expand the class library with some new classes that
are nested as children to some already existing classes. I just copied
the same model for aggregate mapping that I already was successfully
using, but this time it does not work.
Now when I use the following in the parent mapping file:
<bag name="SeaInfoItems" table="EDIImport_SeaInfo" lazy="false" cascade="save-update">
<key column="EDI_FK_OWNERID"/>
<one-to-many class="FargoGate.AppLib.EdiImportSeaInfo, FargoGate.AppLib"/>
</bag>
I can choose to, in the child class, either use:
<property name="EDI_FK_OWNERID" column="EDI_FK_OWNERID" />
...which gives me the infamous "Invalid Index n for this
SqlParameterCollection with Count=n" error.
OR I try with this solution I found after some Googling:
<property name="EDI_FK_OWNERID" column="EDI_FK_OWNERID" insert="false" update="false" />
...which gives me a "Cannot insert the value NULL into column
'EDI_FK_OWNERID'... column does not allow nulls." error.
So basically I have to choose between pest and cholera.
What I don't get is that it works flawlessly for the already existing
aggregate classes, and I really cannot spot the difference. The only
thing is that this foreign key (EDI_FK_OWNERID) could refer to two
different parent tables. Bad database design, I know, but I didn't
design it, and it is my task to develop up to it for better or worse. I cannot change the database design.
The other difference is that I totally removed the foreign key reference from the already existing child classes (the mappings as well as the class members). I tried to emulate that of course, but of no avail.
Also I discovered that one of the new classes (which is quite small) also works fine. But I cannot see what the difference is here either. I am stumped!
Anyone has a clue?
Aaargh! I was put so much on a wrong leg with this infamous “Invalid Index n for this SqlParameterCollection with Count=n” error that I overlooked the obvious: A duplicate mapping of a field for ONE of the classes.
In that particular mapping I left this error, where the primary key is also defined as a property:
<id name="ID" column="ID">
<generator class="guid" />
</id>
<property name="ID" column="ID" />
Now that was a waste of time trying to debug that!
I am having a problem when trying create a many to many mapping. Consider the following tables:
CREATE TABLE [dbo].[student]
(
[Id] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR(255) NOT NULL,
-- Some other stuff...
)
CREATE TABLE [dbo].[Subject]
(
[Id] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (50) NOT NULL,
-- Some other stuff...
)
CREATE TABLE [dbo].[studentToSubject]
(
[studentId] INT NOT NULL,
[subjectId] INT NOT NULL,
)
The interesting part of my student mapping file looks like this:
<id name="Id" type="Int32">
<column name="Id" sql-type="int" not-null="true" unique="true"/>
<generator class="native" />
</id>
<property name="Name" not-null="true" />
<bag name="subjects" table="studentToSubject">
<key column="studentId"></key>
<many-to-many column="subjectId" class="subject" />
</bag>
I want to end up with a student with a collection of their subjects. However, I get an error:
NHibernate.MappingException: Could not determine type for: MyApp.Domain.Subject, MyApp.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=865c2d2b185d0c4b, for columns: NHibernate.Mapping.Column(studentId).
I have seen some examples of this type of mapping, but they differ in the fact that their Id columns have name that match the mapping table name, for example their Id column in the student table is called 'studentId'. I cannot do this (it has to be Id) but I think this is the cause of the problem.
Thanks
you should write this relation for subject class again and sure your field for relation is correct.
i do it with attribute models
It seems like you are either missing the mapping file for Subject (did you remember to include it properly?) or you need to provide the full path if it is in a different namespace.
Please correct me if I'm wrong, but I guess there's something messy about your Id mapping. Perhaps it can be done the way you do and I have never seen it, this is possible.
Though I would write my mapping like so:
<class name="Student"> <!-- I omit the table attribute as both your class and table seems to have the same name. -->
<id name="Id">
<generator class="native"/> <!-- Though I would recommend using "identity" if SQL Server's used. -->
</id>
<property name="Name" length="255" not-null="true"/>
<list name="Subjects" not-null="true" table="StudentToSubject">
<key column="studentId" />
<many-to-many column="studentId" class="Subject" />
</list>
</class>
Within the element, it is optional to specify the not-null, unique, type and sql-type attributes as NHibernate will determine them during runtime using reflection, though I understand that for pedagogy purposes, it is better to write those. Plus, if you want your Id property name within your object class be the same as your table field, you may just omit the column attribute. NH will then consider using the same name as the property for the data table field Id field.
As for your collection of subjects, if you intend to use a Dictionary in your Sudent class, you'd better the element instead of . However, if you want a List, you'd better opt for the element as I did. This all depends on your needs and your objective through this exercise.
Please, consider that I took this NH XML mapping right from the top of my head, and I didn't test it, so it might contain errors on fly.
In addition to this, you can take an eye out on this: Chapter 6. Collection Mapping
Hope this helps! =)
Did you remember to set the .hbm.xml mapping files to an Embedded Resource?
Also this line is not correct.
<many-to-many column="subjectId" class="subject" />
Subject should be capital S and it is good practice to give the namespace and assembly. such as
<many-to-many column="subjectId" class="MyApp.Domain.Subject, MyApp.Domain" />
I might be mistaken but the error hints at it:
NHibernate.MappingException: Could not determine type for: MyApp.Domain.Subject, MyApp.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=865c2d2b185d0c4b, for columns: NHibernate.Mapping.Column(studentId).
Basically the way i read the error, NHibernate can't figure out what type the column studentId is, The fault is most likely in your Subject mapping. There is a property there which apperently references a student (i'm guesing the other side of the many-to-many).
I'm doing some per table inheritance and all is working great- but I'm noticing that when I want the base entity (base table data) NHProf is showing a left outter join on the child entity / (related table)
How can I set the default behavior to only query the needed data - for example: When I want a list of parent elements (and only that data) the query only returns me that element.
right now my mapping is similar to the below:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="FormBase, ClassLibrary1" table="tbl_FormBase">
<id name="BaseID" column="ID" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<property name="ImportDate" column="ImportDate" type="datetime" not-null="false" />
<joined-subclass table="tbl_Form" name="Form, ClassLibrary1">
<key column="ID"/>
<property name="gendate" column="gendate" type="string" not-null="false" />
</joined-subclass>
</class>
</hibernate-mapping>
And the example where I want all the data back vs ONLY the parent entity is shown below:
Dim r As New FormRepository()
Dim forms As List(Of Form) = r.GetFormCollection().ToList()
Dim fbr As New FormBaseRepository()
Dim fb As List(Of FormBase) = fbr.GetFormBaseCollection().ToList()
You can't. It's called "implicit polymorphism" and it's a rather nice (albeit unwanted in your case :-) ) feature provided by Hibernate. When you query a list of base objects, the actual instances returned are of the actual concrete implementations. Hence the left join is needed for Hibernate to find out whether particular entity is a FormBase or a Form.
Update (too big to fit in comment):
The general issue here is that if you were to trick Hibernate into loading only the base entity you may end up with inconsistent session state. Consider the following:
Form instance (that is persisted to both form_base and form tables) was somehow loaded as FormBase.
You've deleted it.
During flush Hibernate (which thinks we're dealing with FormBase and thus is blissfully unaware that there are 2 tables involved) issues a DELETE FROM form statement which throws an exception as FK is violated.
Implicit polymorphism exists to prevent that from happening - Form is always a Form, never a FormBase. You could, of course, use "table-per-hierarchy" mapping where everything is in the same table and thus no joins are needed but you'll end up with (potentially) a lot of NULL columns and - ergo - inability to specify not-null on children's properties.
All that said, if this is REALLY a huge performance issue for you (which it normally shouldn't be - presumably it's an indexed join), you could try using a native query to just return FormBase instances.