NHibernate is not fetching Joins - nhibernate

I have an entity that is defined in two tables with fluent nhibernate
Table one:
Employee
---------
Id,
Name
Table Two:
Salaries
--------
Employee_Id,
Salary
On Fluent NHibernate I defined it like this:
EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Table("Employee");
Map(x => x.Id);
Map (x => x.Name);
Join ("Salaried", m =>
{
m.Map (x => map.Salary);
m.KeyColumn("EmployeeId");
});
}
}
When I do a Session.Get like the following:
Employee e = session.Get<Employee>(employeeId);
Then I got all the details of Employee, except the columns coming from the "Salaries" table
Any idea?

I'd suggest that you use the HasOne method like so:
EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Table("Employee");
Map(x => x.Id);
Map (x => x.Name);
HasOne(x=>x.Salary).PropertyRef(r=>r.EmployeeId);
}
}

The Employee is being lazy loaded so NHibernate will only call out to the database if you access the Salary property.

The table and KeyColumn names are wrong in the join mapping. Should be:
Join ("Salaries", m =>
{
m.Map (x => map.Salary);
m.KeyColumn("Employee_Id");
});

Related

nHibernate One to One property null when loaded

I have a One to One relation between a TimeRecord and the Location.
This implementation is exactly the same es described in documentation:
https://github.com/jagregory/fluent-nhibernate/wiki/Fluent-mapping
public class TimeRecordMap : ClassMap<TimeRecord>
{
public TimeRecordMap()
{
Id(x => x.Id);
Map(x => x.Description);
Map(x => x.StartTime);
Map(x => x.EndTime);
HasOne(x => x.Location).Cascade.All();
}
}
public class LocationMap : ClassMap<Location>
{
public LocationMap()
{
Id(x => x.Id);
Map(x => x.Longitude);
Map(x => x.Latitude);
Map(x => x.Adress);
References(x => x.TimeRecord).Unique();
}
}
Now I query my TimeRecords with the following method:
public IList<TimeRecord> GetTimeRecords(string userid)
{
var query = Session.Query<TimeRecord>().Where(tr => tr.User.Id == userid);
return query.ToList();
}
Unfortunalelty my Location object is always null even if there is a coresponding entry in Location table but when I query for the coresponding Location with the desired TimeRecordId it is returned correctly.
See code here (code is inside a loop -> trCurrent is the current object in list received from "GetTimeRecords")
Location location = _locationRepo.getLocationByTimeRecordId(trCurrent.Id);
//trCurrent.Location = location; <- don't want to do it that way
if (trCurrent.Location != null)<- always null
{
... do stuff here
}
Implementation of my LocationRepository method:
public Location getLocationByTimeRecordId(int timeId)
{
var query = Session.Query<Location>()
.Where(tr => tr.TimeRecord.Id == timeId && tr.IsDeleted == false);
List<Location> lstReturn = query.ToList();
if (lstReturn.Count() == 0)
{
return null;
}
else
{
return lstReturn.First();
}
}
Can someone tell me why my Location is not resolved corretly?
Cheers,
Stefan
People claim that
HasOne / one-to-one is usually reserved for a special case. Generally, you'd use a References / many-to-one relationship in most situations (see: I think you mean a many-to-one). If you really do want a one-to-one, then you can use the HasOne method.
If you really do want a one-to-one and use it, you should remember that entities are joined by their ids by default.
If you check generated SQL you'll see something like JOIN Location ON Location.Id = TimeRecord.Id.
In order to get SQL like JOIN Location ON Location.TimeRecordId = TimeRecord.Id you should specify the foreign key via PropertyRef() method. So your mapping could be the folloving:
public class TimeRecordMap : ClassMap<TimeRecord>
{
public TimeRecordMap()
{
Id(x => x.Id);
Map(x => x.Description);
Map(x => x.StartTime);
Map(x => x.EndTime);
HasOne(x => x.Location).Cascade.All().PropertyRef(it => it.TimeRecord);
}
}
public class LocationMap : ClassMap<Location>
{
public LocationMap()
{
Id(x => x.Id);
Map(x => x.Longitude);
Map(x => x.Latitude);
Map(x => x.Adress);
References(x => x.TimeRecord/*, "TimeRecordId"*/).Unique().Not.Nullable();
}
}
In order to make sure that any location has TimeRecord you can add .Not.Nullable() into your LocationMap class.

