How to map an value object in his own table with Fluent NHibernate? - nhibernate

I have the following scenario: I have a component of an entity, but instead of store it in the same table I need to store in a separate table. The relationship between this two tables is one to one at most (1-0:1).
The id of the component table is given by the main table, as value object it doesn't have an identity.
Now I wonder how can I map the component to be stored in his own table without add an Id to it in the domain model.

There are three main ways to map a one-to-one relationship: inheritance, one-to-one, and join. I'm pretty sure that all three of these can be configured to share primary keys instead of having to add an additional primary key column. In this case, join sounds like the best fit, since you wouldn't have to create a separate entity. Ayende's article is the best resource for understanding join.
Example (from Ayende's article, but adding a component to the mix):
<class name="Person"
table="People">
<id name="Id">
<generator class="identity"/>
</id>
<property name="Name" />
<join table="Addresses">
<key column="PersonId"/>
<component name="Address"
class="Address">
<property name="Line1"/>
<property name="Line2"/>
<property name="City"/>
<property name="Country"/>
<property name="ZipCode"/>
</component>
</join>
</class>
The classes for this would look like:
public class Person
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Address Address { get; set; }
}
public class Address
{
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string ZipCode { get; set; }
}
Note that Address does not have an Id property, and it's properties are not virtual. This is because Address is not an entity, it is a component. But join allows it to live in a separate table from Person's other properties.

If the table with the value object is only referenced by one table you can use the primary key of that table as primary key but don't make any methods to access the field in the value object.
If the table is referenced by more other tables then you have to create an own primary key.
You need a primary key to at least join the tables.

Related

Issue when quering Hierarchy to Hierarchy relationship

A Teacher has a one-to-one with a Student.
A SpecialTeacher extends Teacher but deals specifically with SpecialStudents.
Using table per class in the hierarchies.
public class Teacher
{
public virtual int Id { get; set; }
public virtual int DepartmentId { get; set; }
public virtual String Name { get; set; }
public virtual Student Student { get; set; }
}
public class SpecialTeacher : Teacher
{
public virtual string TelephoneNumber { get; set; } //SpecialTeachers get to have a phone
public virtual SpecialStudent SpecialStudent { get { return (SpecialStudent)base.Student; } set { Student = value; } }
}
public class Student
{
public virtual int Id { get; set; }
public String Name { get; set; }
}
public class SpecialStudent : Student
{
public int SpecialMark { get; set; }
}
and the associated mappings:
<class name="Student">
<id name="Id" />
<property name="Name" />
</class>
<joined-subclass name="SpecialStudent" extends="Student">
<key column="Id" />
<property name="SpecialMark" />
</joined-subclass>
<class name="Teacher">
<id name="Id" />
<property name="DepartmentId" />
<property name="Name" />
<many-to-one name="Student" column="StudentId" />
</class>
<joined-subclass name="SpecialTeacher" extends="Teacher">
<key column="Id" />
<property name="TelephoneNumber" />
</joined-subclass>
So, let's say that we want to get the average mark for SpecialStudents for a given department:
public double GetAverageScoreForSpecialStudentsByDepartment(int departmentId)
{
return CurrentSession.Query<SpecialTeacher>()
.Where(st => st.DepartmentId == departmentId)
.Average(ss => ss.SpecialStudent.SpecialMark);
}
The test will fail because it will complain that SpecialStudent is not a mapped property of SpecialTeacher.
The only way that I can think of avoiding this issue is to map the property, but this is duplication since the base Teacher is already mapped to the Student hierarchy.
Update
I meant to also mention that previously we had the SpecialTeacher set up like:
public class SpecialTeacher : Teacher
{
public virtual string TelephoneNumber { get; set; } //SpecialTeachers get to have a phone
public virtual new SpecialStudent Student { get { return (SpecialStudent)base.Student; } set { Student = value; } }
}
which did appear to work ok, but Envers did not work with it when retrieving audited data.
The only way that I can think of avoiding this issue is to map the property, but this is duplication since the base Teacher is already mapped to the Student hierarchy.
This is not duplication as you never mapped the SpecialStudent property in the SpecialTeacher mapping file. Although you correctly defined the relationship in code, NHibernate has no way of knowing a SpecialTeacher is suppose to have a SpecialStudent. The code is use by NHibernate to recreate the object from the tables, but only if you define the correct relationships in your mapping.
Remeber that BaseTeacher to BaseStudent does not imply SpecialTeacher to SpecialStudent relationship.

