Alright, I've got a quick question. I'm currently working with a legacy database, so I can't change around much. The database revolves around a single type, the entity. All other relevant data (except for customizable lists etc.) 'inherit' from this.
I'm mapping this with a joined subclass approach, which is working fine. The problem however, is that my mapping file is reaching hideous proportions. I would like to split up the file in multiple separate files, but I'm not sure if this is possible and how to approach this problem. I've read through the Hibernate docs on the official site, but couldn't find anything.
To clarify, mappings look like this:
<class name="..." table="...">
<id ...>
<generator class="org.hibernate.id.TableHiLoGenerator">
<param name="table">...</param>
<param name="column">...</param>
</generator>
</id>
<property name="somethingCommon" />
<joined-subclass name="class_1">
...
...
</joined-subclass>
<joined-subclass name="class_2">
...
...
</joined-subclass>
...
<joined-subclass name="class_n">
...
...
</joined-subclass>
</class>
What I would like to be able to do is put the joined-subclass bits in separate files, just like I would do in code (separate classes -> separate files). Is this possible using just mappings, or perhaps by manipulating the mappings when I load them?
(Note: tagged hibernate/nhibernate, as I don't think this is specific to either flavor)
I believe you can use "extends" eg:
<hibernate-mapping>
<joined-subclass name="DomesticCat" extends="Cat">
...
</joined-subclass>
to separate out the joined subclasses into separate files.
Related
I'm struggling with a NHibernate related problem where I could use some input.
Introduction:
I have a legacy database where the relational concepts have not really been applied.
In the database I have an OrderLine table which contains data for an order lines.
On top of that the table also contains all columns with Order specific information. This could for example be order number of a customer.
E.x. If i have 10 order lines - then I have 10 rows in my OrderLines table and each row has all the Order specific data e.g. order number or customer information.
I did not want to have the above structure in my code so a view was created for Orders so that I could map my Order in NHibernate which then has a set/bag of OrderLines which makes much more sense.
Mapping: (simplified)
<class name="Order" table="[view_Orders]">
<bag name="OrderLines">
</class>
<class name="OrderLine" table="OrderLines" />
The problem:
The complexity of the view makes it impossible to save to the view. When trying NHibernates throws this exception:
NHibernate.Exceptions.GenericADOException: could not insert: XXX ---> System.Data.SqlClient.SqlException: View or function 'view_Orders' is not updatable because the modification affects multiple base tables.
My NHibernate mapping is constructed as an Order object which has a "set or bag" of OrderLine objects. Ideally I would like NHibernate only to persist the set of OrderLine objects instead of the whole object.
Is there a way of achieving this? I have tried locking the object using different lock modes but it did not help me.
You can use mutable="false" to avoid the update and deletes as this article says:
Immutable classes, mutable="false", may not be updated or deleted by the application. This allows NHibernate to make some minor performance optimizations.
To avoid the insert you can use the following statement (Uses the proyection instead an insert command, dont forget use check="none"):
<sql-insert check="none">SELECT 1</sql-insert>
Here is a tested example:
<class name="Order" table="[view_Orders]" mutable="false">
<id name="OrderId" type="System.Guid">
<generator class="guid.comb"/> <!-- Change as you need -->
</id>
<!-- Other properties -->
<!-- <property name="GrandTotal"/> -->
<set name="OrderLines" lazy="true" inverse="true" cascade="all-delete-orphan">
<key column="OrderId"/>
<one-to-many class="OrderLine"/>
</set>
<sql-insert check="none">SELECT 1</sql-insert>
</class>
<class name="OrderLine" table="OrderLine">
<id name="OrderLineId" type="System.Guid">
<generator class="guid.comb"/> <!-- Change as you need -->
</id>
<!-- Other properties -->
<!-- <property name="OrderId"/>
<property name="GrandTotal"/>/> -->
</class>
In case I do understand your issue, the solution is surprisingly simple. We just would mark root object with dynamic-update="true"
<class name="Order" table="[view_Orders]" dynamic-update="true">
...
</class>
And then apply update="false" to every property or reference which we have in that Order class mapped to view:
...
<property name="Code" update="false"/>
...
<many-to-one name="Country" update="false />
But our collection will need the standard, even cascade mapping:
<class name="Order" table="[view_Orders]" dynamic-update="true">
<bag name="OrderLines"
lazy="true"
inverse="true"
batch-size="25"
cascade="all-delete-orphan" >
...
</bag>
... // other stuff is update="false"
</class>
And now code like this would do management of OrderLines, while not executing any updates on the root object Order
var session = ... // get ISession
// load root
var root = session.Get<Order>(123);
// if needed change existing line (pretend there is one)
root.OrderLines[0].Amount = 100;
// add new
var newOrder = ... // new order
root.OrderLines.Add(newOrder);
session.Save(root);
session.Flush();
And that is it. Cascade on the root object is doing what we need, while the update="false" is not updating it...
NOTE: Just interesting note - there is also class and collection
setting mutable="false", but it would not work here... as the
solution mentioned above (it is sad, because that would be more
elegant, but not working as expected...). See:
19.2.2. Strategy: read only
If your application needs to read but never modify instances of a persistent class, a read-only cache may be used. This is the simplest and best performing strategy. Its even perfectly safe for use in a cluster.
<class name="Eg.Immutable" mutable="false">
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.
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'm trying to map a set of joined-subclass entities to a parent entity. The SQL being produced by NHibernate seems to be incorrect. Am I missing anything in my mapping? Is there another way I can approach this problem?
<class name="ResultItem" table="result">
<id name="ID">
<generator class="identity" />
</id>
<many-to-one name="Job" column="JobID"/>
<property name="Timestamp"/>
<many-to-one name="User" column="UserID"/>
<joined-subclass name="ResultItemAttachment" table="result_attachment">
<key column="ID"/>
<property name="Comment"/>
</joined-subclass>
</class>
This is the SQL being generated by NHibernate. It seems as if its getting confused between super and sub class members? The only table with JobID is the result table and not result_attachment.
SELECT attachment0_.JobID as JobID1_,
attachment0_.ID as ID1_,
attachment0_.ID as ID26_0_,
attachment0_1_.JobID as JobID26_0_,
attachment0_1_.`Timestamp` as Timestamp26_0_,
attachment0_1_.UserID as UserID26_0_,
attachment0_.`Comment` as Comment33_0_
FROM result_attachment attachment0_
inner join result attachment0_1_ on attachment0_.ID=attachment0_1_.ID
WHERE attachment0_1_.JobID=?;
Thanks in advance
I'm afraid this is currently a bug in NHibernate (in there since 2.1 :|) apperently there is a fix in the current alpha of 3.2
https://nhibernate.jira.com/browse/NH-1747
Possible work around is to manually fetch the bag at runtime. Hardly ideal, other option would be to try and get the alpha version running but running an alpha in a production environment is hardly a great idea.
I am trying to develop my very first project with Nhibernate 3.0.
I've gone through loads of material (blogs, papers and samples) and I think I can understand the basics, pretty much.
I think I've understood the meaning of different types of collection but than when I see the examples found on the Internet I think I haven't really understood.
The documentation says that you should use a Set when you do not want duplicates and a List/Bag when you want to allow duplicates.
Most of the samples I have found are based on a typical situation where you have Orders/OrderLines.
If I have a look the mapping file of the Order I can see something like this:
<class name="OrderHeader"
table="Orders">
<id name="OrderId">
<generator class="hilo"/>
</id>
<property name="OrderDate"/>
<bag name="OrderItems" table="OrderDetails" cascade="all" inverse="true">
<key column="OrderId"/>
<one-to-many class="OrderDetail"/>
</bag>
</class>
<class name="OrderDetail"
table="OrderDetails">
<id name="DetailId">
<generator class="hilo"/>
</id>
<many-to-one name="ProductOrdered" column="ProductId"/>
<many-to-one name="Order" column="OrderId" />
</class>
I had expcted to see the OrderItems mapped as a Set; an order will have unique OrderItems?
Am I right?
At the same time I would expect to find a mapping for the class Product a bag of OrderItems
...
<bag lazy="true" name="OrderItems">
<key foreign-key="FK_OrderItems_Products">
<column name="ProductCode" />
</key>
.....
</bag>
...
In this situation a product would have a list of non-unique OrderItems.
Is there anything am I missing?
Forgive me all for the silly question :-s
I would set it as a <list ...> as in my book two orderlines with the same characteristics are identical (.Equals should return true) - also, many consider the order(!) of orderlines to be important. :-)
The reason for this is that bag mapping is compatible with a .NET IList<T> mapping as Yads answered. Collections are typically mapped that way to avoid having to reference Iesi.Collections in your classes.
With NHibernate 2.1, you can use set mapping by declaring the collection as ICollection<T> and initializing it as a HashSet<T>. Using NHibernate 3 I believe you can declare the collection as ISet<T>.
I agree most real world collections should be mapped as sets.
The reason most people implement it as a bag is because they can use the built in List<> class or IList<> interface to represent their collection.