Fluent NHibernate join table mapping - nhibernate

Reverse engineering an existing database to map with N-Hibernate using Fluent N-Hibernate.
How can I map this?
Address table
Id
Address1
Address2
Person table
Id
First
Last
Types
Id
TypeName
PersonAddress table (A person can have home, business etc addresses)
Id
PersonId (Id from person table)
AddressId (Id from address table)
TypeId (Id from types lookup table HOME, BUSINESS etc..)
Any help would be great. Thanks
Here's another tricky one in addition to above mapping. Don't know how easy it would be to map it.
Party Table
Id
Person Id points to Person
Identifiers Tables
Id
Party Id
Type Id
Identifier value
Employee table
Employee Id No party or person table has foreign key to this table. The employee id is stored in the
identifiers table. so for e.g. The identifier table is used store values for different types. The identifiers for a given party could DriverLicense, EmployeeId, SSN, Credit Card numeber etc, this could be many values.
Sample identifier data
Id, PartyId, TypeId, IdentifierValue
1 , 1, 1, EMPLID-1234
2 , 2, 1, EMPLID-4567
3 , 3, 1, EMPLID-34354
I am trying to get my head around this and just can't get it to mapped.

// this answer assumes you have functional Address, Person, Type, and PersonAddress objects.
public class AddressMap : ClassMap<Address>
{
public AddressMap()
{
Id(x=>x.Id);
Map(x=>x.Address1);
Map(x=>x.Address2);
}
}
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Id(x=>x.Id);
Map(x=>x.First);
Map(x=>x.Last);
}
}
public class TypeMap : ClassMap<Type>
{
public TypeMap()
{
Id(x=>x.Id);
Map(x=>x.TypeName);
}
}
public class PersonAddressMap : ClassMap<PersonAddress>
{
public PersonAddressMap()
{
Id(x=>x.Id);
References(x=>x.Person, "PersonId");
References(x=>x.Address, "AddressId");
References(x=>x.Type, "TypeId");
}
}

Related

Fluent Nhibernate join on non foreign key property

I can't find this anywhere, but it seems pretty trivial. So, please excuse if this is a duplicate.
I have something like:
public class Doctor : Entity
{
...some other properties here...
public virtual string Email { get; set; }
}
public class Lawyer : Entity
{
...some other properties here...
public virtual string Email { get; set; }
}
I want to return all doctors where there is no email match in the Lawyers table like:
select * from Doctors d
where d.Email not in
(select l.Email from Lawyers l where l.Email is not null)
or using a join:
select d.* from Doctors d
left join Lawyers l on l.Email = d.Email
where l.Email is null
The problem is that the Email is of course not set up as a foreign key. I have no mapped property on the Doctor entity that maps to Lawyer.
What I've tried so far:
ICriteria criteria = Session.CreateCriteria(typeof(Doctor))
.CreateAlias("Lawyers.Email", "LawyerEmail", JoinType.LeftOuterJoin)
.Add(Restrictions.IsNull("LawyerEmail"));
return criteria.List<Doctor>();
But, I get a "cannot resolve property Lawyer of MyPlatform.MyNamespace.Doctor" error. Any ideas how to set up my DoctorMap and adjust the criteria tomfoolery to achieve this?
NHibernate for the loss........Entity Framework for the win....
We can achieve that with a feature called subquery:
// a inner SELECT to return all EMAILs from Lawyer table
var subQuery = DetachedCriteria.For<Lawyer>()
.SetProjection(Projections.Property("Email"));
// the root SELECT to get only these Doctors
var criteria = session.CreateCriteria<Doctor>();
// whos email is not in the sub SELECT
criteria.Add(Subqueries.PropertyNotIn("Email", subQuery));
// get first 10
var result = criteria
.SetMaxResults(10)
.SetFirstResult(0) // paging
.List<Doctor>();

How to avoid ImprovedNamingStrategy in joinTable in Grails

