Fluent Nhibernate join on non foreign key property - nhibernate

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>();

Related

Spring Data JPA Query for inner join table throwing error

Spring DATA JPA question... I am trying to write a query to access the data in my sql join table.
I have my join table set up as follows:
#Entity
#Table(name="WritingCompany")
public class WritingCompany {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "companyId")
private Long id;
// private String companyName;
#ManyToMany
#JoinTable(name = "Join-WritingCo-Carrier",
joinColumns = #JoinColumn(name="writingCo"),
inverseJoinColumns = #JoinColumn(name="carrier")
)
private Set<CarrierAppointment> carriers;
//...getters and setters
}
public class CarrierAppointment {
// ...
#ManyToMany(
cascade=CascadeType.ALL,
mappedBy = "companyName"
)
private Set<WritingCompany> companies;
public Set<WritingCompany> getCompanies() {
return companies;
}
public void setCompanies(Set<WritingCompany> companies) {
this.companies = companies;
}
//...
}
I am unsure which class repository I need to write the query for... I am trying to find all the writingCo names from the join table that all have the same carrier id that matches one specific carrier.
The Join Table is set up through writing company but I feel like it should be accessed through CarrierAppointment Repository since I am matching carrier Id's.
This is what I've tried in the CarrierAppointmentrepository and it throws this error (unexpected token: Join near line 1, column 94):
#Query("Select companyName FROM WritingCompany INNER JOIN CarrierAppointment ON Join-WritingCo-Carrier.carrier= CarrierAppointment.CarrierAppointmentId")
List<CarrierAppointment> findCarrierAppoinmentFromJoin(CarrierAppointment carrier);
I've also tried:
#Query("SELECT writingCo FROM Join-WritingCo-Carrier WHERE carrier= CarrierAppointment.CarrierAppointmentId")
List<CarrierAppointment> findCarrierAppoinmentFromJoin(CarrierAppointment carrier);
Then I tried this in the writingCompanyRepository which also throws a similar error:
#Query("Select companyName FROM WritingCompany INNER JOIN CarrierAppointment ON Join-WritingCo-Carrier.carrier= CarrierAppointment.CarrierAppointmentId")
List<WritingCompany> findAllWithDescriptionQuery(CarrierAppointment carrier);
I am having a hard time understanding what this query is saying. Do I ever need to access the columns from the sql join table, or am I just querying around the join table by asking for each class that is making up the join columns in the join table? What is the right part of the statement, after INNER JOIN stating ? Could someone please provide a deeper explanation of why the query is written so I can figure out why it's not working? I've been reading a lot of inner join examples and just can't seem to figure it out.

add Nhibernate Projections