Mapping a column multiple times in Nhibernate

I have for example an entity. With the following properties:
public class Entity
{
public int CustomerId { get; set; }
public Customer { get; set; }
}
How can I map the CustomerId twice. Once for the int property and once for the many-to-one relationship ?
<many-to-one name="Customer" column="[CustomerId]" class="Customer"/>
<property name="CustomerId" column="[CustomerId]" type="Int64" />
Just this, doesn't work. I've already tried, making them readonly but no success.
One of them should be mapped as readonly (inser/udpate false), and referenced as formula
<many-to-one name="Customer" column="[CustomerId]" class="Customer"/>
<property name="CustomerId" formula="[CustomerId]" type="Int64" insert="false" update="false" />
Then it should be working correctly. Both properties then can be used for Select, Where... order by
You don't need to map CustomerId, you can access it through Customer.CustomerId. If you're using lazy loading, CustomerId will be populated in the proxy object so it's always available without triggering an additional select.
If you absolutely have to expose it, expose it as a nullable read only property:
public Customer { get; set; }
public int? CustomerId
{
get { return Customer == null ? (int?)null: Customer.CustomerId }
}

NHibernate mapping

I use HBM mapping.
I have tables :
I) person with columns :
1. ID
2. TYPE
3.CREATE_DATE
4.UPDATE_DATE
II) Attribute with columns:
1.ID
2.TYPE(in this example person may be all type)
3.NAME
4.CREATE_DATE
5.UPDATE_DATE
III) Attribute_VALUE with columns:
1.ID
2.VALUE
4.OBJECT_ID
5.ATTRIBUTE_ID
6.CREATE_DATE
7.UPDATE_DATE
There is relationship one-to-many between person(ID) and Attribute_VALUE(OBJECT_ID).
There is relationship one-to-many between Attribute(ID) and Attribute_VALUE(ATTRIBUTE_ID)
I need build object PERSON that contain all columns of person and dictionary with name attribute.
The dictionary contain key - name of attribute value- collection of values .
Can I build appropriate HBM ??
the short answer no.
the long answer:
consider how should nhibernate match attributes when you Attributes.Add("foo", "value")? it has to search the db for an attribute foo (which is not a simple mapping, its logic) or it would create a new Attribute, everytime you add one.
So given the above schema you either a) have some kind of custom onsave code (which i think is a lot of effort) or b) you change the Person to
class Person
{
public virtual int Id { get; set; }
public virtual ICollection<AttributeValue> Attributes { get; set; }
public virtual IEnumerable<string> GetValues(string attributeName)
{
return Attributes
.Where(attr => attr.Attribute.Name == attributeName)
.Select(attr => attr.Value);
}
public virtual void AddValue(Attribute attribute, string value)
{
Attributes.Add(new AttributeValue
{
Attribute = attribute,
Value = value
});
}
public virtual IEnumerable<string> GetAttributeNames()
{
return Attributes
.Select(attr => attr.Attribute.Name);
}
}
class Attribute
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
// and more Properties like created and updated
}
class AttributeValue
{
public virtual int Id { get; set; }
public virtual Attribute Attribute { get; set; }
public virtual string Value { get; set; }
// and more Properties like created and updated
}
and then use
<class name="Person" table="Persons" xmlns="urn:nhibernate-mapping-2.2">
<id name="Id" column="ID"/>
<bag name="Attributes">
<key column="OBJECT_ID"/>
<one-to-many class="AttributeValue"/>
</bag>
</class>
<class name="Attribute" table="Attributes" xmlns="urn:nhibernate-mapping-2.2">
<id name="Id" column="ID"/>
<property name="Name" column="Name"/>
<!--additional properties-->
</class>
<class name="AttributeValue" table="AttributeValues" xmlns="urn:nhibernate-mapping-2.2">
<id name="Id" column="ID"/>
<many-to-one class="Attribute" column="ATTRIBUTE_ID"/>
<property name="Value" column="Value"/>
<!--additional properties-->
</class>

