Hibernate xml mapping - JOIN on different foreign key column - sql

I'm using hibernate 3 with XML entity definitions. I want to join some columns of table person_data into an entity based on table employee. The problem is that the relation is defined on employee.person_data_id to person_data.person_id.
<hibernate-mapping package="...">
<class name="Employee" table="employee">
<id name="id" column="id" type="java.lang.Long">
<generator class="identity"/>
</id>
<property ... />
<property ... />
<property ... />
<join table="person_data">
<key column="person_id" foreign-key="person_data_id"/>
<property ... />
</join>
</class>
</hibernate-mapping>
But the resulting SQL statement contains:
... FROM employee JOIN person_data ON employee.id = person_data.person_id ...
Looks like the key column is used properly but somehow I cannot tell hibernate to use employee.person_data_id as a foreign key. I thought that the attribute foreign-key is for this purpose but maybe not.
(I know I can solve this problem by using a many-to-one relation but then I have to use an entity 'PersonData' as component of 'Employee'.)

Related

How would you define the NHibernate mapping where a single column is used for multiple relationships?

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"/>

hibernateTemplate.delete() delete the proper entity and the FK in relationated table

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.

Nhibernate mapping, join only brings back the first value in the joined table

I am trying to retrieve the plant name for some parts. I usually use entity framework but I am working with the AS400 and Entity Framework was not an option for AS00. I am getting only the first value found in the join. Parts with the same id can be made in different plants.
Actual Data
Part Table PartDetails Table
partid-1234, plant4
partid-1234, plant5
partid-1234, plant6
What the query returns
Part Table PartDetails Table
partid-1234, plant4
partid-1234, plant4
partid-1234, plant4
<class name="Part" table="DCSCIM" dynamic-update="false">
<cache usage="read-write"/>
<id name="Id" column="ITMID" type="String">
<generator class="uuid.string"/>
</id>
<property name="ITMDESC"/>
<property name="ALTDESC"/>
<property name="DTECRT"/>
<join table="DMFPSMR">
<key column="ITMID"/>
<property name="PLT" column="PLT" type="String" length="100" />
</join>
</class>
</hibernate-mapping>
You should be using collections, not join. I suggest you read the docs: http://nhibernate.info/doc/nh/en/index.html
I ended up joining the data n the repository instead of the mapping file with Linq.

NHibernate executing extraneous select statements

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.

NHibernate one-to-one mapping where second table data can be null

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.