Have Employee: BaseEntity class with
public virtual BaseEntity Department {get;set;}
public virtual BaseEntity Position {get;set;}
and BaseEntity class:
public virtual long Id {get;set;}
public virtual long Name {get;set;}
How can I map my entities and /create select like this
SELECT DepartmentTable.nvarchar1 as Department, PositionTable.nvarchar1 as Position, COUNT(*) as N
FROM AllUserData EmployeeTable
left outer join AllUserData PositionTable on EmployeeTable.int2=PositionTable.tp_ID
left outer join AllUserData DepartmentTable on EmployeeTable.int3=DepartmentTable.tp_ID
WHERE EmployeeTable.tp_ListId = #p0 and PositionTable.tp_ListId = #p2 and DepartmentTable.tp_ListId = #p1
GROUP BY DepartmentTable.nvarchar1, PositionTable.nvarchar1;
I tried like this:
var criteria = session.CreateCriteria<Entities.Employee>();
criteria.CreateAlias("Position", "PositionTable", JoinType.LeftOuterJoin);
criteria.Add(Restrictions.Or(Restrictions.Eq("PositionTable.ListId", Program.PositionListGuid), Restrictions.IsNull("PositionTable.Id")));
criteria.CreateAlias("Department", "DepartmentTable", JoinType.LeftOuterJoin);
criteria.Add(Restrictions.Or(Restrictions.Eq("DepartmentTable.ListId", Program.DepartmentListGuid), Restrictions.IsNull("DepartmentTable.Id")));
criteria.Add(Restrictions.Eq("ListId", Program.EmployeeListGuid));
var projectionList = Projections.ProjectionList();
projectionList.Add(Projections.RowCount());
projectionList.Add(Projections.GroupProperty("Department"), "AliasedId");
criteria.SetProjection(projectionList);
criteria.SetResultTransformer(Transformers.AliasToBean(typeof(BaseEntity)));
var list = criteria.List<BaseEntity>();
// "More than one row with the given identifier was found" exception raised here
I'am confused, should I somehow use joined table alias for Projections.GroupProperty, rework my mapping/Entity to use only primitive types (string, int) or any other approach?
Figured out:
Add int Number to BaseEntity and string DepartmentName, string PositionName to Employee. Now I can use Projections to map Sql grouped by result to this properties:
var pl = Projections.ProjectionList()
.Add(Projections.RowCount(), GetEntityProperty(typeof (BaseEntity), "Number"))
.Add(Projections.GroupProperty("DepartmentTable.Title"), "PositionName")
.Add(Projections.GroupProperty("PositionTable.Title"), "DepartmentName");
ICriteria criteria = session.CreateCriteria<Employee>()
.CreateAlias("Position", "PositionTable", JoinType.LeftOuterJoin)
.Add(Restrictions.Eq("PositionTable.ListId", Program.PositionListGuid))
.CreateAlias("Department", "DepartmentTable", JoinType.LeftOuterJoin)
.Add(Restrictions.Eq("DepartmentTable.ListId",Program.DepartmentListGuid))
.Add(Restrictions.Eq("ListId", Program.EmployeeListGuid))
.SetProjection(pl)
.SetResultTransformer(Transformers.AliasToBean<BaseEntity>());
var list = criteria.List<BaseListEntity>().ToList();

LINQ getting Distinct values

I know there are several questions regarding this topic. However; I cannot find one that is directly related to my problem.
I have 3tables in a DB and the PK's from those 3 tables form a composite PK in a XRef table.
I need to be able to select Distinct items based on 2 of the keys just for display on a report.
public IEnumerable<AssemblyPrograms> GetProgramAssemblies()
{
var assembliesList = (from c in eModel.Assemblies.ToList()
join d in eModel.Programs_X_Assemblies_X_Builds
on c.AssemblyID equals d.AssemblyID
join p in eModel.Programs
on d.ProgramID equals p.ProgramID
join a in eModel.AssemblyTypes
on c.AssemblyTypeID equals a.AssemblyTypeID
select new AssemblyPrograms
{
AssemblyID = c.AssemblyID
,ProgramID = d.ProgramID
,AssemblyName = c.AssemblyName
,AssemblyPrefixName = c.AssemblyPrefixName
,ProgramName = p.ProgramName
,AssemblyTypeName = a.AssemblyTypeName
,AssemblyTypeID = a.AssemblyTypeID
});
return assembliesList;
}
This is my query and what I need to pull out of the tables
In my XRef table I have AssemblyID, ProgramID and BuildID as my composite PK.
There can be a many-many relationship from AssemblyID to ProgramID. The BuildID is the key that separates them.
I need to pull Distinct AssemblyID to ProgramID relationships for my report, the BuildID can be ignored.
I have tried .Distinct() in my query and a few other things to no avail.
I would appreciate any help anyone could give me.
Thanks
How about a Distinct overload that accepts a custom equality comparer? Something like this:
class AssemblyComparer : EqualityComparer<AssemblyPrograms> {
public override bool Equals(AssemblyPrograms x, AssemblyPrograms y) {
return x.ProgramID == y.ProgramID && x.AssemblyID == y.AssemblyID;
}
public override int GetHashCode(AssemblyPrograms obj) {
return obj.ProgramID.GetHashCode() ^ obj.AssemblyID.GetHashCode();
}
}