Map a column multiple times

I have a rather odd requirement in my fluent hibernate maps. I have an table(A) which has a compound foreign key relationship with another table(B). In the mapping for table A I would like to have both the object created from table B and access to the individual attributes of A which define the key. Is there any way to do that? I seem to get index out of range exceptions if I map the column twice.
I cannot just explore B for the attributes because the row in table B may not exist. I am painfully aware that there are some significant smells in the structure with which I'm dealing. Such is the fate of those who deal with legacy systems.
It's kinda possible, by hacking around a little.
We're going to define a domain that with a fake collection that we'll use to retrieve the single related element, if found:
public class Foo
{
public virtual BarKey BarKey { get; set; }
public virtual Bar Bar { get { return Bars.SingleOrDefault(); } }
protected virtual ICollection<Bar> Bars { get; set; }
}
public class Bar
{
public virtual BarKey Id { get; set; }
}
//this class must override Equals and GetHashcode. Implementation not shown.
public class BarKey
{
public virtual int X { get; set; }
public virtual int Y { get; set; }
}
The BarKey component contains the properties that are part of the key.
Now, the mapping:
<class name="Foo">
<id ...><generator .../></id>
<component name="BarKey">
<property name="X" />
<property name="Y" />
</component>
<bag name="Bars" inverse="true">
<key property-ref="BarKey">
<column name="X"/>
<column name="Y"/>
</key>
<one-to-many class="Bar"/>
</bag>
</class>
<class name="Bar">
<composite-id name="Id">
<key-property name="X" />
<key-property name="Y" />
</composite-id>
</class>
The property-ref attribute there tells NH to match those columns in Bar against the BarKey property of Foo instead of its Id.

How does ORM solve bidirectional relationship between entities (NHibernate, for example)?

I'm writing a homework for my RDBMS class, I need to perform CRUD operations on quite simple domain, which is cyber sport championship.
Students are required to use ADO.NET. My question is how can I solve bidirectional relationship, for example 1:m (every championship has many matches, but every match belongs to only one exact championship)? It seems to me that there must be some technique for that.
And the most interesting part for me is - how does ORM like EF or NHibernate solve this situation?
In NHibernate, it is quite simple and straight-forward. Here's how the domain classes would look, followed by fluent mappings. This assumes you would use NHibernate to generate your schema. If you are mapping a legacy database, it is simple to set the column names and table names used.
public class Championship {
public virtual int Id { get; set; }
public virtual IList<Match> Matches { get; set; }
}
public class Match {
public virtual int Id { get; set; }
public virtual Championship Champioship { get; set; }
}
public class ChampionshipMap : ClassMap<Championship> {
public ChampionshipMap() {
Id(x => x.Id);
HasMany(x => x.Matches);
}
}
public class MatchMap : ClassMap<Match> {
public MatchMap () {
Id(x => x.Id);
References(x => x.Championship);
}
}
Have a look at Davy Brions Blog about building your own Data Access Layer. He talks about all those sort of challenges.
For something like many-to-many with Hibernate, you define the relationship. Here's an example (reference is here:
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true" table="PersonAddress">
<key column="addressId"/>
<many-to-many column="personId"
class="Person"/>
</set>
</class>
From the database side itself, for many-to-many relationship you will usually have a link table.
So we'd have:
PERSON
ADDRESS
PERSON_ADDRESS
The PERSON_ADDRESS table would contain person_id and address_id to link the two entities together. So one person could have many addresses, and a given address could potentially belong to more than one person or company.
For a 1:m relationship, it's good enough to have this:
PERSON
ADDRESS
In address, you would have the person_id column, but there could be many address records for a given person_id, giving you the 1:m capability.
For example in DataObjects.Net you can write following to get automatically associated Championship.Matches entity set and Match.Championship persistent field.
[HierarchyRoot]
public class Championship : Entity
{
[Field, Key]
public int Id { get; set; }
[Field, Association(PairTo="Championship")]
public EntitySet<Match> Matches { get; private set; }
}
[HierarchyRoot]
public class Match : Entity
{
[Field, Key]
public int Id { get; set; }
[Field]
public Championship Championship { get; set; }
}