Fluent Nhibernate Many-to-Many mapping with extra column, update

Can not update / insert data into the database
This is the tables I have:
Role (Id, Name, ...)
Privilege (Id, Name, ...)
RolePrivilege (Id, RoleId, PrivilegeId, IsAllowed)
I have map:
public class RoleWithPrivilegesEntityMap : ClassMap<RoleWithPrivilegesEntity>
{
public RoleWithPrivilegesEntityMap()
{
Table( "ROLE" );
Not.LazyLoad();
Id( x => x.Id, "ID" ).GeneratedBy.Identity();
Map( x => x.Name, "NAME" ).Not.Nullable().Length( 100 );
HasMany(x => x.RolePrivileges)
.Cascade.AllDeleteOrphan()
.Inverse()
.Fetch.Join().KeyColumn("RoleId");
}
}
public class PrivilegeEntityMap : ClassMap<PrivilegeEntity>
{
public PrivilegeEntityMap()
{
Table("PRIVILEGE");
Id(x => x.Id, "ID").GeneratedBy.Identity();
Map(x => x.Name, "NAME").Not.Nullable();
HasMany(x => x.RolePrivileges)
.Cascade.AllDeleteOrphan()
.Inverse()
.Fetch.Join().KeyColumn("PrivilegeId");
}
}
public class RolePrivilegeEntityMap : ClassMap<RolePrivilegeEntity>
{
public RolePrivilegeEntityMap()
{
Table("ROLEPRIVILEGE");
Id(x => x.Id, "R_ID").GeneratedBy.Identity();
Map(x => x.IsAllowed, "ISALLOWED").Not.Nullable();
References(x => x.Role)
.Not.Nullable()
.Column("R_ID");
References(x => x.Privilege)
.Not.Nullable()
.Column("P_ID");
}
}
I'm trying to update existing record
session.SaveOrUpdate(rolePrivilege)
database make insert into table RolePrivilege, but I need to at first delete record.
what am I doing wrong?
normally nothing will go to the database at all because you never call session.Flush() or transaction.Commit(). However with Id generation Identity you basicly force NHibernate to insert immediately hence you see the INSERT statement.
You can also get rid of the Id in RolePrivilegeEntity and form a compositeId over Role and Priviledge which would also enhance performance (no need to insert immediatly).

NHibernate inserts twice to base class when saving subclass. Violates unique constraint

I am using fluent nhibernate with a legacy oracle db, and Devart Entity Developer to generate mapping and entity classes.
I have a base table Product, which has several subclasses, including Tour. When saving Tour, nhibernate issues 2 identical inserts to the Product table which violates PK unique constraint.
Product mapping is:
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Schema("CT_PRODUCTS");
Table("PRODUCT");
OptimisticLock.None();
LazyLoad();
CompositeId()
.KeyProperty(x => x.Season, set =>
{
set.Type("CT.DomainKernel.Enums.Season,CT.DomainKernel");
set.ColumnName("SEASON");
set.Access.Property();
})
.KeyProperty(x => x.ProductCode, set =>
{
set.ColumnName("PROD_CODE");
set.Length(10);
set.Access.Property();
});
Map(x => x.Name)
.Column("NAME")
.Access.Property()
.Generated.Never()
.CustomSqlType("VARCHAR2")
.Length(200);
HasOne(x => x.Tour)
.Class<Tour>()
.Access.Property()
.Cascade.SaveUpdate()
.LazyLoad();
}
}
Tour mapping is:
public class TourMap : SubclassMap<Tour>
{
public TourMap()
{
Schema("CT_PRODUCTS");
Table("TOUR");
LazyLoad();
KeyColumn("SEASON");
KeyColumn("PROD_CODE");
Map(x => x.Duration)
.Column("DURATION")
.Access.Property()
.Generated.Never()
.CustomSqlType("NUMBER")
.Not.Nullable()
.Precision(3);
HasOne(x => x.Product)
.Class<Product>()
.Access.Property()
.Cascade.SaveUpdate()
.LazyLoad()
.Constrained();
}
}
Tour Entity class:
public partial class Tour2 : Product
{
public virtual Product Product
{
get
{
return this._Product;
}
set
{
this._Product = value;
}
}
}
Any Ideas as to what is going wrong?
The solution to this was to Remove the Property references from Tour to Product, and from Product to Tour, which when thought about, make no sense anyway.

