Let's say I have a table 'Team' and another table 'Team Members'. Team Member has a foreign key to Team.
Ideally, I would want hibernate to map my java objects such that, Team class would have a Map of Team Members. The key of the map is the name of the team member, and value is the TeamMember object.
So I would want my Team class to look like this:
public class Team {
private Map<String, TeamMember> _teamMembers;
}
How do I map in my hibernate xml mapping file?
you'll need <map> and set the index column to the column of the name property
<map name="teamMembers">
<key column="team_id" />
<index column="NameColumn" />
<one-to-many class="TeamMember" />
</map>
Related
I have recently inherited a project at work that contains NHibernate. I am extremely new to it and have to make a modification to one of the mappings. I've read through the documentation here and I'm still not sure how to do this or if my understanding/terminology is even correct.
So given the following table structure I need a bag that will get me the ProjectName:
User
UserID (PK)
ProjectUser
UserID (PK, FK User.UserID)
ProjectID (PK, FK Project.ProjectID)
Project
ProjectID (PK)
ProjectName
Here is the existing bag mapping and it correctly returns the ProjectID, but now I'm trying to understand how I need to modify it to return both the ProjectID and the ProjectName:
<bag name="Projects" table="ProjectUser" lazy="true" inverse="true" cascade="save-update">
<key column="UserId"></key>
<many-to-many class="Project" column="ProjectID"></many-to-many>
</bag>
Well, your mapping seems to be correct, i.e. already returning the ProjectName. To be sure please, check that the object Project is mapped like this:
<class name="Project" table="Project">
<id name="Id" column="ProjectID" generator class="native"/>
<!-- above as the Id we have mapping for column ProjectId
below the C# Name will contain the column ProjectName -->
<property name="Name" column="ProjectName" />
<!-- related Users to this Project -->
<bag name="Users" table="ProjectUser" lazy="true" >
<key column="ProjectID"></key>
<many-to-many class="User" column="UserID" />
</bag>
</class>
And the Project would be like this
public class Project
{
public virtual int Id { get; set;}
public virtual string Name { get; set;}
public virtual IList<User> Users { get; set;}
...
So, having this in place, we should be able to use the User:
public class User
{
public virtual IList<Project> Projects { get; set;}
...
mapped and loaded by NHibernate like this
user = session.Get<User>(x) // id of searched user
foreach(var project in user.Projects)
{
var name = project.Name;
var id = project.Id;
...
}
NOTES:
In case of many-to-many there obviously could/should be <bag> mapping on both sides - Project and User. But only one of them can have inverse="true". So in this case Project.Users does not have that.
The cascade setting on many-to-many is doing (most likely) different thing than one would expect. It is not related to the pairing table but to the second end of that mapping.
Cascading of the pairing object is done out of the box. It does not have to be mapped, in fact it cannot be mapped or turned off... Other words I would suggest to remove that cascade, unless you really want to change the Project in persistence, if you are working with some User.
Check also:
23.2. Author/Work
How to do it without many-to-many ... with explicit pairing object as a mapped Entity: Nhibernate: How to represent Many-To-Many relationships with One-to-Many relationships? or Am I doing many to many incorrectly when using fluent nhibernate?
My many-to-many relationship does not involve the standard "joining-table" approach, in which a table stores the "FK1-to-FK2" relationships.
Instead, I'm "loosely" joining to a legacy read-only view as follows:
Appointment class (based on the Appointment table)
int AppointmentId (PK)
int OrderId
List<LegacyOrder> LegacyOrders
LegacyOrder class (based on the LEGACY_ORDERS_VIEW view in our legacy system)
int OrderId (composite PK)
int VersionNumber (composite PK)
An Appointment can have many (versions of a) LegacyOrder.
A LegacyOrder can have many Appointments, but this relationship is not important in our application.
I want to populate the LegacyOrders property with all LegacyOrders for the specified OrderId.
My attempt at mapping is as follows:
<class name="Appointment" table="Appointments" lazy="true">
<bag name="Orders" table="LEGACY_ORDERS_VIEW" inverse="true">
<key column="OrderId" />
<many-to-many class="LegacyOrder" column="ORDER_ID" />
</bag>
</class>
....but I'm getting "could not execute query" exceptions due to invalid SQL.
I think the table referred to in the <bag> mapping should be the "joining table".... but I don't have one.
I'm fairly sure my mapping approach is fundamentally wrong.... what's the right way to go about it?
Edit:
Thanks Radim: perhaps a better name for LegacyOrder would be LegacyOrderVersion: each record in that view corresponds to a "version" of an order, rather than an order.
i.e. An order may be for 100 units, then when say 20 units are collected, another record is written with the same OrderId but for 80 units. (I did warn you it was legacy :)
If an Appointment (in the new system) can retrieve all related LegacyOrderVersions, then it can derive useful properties such as CurrentLegacyOrderVersion and OriginalLegacyOrderVersion.
FWIW: this works great for me:
<class name="Appointment" table="Appointments" lazy="true">
<bag name="Orders" inverse="true">
<key property-ref="OrderId" column="ORDER_ID" />
<one-to-many class="LegacyOrder" />
</bag>
</class>
One way how to solve this a bit challenging DB structure, could be with the property-ref feature. See more details here: 5.1.10. many-to-one, working even for our many-to-many scenario.
So firstly we have to map the property, which we will use as a reference:
<class name="Appointment" table="Appointments" lazy="true">
...
// the column name is coming from the Appointment table
<property name="OrderId" column="ORDER_ID" />
So, now we have mapped the OrderId - the property (column) - which we will use to map the <bag>.
Well, honestly, now I am not sure what your thoughts were. In case that LegacyOrder would have one column mapped as key (the Order_ID) we can do it like this.
<bag name="Orders" table="LEGACY_ORDERS_VIEW" inverse="true">
<key column="ORDER_ID" property-ref="OrderId" />
<many-to-many class="LegacyOrder" formula="ORDER_ID" />
</bag>
But that's not reasonable, because the Order_Id is not unique. In fact the LegacyOrder view, does not seem to be the entity at all. It could be some real intermediate structure.
I would say, that what the pairing view Legacy_orders_view represents, is the map (dictionary) saying: The Order with ID == X, had these Versions.
This information, the int Version numbers, is the only thing/information I can find out as really interesting. The OrderId is representing still the same Order
Anyhow, with the proeprty-ref and more detailed knowledge what you need to achieve we can at the end have:
// I. Map
public virtual IDictionary<int, Order> OrderMap { get; set; }
above the Version will play the role of the Key, the Order is questinable, because it will be the same Order as the OrderId says
// II. Version collection
public virtual IList<int> OrderVersions { get; set; }
in this case we will get set of int numbers related to the OrderId. Seems to be the only interesting message we can get.
III. There must be more information, about your entity/DB model. Why does the Legacy_orders_view exists at all? What would we like to get from that "relation" at the end?
I have customer and BankAccount tables. CustomerID is a foreign key in the BankAccount table. Here we have to do with one to many relationship. I have done mapping for CustomerInfo and BankAccountInfo. I have attached with the question post.
Here we want to do with collection using Iesi.collections. Does anyone have ideas on how to do this in Nhibernate?
To do Collection mapping start reading this documentation: Chapter 6. Collection Mapping
In case that you will use the Iesi collections, most suitable could be ISet<>, which is designed to allow only unqiue values to be inserted:
public class Customer
{
public virtual Iesi.Collections.Generic.ISet<BankAccount> BankAccounts { get; set; }
}
And the mapping could be like this
<set name="BankAccounts" inverse="true" lazy="true" table="BankAccount"
cascade="all" batch-size="25" >
<key column="CustomerId" />
<one-to-many class="BankAccount" />
</set>
Notes: This mapping has some redundant attributes, which can help to do smarter loading. E.g. batch-size will avoid 1+N loads, while doing 1 + N/25... cascade will implicitly solve the persistence of added items... read more in doc.
The complete ISet description: http://ayende.com/blog/3943/nhibernate-mapping-set
Let's start with this mapping:
<component name="Location">
...
<property name="Settings" type="JsonUserType,...">
<column name="LocationSettingsType" />
<column name="LocationSettingsData" />
</property>
</component>
This maps to
TABLE Primary (
...
LocationSettingsType,
LocationSettingsData
...
)
and
class Location {
...
object Settings { get; set; }
}
Now, I want to extract settings into a separate table (because they are seldom here).
So I get
TABLE Primary (
...
LocationSettingsId,
...
)
TABLE Settings (
Id,
Type,
Data
)
Can I keep my C# classes the same?
Update: This is not a many-to-one relationship. As before, each location has zero or one settings, and each settings belong to at most one location.
I believe the closest thing that exists to this is the <map> mapping element; details are explained in this article.
If you want a one to many relationship on the Primary and Settings tables, you'll have to set a foreign key constraint first. Then you'll use the bag property in XML to map your tables. You will have an entity for each table.
See also this question on NHibernate/FluentNHibernate Property Bag.
I also recommend you purchase the NHibernate 2 for Beginners book. It helped me alot.
This is an old question but i had the same issue and looking to a solution I came here.
the component element can map several columns to several object models.
the join element can map several tables to an object model.
The main problem is that while the component cannot map columns from a different table where the model belong, the join cannot map the different table columns to a different object model.
The solution I found is to use both to achieve the map column of a different table to several object:
<class name="Primary" table="Primary">
<id name="Id">
<generator class="identity"/>
</id>
<property name="Name" />
...
<join table="Settings">
<key column="PrimaryId"/>
<component name="Location">
...
<property name="Settings" type="JsonUserType,...">
<column name="LocationSettingsType" />
<column name="LocationSettingsData" />
</property>
</component>
</join>
</class>
Reference:
NHibernate Join mapping element
NHibernate Component mapping element
I have been trying to work with DDD style for my e-commerce application. Most of my business logic are implemented using fake repositories. Now, I would like to use NHibernate to hook my Domain Model to a real database.
I have a class Order which contains a list of OrderLines object
public IList<OrderLine> OrderLines{ ... } //line 1
In my OrderLine class I have a reference to the parent Order as follows
2. public Order Order { set; get;} // line 2
According to my understanding, OrderLine is a Value class instead of an Entity class, so I will use the composite-element to do the mapping.
<bag name="OrderLines" table="OrderLines" lazy="true">
<key column="Order_ID"/> <!--This is where I got confused. line 3-->
<composite-element class="OrderLine">
<!-- class attribute required -->
<many-to-one name="Order" class="Order" column="Order_ID"/> <!--Do I need this? line 4-->
...
</composite-element>
</bag>
Note that in line 3, I created a key element for the mapping(syntax requires this). But since I have defined a reference to Order class in my OrderLine class(line 2), do I also need to create a mapping at line 4?
You don't need line#4.
You can find a great explanation on the subject in another thread at stackoverflow here: Nhibernate , collections and compositeid