I have db tables in each I have same primary column name ('ID')
I get error from nhibernate:
System.IndexOutOfRangeException : An SqlCeParameter with
ParameterIndex '5' is not contained by this SqlCeParameterCollection
When I change these columns names to unique names, all is ok.
But i wonder how to fix it without name changes. I simply want same name in each table.
<class name="AppSignature" table="app_signatures" lazy="true">
<id name="Id"><generator class="guid"></generator></id>
</class>
<class name="AppState" table="app_states" lazy="true">
<id name="Id"><generator class="guid"></generator></id>
<many-to-one name="app_signature"
class="AppSignature"
column="Id"
foreign-key="id_fom_app_signature"
not-null="true"
>
</many-to-one>
</class>
Many-to-one relation means that an AppState instance can be assigned at most to one AppSignature instance. To an AppSignature can be assigned any number of AppState instances. This relation is implemented as an foreign key from app_states table to app_signatures table. "Column" attribute in "many-to-one" element determines column name that stores a value for the foreign key. To the Id column are mapped two AppState members: Id and app_signature which is not possible in NH and leads to the described exception.
The fix is easy:
<many-to-one name="app_signature"
class="AppSignature"
column="app_signature_id"
foreign-key="id_fom_app_signature"
not-null="true"
>
</many-to-one>
Name of column that implements foreign key was changed to an unique name: "app_signature_id". Now app_signature member is mapped to AppState.app_signature_id column which points to the app_signatures.Id column
However described exception should disappear (if the new column is properly added to app_signatures table) it would not necessarily be what you you really want. The relation between AppState and AppSignature could be one-to-one. It means an AppState instnace can be assigned to a single AppSignature instnance and vice versa. Such relation could be implemented by a primary and foreign key on the same column. There is a very nice article about one-to-one relations.
Related
Following on from NHibernate one-to-one vs 2 many-to-one
Is there an easy way to maintain multiple one-to-many relationships which are being used as a pseudo one-to-one.
E.g.
If I have 2 entities, User and Contact, which are related by a FK on each (User.ContactId and Contact.UserID).
What is the best way to maintain that each reference points at the other. It would be wrong for the system to update User with a different contact, but the Contact still references User...
Most likely you don't need to maintain this at all if you remove one of redundant foreign keys. Your database schema should not allow anomalies like that (userX references contactX but contactX references userY). Conceptually you have one-to-one relationship between user and contact. Why not have one-to-one in NHibernate mappings? If this is because of lazy loading that is not supported for nullable one-to-one in NHibernate? There is a solution to this problem that does not involve redundant foreign keys in the database.
1) In User mapping define a bogus list. List can have only one or zero items. Zero is treated as NULL (no Contact).
<bag
name="_contact"
table="UserContacts"
lazy="true"
inverse="true"
cascade="all-delete-orphan" >
<key column="UserId" />
<one-to-many class="Contact" />
</bag>
In Contact mapping define one-to-one:
<one-to-one name="_user" class="User" constrained="true" />
In the database you need to have PK Users.Id and one (!) foreign key Contacts.UserID.
2) Another option is to simply have many-to-one in User mapping and one FK Users.ContactId
<many-to-one
name="_contact"
column="ContactId"
cascade="all-delete-orphan"
unique="true"
lazy="proxy"/>
Either way the maintenance that you asked about is not needed and anomalies are not possible.
We have a fairly big DB (~200 tables) which almost entirely uses composite primary keys and composite foreign keys, using a single "base table" from which every other table inherits part of its primary key:
Parent has single column primary key ParentId
Child has composite primary key (ParentId, ChildId) and foreign key ParentId
Nephew has composite primary key (ParentId, NephewId), foreign key ParentId and foreign key (ParentId, ChildId)
and so on. Up until now we managed this whole shebang with an ORM framework of our own, but we're considering using NHibernate, which I've been assigned to learn (i've downloaded v2.1.2).
The mappings:
Child
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Assembly" namespace="Namespace">
<class name="Child" table="Child">
<composite-id name="IdChild" class="ChildId">
<key-many-to-one name="Parent" column="ParentId" class="ParentId"></key-many-to-one>
<key-property name="Id" column="ChildId" type="Int32"></key-property>
</composite-id>
<!--simple properties-->
<set name="Nephews" table="Nephew">
<key>
<column name="ParentId"></column>
<column name="ChildId"></column>
</key>
<one-to-many class="Nephew"/>
</set>
</class>
</hibernate-mapping>
Nephew
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Assembly" namespace="Namespace">
<class name="Nephew" table="Nephew">
<composite-id name="IdNephew" class="NephewId">
<key-many-to-one name="Parent" column="ParentId" class="Parent"></key-many-to-one>
<key-property name="Id" column="NephewId" type="Int32"></key-property>
</composite-id>
<many-to-one name="Child" class="Child">
<column name="ParentId"></column>
<column name="ChildId"></column>
</many-to-one>
<!--simple properties-->
</class>
I can post the classes too if you want, I'll omit them now for brevity (as I'll omit the mapping for Parent, since it doesn't have problems). Every property is virtual, every mapping file is an embedded resource, every composite Id has its own class which overrides Equals and GetHashCode.
The problem is I can't save an instance of Nephew, initialized through a simple new Nephew() and passed on to _session.Save(), because I get a System.IndexOutOfRangeException: Invalid index n for this SqlParameterCollection with Count=n..
The only column which is duplicated in the mapping is the ParentId. Removing the many-to-one mapping in the Nephew, the set mapping in the Child and all related properties everything works fine.
I found several posts reporting this exception, and the most appropriate in my case seems to be this one, which gives me the gut feeling that my current schema is infeasible with NHibernate. Please tell me I'm wrong :-)
NOTES:
I'm not using Fluent right now, even though it may be an option, but I preferred learning the basics first;
Yes, we realize composite primary keys are a big pain in the ass. This DB has passed through several hands through the years, probably not so skilled ones, but before refactoring it we will count to 10 000
Unfortunately, you won't be able to map that relationship in its current form, as you have inferred.
However, there's a workaround. Instead of mapping Nephew.Child as a many-to-one, map ChildId as a regular property, and use a query when you need to retrieve the Child.
There's one more chance for a hack. If and only if Nephew.Child is not null and not mutable, you could map the key referencing the Child instead of the parent:
<class name="Nephew" table="Nephew">
<composite-id name="IdNephew" class="NephewId">
<key-many-to-one name="Child">
<column="ParentId">
<column="ChildId">
</key-many-to-one>
<key-property name="Id" column="NephewId"/>
</composite-id>
</class>
I found a better solution:
<many-to-one name="Child" class="Child">
<formula>ParentId</formula>
<column name="ChildId"></column>
</many-to-one>
I had already tried that and it gave me an error, but then I noticed this patch, so I downloaded the 3.0.0 alpha2 and it all worked correctly! Thanks to this solution I can map the Nephew.Child property as it was meant to be.
I still need the Child.Add(Nephew) method, though (but I realized that is recommended even in the documentation)
I have two tables A -> B with many-to-one mapping/associations.
Table B's primary key is foreign key in table A.
The problem is column names in both tables are different. let's say B has primary key column "typeNumId" which is foreign key in table A as "type". How can I join both tables on this column? how can I specify mapping to indicate that tables have to join on "typeNumId" and "type" which is essentially same.
especially is this possible through hibernate config ( hbm files) ?
something like
<many-to-one name="Type" class="com.domain.ProcedureType" update="false" insert="false" fetch="join" lazy="false">
<column name="? <this is in questions? >" not-null="true" />
</many-to-one>
The ON clause:
select * from A join B on A.type = B.typeNumId
You declare the name of the foreign key using the column attribute of the many-to-one element or the equivalnent nested column element. From the documentation:
5.1.12. Many-to-one
An ordinary association to another
persistent class is declared using a
many-to-one element. The relational
model is a many-to-one association; a
foreign key in one table is
referencing the primary key column(s)
of the target table.
<many-to-one
name="propertyName" (1)
column="column_name" (2)
class="ClassName" (3)
cascade="cascade_style" (4)
fetch="join|select" (5)
update="true|false" (6)
insert="true|false" (6)
property-ref="propertyNameFromAssociatedClass" (7)
access="field|property|ClassName" (8)
unique="true|false" (9)
not-null="true|false" (10)
optimistic-lock="true|false" (11)
lazy="proxy|no-proxy|false" (12)
not-found="ignore|exception" (13)
entity-name="EntityName" (14)
formula="arbitrary SQL expression" (15)
node="element-name|#attribute-name|element/#attribute|."
embed-xml="true|false"
index="index_name"
unique_key="unique_key_id"
foreign-key="foreign_key_name"
/>
name: the name of the property.
column (optional): the name of the foreign key column. This can also be
specified by nested
element(s).
class (optional - defaults to the property type determined by
reflection): the name of the
associated class.
cascade (optional): specifies which operations should be cascaded from the
parent object to the associated
object.
fetch (optional - defaults to select): chooses between outer-join
fetching or sequential select
fetching.
update, insert (optional - defaults to true): specifies that the mapped
columns should be included in SQL
UPDATE and/or INSERT statements.
Setting both to false allows a pure
"derived" association whose value is
initialized from another property that
maps to the same column(s), or by a
trigger or other application.
property-ref (optional): the name of a property of the associated class
that is joined to this foreign key. If
not specified, the primary key of the
associated class is used.
access (optional - defaults to property): the strategy Hibernate uses
for accessing the property value.
unique (optional): enables the DDL generation of a unique constraint for
the foreign-key column. By allowing
this to be the target of a
property-ref, you can make the
association multiplicity one-to-one.
not-null (optional): enables the DDL generation of a nullability
constraint for the foreign key
columns.
optimistic-lock (optional - defaults to true): specifies that
updates to this property do or do not
require acquisition of the optimistic
lock. In other words, it determines if
a version increment should occur when
this property is dirty.
lazy (optional - defaults to proxy): by default, single point
associations are proxied.
lazy="no-proxy" specifies that the
property should be fetched lazily when
the instance variable is first
accessed. This requires build-time
bytecode instrumentation. lazy="false"
specifies that the association will
always be eagerly fetched.
not-found (optional - defaults to exception): specifies how foreign keys
that reference missing rows will be
handled. ignore will treat a missing
row as a null association.
entity-name (optional): the entity name of the associated class.
formula (optional): an SQL expression that defines the value for
a computed foreign key.
So something like this should do it:
<many-to-one name="Type" class="com.domain.ProcedureType" update="false" insert="false" fetch="join" lazy="false">
<column name="type" not-null="true" />
</many-to-one>
Reference
Hibernate Core documenation
5.1.12. Many-to-one
Something like this?
LEFT JOIN B on A.field1 = B.field2
Choose type of JOIN on your taste
I have a table called "Orderrow". Orderrow has a a compound primary key (CPK) with the following columns: OrderId, ProductId, RowNumber
OrderId and ProductId are also foreign keys refering to the tables Order and Product. The RowNumber is generated in the app.
I was wondering how this is mapped in NHibernate because I can only set 1 id element and 1 generator subelement.
Here is an example using the composite-id property in a NHibernate mapping file:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Domain" namespace="Domain.Model">
<class name="Program" table="program">
<composite-id>
<key-property name="Id" column="id"></key-property>
<key-property name="Prog" column="prog"></key-property>
<key-property name="Site" column="site"></key-property>
</composite-id>
<property name="ActiveDate" column="active_date"/>
<property name="Year" column="year"/>
</class>
</hibernate-mapping>
There is a 'composite-id' property you can use in your class mapping instead of the 'id' property. However, you should know that the nHibernate team strongly discourages the use of it. It sounds like you are dealing with a legacy database, in which you have no control over the schema. So, the 'composite-id' is specifically for your situation.
Check out the NH docs for more info: http://nhibernate.info/doc/nh/en/index.html
Personally I do not like to have compound primary keys in my database table schemas, plus they usually are 'hard' to handle by any ORM mapper I tried.
If I have to give a unique identity to a row in a table (especially if it represents a business object), I prefer to use a single value surrogate Id - a GUID or an Integer - if any of the fields in the table isn't suitable.
Using a composite Id is always troublesome when writing interrogation queries and joins, so my usual strategy is to use a surrogate Id (which has no meaning to the business object but to identify the object itself) and place a unique constraint on the fields of the previous CPK.
I have the following object model:
A top-level abstract class Element with many children and descendants.
A class Event.
Each Element contains a bag of Events.
Each Event has a pointer to the parent Element.
Up till now - pretty standart one-to-many relationship.
But, I want to use table per concrete class strategy. So, the class Element is not mapped to the database. I've tried to solve it this way: each of the concrete descendants of Element defines its own Bag of Events. The problem with this is that each <bag> element contains a <key> element. That key points to the Parent property of Event. It also makes the Parent column in the Events table a foreign key to the table which contains the Bag! But one column can't be a foreign key to several tables and I'm getting an exception on insert.
I've also tried to make the Parent field in the Events table a many-to-any kind of field. That worked. But when I want to make the relation bidirectional, meaning, to add the bags to the descendants of Element I come back to the same problem. Bag => foreign key => exception on insert.
I'm sure this case isn't as unique as it seems.
Thank you in advance for your help.
A little bit late, but I have some advise.
If you are using "table per concrete class", it is as if you would map completely independent tables. So you need separate foreign keys or many-to-any.
many-to-any stores the type name and NH knows to where the foreign key points. But it's impossible to have constraints on such a foreign key.
If you have several bags having items of the same type, make sure they all define different foreign keys:
<class name="A">
<!-- ... -->
<bag name="Events">
<key column="A_FK"/>
<one-to-many class="Event"/>
</bag>
</class>
<class name="B">
<!-- ... -->
<bag name="Events">
<key column="B_FK"/>
<one-to-many class="Event"/>
</bag>
</class>
You can have foreign key constraints on such a foreign key, but no not-null constraint, because only one of these foreign keys is used.
To really have only one foreign key with all the constraints, you need to map the element to a separate table.