NHibernate: Return a referenced property without lazy loading using SetProjection

I have the following two classes mapped:
public class Foo
{
public virtual Guid Id { get; set; }
public virtual Bar Bar { get; set; }
}
public class Bar
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
}
I have the following Criteria:
return Session.CreateCriteria<Foo>("f")
.CreateAlias("f.Bar", "b")
.SetProjection(Projections.Property("f.Bar"))
.List<Bar>();
This generates the following SQL:
select b.Id from Foo f
inner join Bar on f.BarId = b.Id
Notice how only the Id of Bar is returned, rather than the entire class. How do I get all the columns of Bar instead?
The solution depends on your needs.
First of all if you need to return entity Bar, then your initial Criteria must be of type Bar so you just:
session.CreateCriteria<Bar>().
.List<Bar();
If you need to add some restrictions based on Foo then there are two ways.
Use HQL
session.CreateQuery(
"select b " +
"from Foo f " +
"inner join f.Bar b " +
"where f.Id = 9 ")
.List();
Use query only property. Do this by adding access="noop" in your Bar mapping file.
<many-to-one name="foo" access="noop" class="Foo" column="FooFk"/>
Note that your domain model doesn't have to change! You don't need to add that "foo" property/field in the Bar class.
Now you can use that property in your queries like:
session.CreateCriteria<Bar>()
.CreateAlias("foo", "f")
.Add(Restrictions.Eq("f.Id", 9))
.List<Bar>();
If it doesn't fit into a single criteria, use DetachedCriteria:
var subquery = DetachedCriteria.For<Foo>("f")
.SetProjection(Projections.Property("f.Bar"))
// more filter criteria ...
return session.CreateCriteria<Bar>
.SetProjection(Subqueries.PropertyIn("id", subquery));
which creates sql like this:
select Bar.*
from Bar
where Bar.id in (Select b.id from Foo f inner join Bar b on ...)
Is obviously only make sense if you have some filter criteria base on Foo in the subquery.
If the above detached criteria query doesnt work out here is a simple HQL query that should do the trick.
var hqlQuery="select b from Foo as f inner join f.Bar as b";
Now run this query as follows:
Session.CreateQuery(hqlQuery).List<Boo>();
you can now add where condition to your query too if you want.
Hope this helps.. I can tell you how to do it with Criteria but I think this is a little easier for you to ustand as u seem to be comfortable with SQL.

NHibernate Polymorphic Query on a Collection

I'm trying to write a query in NHibernate. I don't really care if I use the Criteria API or HQL, I just can't figure out how to write the query.
Here's my model:
public class LogEntry { public DateTime TimeCreated { get; set; } }
public class Note : LogEntry { public string Content { get; set; } }
public class Workflow { public IList<LogEntry> Log { get; set; } }
I want the query to return all Workflows that which contain a Note with specific words in the Content of the note.
In pseudo-SQL, I'd write this like:
select w.*
from Workflow w
join w.Log l where l is class:Note
where (Note)l.Content like '%keyword%'
I'm not sure about the Criteria API, but HQL seems to handle polymorphic queries quite well, even when searching on a property that only exists in a specific sub-class. I would expect the following to work:
from Workflow w join w.Log l where l.class = Note and l.Content like '%keyword%'
I don't know if there is a better way, but I use subqueries for this:
from Workflow w
join w.Log l
where l in (
select n
from Note n
where n.Content like '%keyword%'
)
(if this doesn't work, write l.id in (select n.id...)
In criteria, you can directly filter properties that are only available on a subclass, but you shouldn't, because it only filters for the first subtype it finds where this property is defined.
I use subqueries as well:
DetachedCriteria subquery = DetachedCriteria.For<Note>("n")
.Add(Expression.Like("n.Content", "%keyword%"))
.SetProjection(Projections.Property("n.id"));
IList<Workflow> workflows = session.CreateCriteria<Workflow>("w")
.CreateCriteria("w.Log", "l")
.Add(Subqueries.PropertyIn("l.id", subquery))
.List<Workflow>();