fluent nhibernate mapping

I am trying to create a map to get results as like from below query.
I am having hard time to get set Product mapping to set References to Product_Line on 3 columns as in where condition. How can I achieve this using fluent?
Product table: cId, ProjID, Line, etc., columns
Product_Line table: cId, ProjID, Line, etc., columns
select f.* from Product f
join Product_Line v on f.cId = v.CId and f.ProjID = v.ProjID and f.line = v.line
Thanks in Advance.
RajeshC
First, thank you for looking into it and Here with more info:
//Req: I want to query product such that if there is NO ProductLine, then I want to create a ProductLine, if there is one, then I'll update it.
public class ProductMap : ClassMap<Product>
{
Id(x => x.Id);
Map(x => x.CustomerId, "CustId");
Map(x => x.ProjId, "PROJId");
Map(x => x.LineNumber, "LineNumber");
Map(x => x.ReportType, "ReportType");
// Reference to Product_Line? - this reference should be based on three columns (custId, ProjId, LineNumber)
References(x => x.Line);
}
public class ProductLineMap : ClassMap<ProductLine>
{
Table("Product_Line");
Map(x => x.CustomerId, "CustId"); //same column as Product.CustId
Map(x => x.ProjId, "PROJId"); //Same as Product.ProjId
Map(x => x.LineNumber, "LINENUMBER"); //Same as Product.LineNumber
//etc.,
//for me, this reference is not needed as I need from Product to ProductLine - one way.
//References(x => x.Product).Column("ProjId") //
}
We could give you a much better answer if you showed us your C# code and wrapped the SQL in < code > tags... Here's my guess at what I think you want:
public class ProductMap : ClassMap<Product>
{
Id(x => x.Id);
References(x => x.Line); // Reference to Product_Line?
// etc.
}
public class ProductLineMap : ClassMap<ProductLine>
{
Table("Product_Line");
Id(x => x.Id).Column("cId");
References(x => x.Product).Column("ProjId")
}

Fluent Nhibernate left join

I want to map a class that result in a left outer join and not in an innner join.
My composite user entity is made by one table ("aspnet_users") and an some optional properties in a second table (like FullName in "users").
public class UserMap : ClassMap<User> {
public UserMap() {
Table("aspnet_Users");
Id(x => x.Id, "UserId").GeneratedBy.Guid();
Map(x => x.UserName, "UserName");
Map(x => x.LoweredUserName, "LoweredUserName");
Join("Users",mm=>
{
mm.Map(xx => xx.FullName);
});
}
}
this mapping result in an inner join select so no result come out is second table as no data. I'd like to generate an left join.
Is this possible only at query level?
Try the Optional() method.
Join("Users", m =>
{
m.Optional();
m.Map(x => x.FullName);
});
Only this did work for me (NH 3.3):
Join("OuterJoinTable",
m =>
{
m.Fetch.Join().Optional();
m.KeyColumn("ForeignKeyColumn");
m.Map(t => t.Field, "FieldName");
});