I have a legacy database which I can't change and I have this setup
class Foo {
static hasMany = [bars:Bar]
static mapping = {
version false
columns {
id column: "FooId"
color column: "FooColor"
bars joinTable: [name: "FooBar", key: 'FooId', column: 'BarId']
}
transient
def getBarName(){
((Bar)this.bars.toArray()[0]).name
}
}
class Bar {
static hasMany = [foos:Foo]
static belongsTo = [Foo, Baz]
static mapping = {
version false
columns {
id column: "BarId"
name column: "BarName"
}
}
When i try to access the method getBarName() in a controller Hibernate translates the inverse column name to "bar_id". Is there some way to set up a mapping like the one for the id and property columns?
And on a side note. How do i correctly implement getBarName()? Thacan't possibly be the correct implementation...
*EDIT*
----------------------------------------------------------------------
Apparently I was unclear above. The thing is that i already have a join column which has the form
-------------------
|RowId|FooId|BarId|
-------------------
| 1 | abc | 123 |
-------------------
Benoit's answer isn't really applicable in this situation since I want to avoid having a domain object for the joinTable.
*EDIT 2*
----------------------------------------------------------------------
Solved it. Dont understand it though... But split the join table information between the two domain classes and it works...
class Foo {
static hasMany = [bars:Bar]
static mapping = {
version false
columns {
id column: "FooId"
color column: "FooColor"
bars joinTable: [name: "FooBar", key: 'FooId']
}
transient
def getBarName(){
((Bar)this.bars.toArray()[0]).name
}
}
class Bar {
static hasMany = [foos:Foo]
static belongsTo = [Foo, Baz]
static mapping = {
version false
columns {
id column: "BarId"
name column: "BarName"
bars joinTable: [name: "FooBar", key: 'BarId']
}
}
As stated in the documentation Many-to-One/One-to-One Mappings and One-to-Many Mapping :
With a bidirectional one-to-many you can change the foreign key column
used by changing the column name on the many side of the association
as per the example in the previous section on one-to-one associations.
However, with unidirectional associations the foreign key needs to be
specified on the association itself.
Thye given example is:
class Person {
String firstName
static hasMany = [addresses: Address]
static mapping = {
table 'people'
firstName column: 'First_Name'
addresses column: 'Person_Address_Id'
}
}

Fluent NHibernate: Custom ForeignKeyConvention not working with explicitly specified table names

EDIT: for the tl;dr crowd, my question is: How do I access the mappings from inside the ForeignKeyConvention in order to determine the table name that a given type is mapped to?
The long version:
I am using Fluent NHibernate to configure NHibernate, and I have a custom foreign key convention that is failing when I alias tables and columns.
My tables use a convention where the primary key is always called "PK", and the foreign key is "FK" followed by the name of the foreign key table, e.g., "FKParent". For example:
CREATE TABLE OrderHeader (
PK INT IDENTITY(1,1) NOT NULL,
...
)
CREATE TABLE OrderDetail (
PK INT IDENTITY(1,1) NOT NULL,
FKOrderHeader INT NOT NULL,
...
)
To make this work, I've built a custom ForeignKeyConvention that looks like this:
public class AmberForeignKeyConvention : ForeignKeyConvention
{
protected override string GetKeyName( Member member, Type type )
{
if ( member == null )
return "FK" + type.Name; // many-to-many, one-to-many, join
return "FK" + member.Name; // many-to-one
}
}
This works so long as my entities are named the same as the table. But it breaks when they aren't. For example, if I want to map the OrderDetail table to a class called Detail, I can do so like this:
public class DetailMap : ClassMap<Detail>
{
public DetailMap()
{
Table( "OrderDetail" );
Id( o => o.PK );
References( o => o.Order, "FKOrderHeader" );
...
}
}
The mapping works for loading a single entity, but when I try to run any kind of complicated query with a join, it fails, because the AmberForeignKeyConvention class is making incorrect assumptions about how the columns are mapped. I.e., it assumes that the foreign key should be "FK" + type.Name, which in this case is Order, so it calls the foreign key "FKOrder" instead of "FKOrderHeader".
So as I said above: My question is, how do I access the mappings from inside the ForeignKeyConvention in order to determine a given type's mapped table name (and for that matter, their mapped column names, too)? The answer to this question seems to hint at the right direction, but I don't understand how the classes involved work together. When I look through the documentation, it's frightfully sparse for the classes I've looked up (such as the IdMapping class).
the idea is to load the mappings
public class AmberForeignKeyConvention : ForeignKeyConvention
{
private static IDictionary<Type, string> tablenames;
static AmberForeignKeyConvention()
{
tablenames = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => typeof(IMappingProvider).IsAssignableFrom(t))
.ToDictionary(
t => t.BaseType.GetGenericArguments()[0],
t => ((IMappingProvider)Activator.CreateInstance(t)).GetClassMapping().TableName);
}
protected override string GetKeyName( Member member, Type type )
{
return "FK" + tablenames[type]; // many-to-one
}
}

nhibernate manytomany query

I am new to nhibernate and trying to create a query on a database with manytomany links between items and categories.
I have a database with 3 tables : items, categories and a lookup table categoryitem like this:
categorys - primary key categoryId
items - primary key itemId
categoryItem - categoryId column and itemId column
I want a query returning items for a particular category and have tried this and think i am along the right lines:
public IList<Item> GetItemsForCategory(Category category)
{
//detached criteria
DetachedCriteria itemIdsCriteria = DetachedCriteria.For(typeof(Category))
.SetProjection(Projections.Distinct(Projections.Property("Item.Id")))
.Add(Restrictions.Eq("Category.Id", category.Id));
criteria.Add(Subqueries.PropertyIn("Id", itemIdsCriteria));
return criteria.List<Item>() as List<Item>;
}
I only have business objects for category and item.
how do i create a repository method to find items for a particular category?
I assume that your classes look like this:
class Item
{
// id and stuff
IList<Category> Categories { get; private set; }
}
class Category
{
// id and stuff
}
query (HQL)
session.CreateQuery(#"select i
from Item i
inner join i.Categories c
where
c = :category")
.SetEntity("category", category)
Criteria
session
.CreateCriteria(typeof(Item))
.CreateCriteria("Categories", "c")
.Add(Restrictions.Eq("c.Id", category.Id))

NHibernate does not filter data base on COORECT TYPE of meta data

I have an interface (IContactable) which is realizing by 3 classes : Person, Department, RestUnit
public interface IContactable
{
Contact Contact { get; set; }
string Title { get; }
int? Id { get; set; }
}
public class Person:IContactable
public class Department:IContactable
public class RestUnit:IContactable
There is another class, Contact, which should maintain which one of these objects are the owner of the contact entity.
A part of Contact mapping which does the job is:
ReferencesAny(p => p.Contactable)
.EntityTypeColumn("ContactableType")
.EntityIdentifierColumn("ContactableId")
.IdentityType<int>()
.AddMetaValue<Person>("Person")
.AddMetaValue<Department>("Department")
.AddMetaValue<RestUnit>("RestUnit");
So that Contact records in database would be like (The types are being saved as string):
X Y ContactableType ContactableId
... ... Person 123
... ... Person 124
... ... Department 59879
... ... RestUnit 65
... ... Person 3333
... ... Department 35564
Everything works just fine but filtering data. When I want to get some particular Contacts, say with Department type, I would write something like :
var contacts = Repository<Contact>.Find(p=>p is Department);
Nhibernate tries to filter data based on ContactableType field with an integer value but the ContactableType column is nvarchar
Generated query by NHibernate :
select .......... from contact.[Contact] where ContactableType=1
Expected query:
select .......... from contact.[Contact] where ContactableType='Department'
So NHibernate kinda using a wrong type. int instead of string.
I think NH is using the index of the object in list which AddMetaValue("Department") has added department type into...
I hope the explanation would be clear enough
I'm using NH3....
any idea?
Have you tried to add an extra line:
ReferencesAny(p => p.Contactable)
.MetaType<string>()