Inner join fetch in Linq query - nhibernate

Is it possible to modify a query (or the mapping) so that the call to the .Fetch() extension method of NHibernate results in a inner join in the generated SQL query?
In HQL this is easy, you just have to write inner join fetch instead of left join fetch, but I couldn't find that for Linq.

Since this isn't possible yet, I have created a Jira issue for it: NH-2790

UPDATE 2:
https://www.nuget.org/packages/NHibernate.Linq.InnerJoinFetch
Enjoy!!!
UPDATE:
I finally managed to do this and sent the followign pull request to the NH team
https://github.com/nhibernate/nhibernate-core/pull/210
In Fact, it is possible but you need to hack NHibernate, the line to change is this one
tree.AddFromClause(tree.TreeBuilder.LeftFetchJoin(join, tree.TreeBuilder.Alias(alias)));
into
tree.AddFromClause(tree.TreeBuilder.FetchJoin(join, tree.TreeBuilder.Alias(alias)));
at the ProcessFetch.cs file
It seems the behavior is hardcoded for Linq and I think its because its using the extension methods in order to send what to use for the DefaultQueryProvider and re-linq processing, I still need to figure out how to specify the kind of join you want to do on eager fetching using the linq api but I'm working on it, hopefully I'll send a pull request to the NH team so they can put this into the codebase, for now in my project I'll use my custom NH version

Not sure if you can control the JOIN type in LINQ but you can still have strongly typed queries controlling the JOIN type using the QueryOver API.
var ordersWithCustomers = Session.QueryOver<Order>()
.JoinQueryOver(o => o.Customer,JoinType.InnerJoin) //You can specify join type
.List();
Also try adding not-null="true" in your mapping, although I just tried this and it didn't make a difference to the join type in the query.
Wierdly this works for the inner join (using the old ANSI SQL joining syntax) but doesn't fetch the Customer.
var ordersWithCustomers =
from o in Session.Query<Order>()
join c in Session.Query<Customer>() on o.Customer.Id equals c.Id
select o;

Related

Many to many relation, multi where clause on the same column and hibernate

Sorry for the bad question title, couldnt think of anything better.
Anyway, my tables are Tags - Poststags - Posts. Poststags is a junction table for many to many relation. I need to select all posts with given tags, I dont know how many tags the user will choose to search for. One way I found to do this is in the code below, however i would need to loop all the tags given by the user and construct the query string from there since the number of tags is unknown. Seems like a pretty bad solution to me.
Another solution would be to store all tags in one column in the Posts table as a pure string, but I dont want to do that because of other application requirements.
I have a working sql query, since I was trying pure sql before trying to implement it in hibernate, but I dont like doing a select of all posts containing each tag and then joining each query, is there a way to specify the same column multiple times in the WHERE clause? Something along the lines WHERE pt.tag_id = x AND pt.tag_id = y? (I know this won't work). IN operator won't work either since it will give me Posts that contain any of the supplied tags and not just the posts containing ALL of the supplied tags.
Also how would I implement such a query in HQL(if subqueries like this are even supported?). Or can I somehow manage this via criteria? Or do I have to resort to using createSQLQuery method of a hibernate session?
SELECT * FROM
( SELECT * FROM posts p
inner join poststags pt on pt.post_id = p.id
WHERE pt.tag_id = 1 ) AS A
INNER JOIN
( SELECT * FROM posts p
inner join poststags pt on pt.post_id = p.id
WHERE pt.tag_id = 2 ) AS B ON A.id = B.id
And yes, I know this query is not returning the Post entity itself, but I can handle that later.
Don't use hibernate or ORM for this kind of complex select, it may work, but in a bad way.
Your use case should be solved by full text search, which means each Post will need have its own tags.
I don't see much value to make Tag an entity. It's just a string.
Full text search could be heavy for database , A better way is using elasticsearch to help. Spring has integration with spring-data-elasticsearch and it's not difficult to use. Elasyicsearch is very powerful for free text search.
Here is a solution that 'should' work using Criteria queries in Hibernate.
Assuming that you have an entity for Post and an entity for PostTag and PostTag has reference to Post (which I think it should given the example query that you provided), I believe that something like this should do what you want:
static DetachedCriteria getPostTagCriteria(String tagString)
{
DetachedCriteria criteria = DetachedCriteria.forClass(PostTag.class, "uniqueName_" + postTagId);
criteria.createAlias("tag", "tag");
criteria.add(Restrictions.eq("tag.tagString", tagString));
criteria.setProjection(Projections.property("postId"));
return criteria;
}
static List<Post> getPosts(List<String> tagStrings)
{
Criteria criteria = getCurrentSession().createCriteria(Post.class, "post");
for(String tagString : tagStrings)
{
criteria.add(Property.forName("post.id").in(getPostTagCriteria(tagString)));
}
List<Post> ret = criteria.list();
return ret;
}
This assumes that you have reasonable entities to represent Post, PostTag and Tag and that they all reference each other in obvious parent/child sort of ways that I have completely made up here.
But, the general idea of creating multiple detached criteria objects based on your input should solve your problem. This solution also comes with the same caveats regarding SQL complexity mentioned above. You will be creating a sub-query for each tag passed in. So, depending on your indexes and table sizes, you may need to consider a different approach.

ORMlite join and order by over three tables

I have a many to many relationship and am trying to order by the one side. So in SQL this would be:
select * from
patient join patientuserrelation on patient.id=patientuserrelation.p_id
join user on patientuserrelation.u_id=user.id
order by user.name
Which I have implemented in Ormlite as:
QueryBuilder<Visit, String> qbVisit = setupAccess(Visit.class)
.queryBuilder();
QueryBuilder<UserVisitRelation, String> qbUserVisitRelation = setupAccess(
UserVisitRelation.class).queryBuilder();
QueryBuilder<User, String> qbUser = setupAccess(User.class)
.queryBuilder();
qbUser.orderBy(sortByThisColumn, true);
qbUserVisitRelation.join(qbUser);
qbVisit.join(qbUserVisitRelation);
return qbVisit.distinct().query();
However, this does not work. The results are not ordered at all. I could try to use rawSQL and rawRowMapper but that bloat up my code.
There is a similar question here: ORMLITE order by a column from another table. Unfortunately with no answer. Is there a helpful expert around?
Ok, to answer my own question for posterity: It seems like join and order across multiple tables is not supported in ormlite 4.48. If you think about it for a while you figure out why this is probably the case. Anyway, the solution is to write a raw sql statement, only select the necessary columns WITHOUT foreign collections and cast it to your object using RawRowMapper and GenericRawResults. Not what you like to do when using an ORM, but OK.

NHibernate Query parent and child objects eagerly without join

I have simple domain with Order and OrderLines. Is it possible to load the Order and associated OrderLine objects without a join? I'm trying to use the Future/FutureValue to perform two simple queries. I'm hoping that NHibernate knows how to combine these in the cache. I'm using NHibernate 3.2 with code only mapping.
So far here is what I have:
// Get all the order lines for the order
var lineQuery = session.QueryOver<OrderLine>()
.Where(x => x.WebOrder.Id == id).Future<OrderLine>();
// Get the order
var orderQuery = session.QueryOver<WebOrder>()
.Where(x => x.Id == id)
.FutureValue<WebOrder>();
var order = orderQuery.Value;
This works as expected sending two queries into the database. However when I use loop to go through order.OrderLines NHibernate send another query to get the order lines. My guess is that because I'm using constraints (Where(x => ...) NHibernate doesn't know how to get the objects from the session cache.
Why do I want to do this without join
I know I can use Fetch(x => x.OrderLines).Eager but sometimes the actual parent (in this case Order) is so large that I don't want to perform the join. After all the result set contains all the order columns for each orderline if I perform the join. I don't have any raw numbers or anything I'm just wondering if this is possible.
it's quite possilbe. see nHib's fetching strategies.
you can choose either 'select' (if you're only dealing with one Order at a time) or 'subselect'.

nhibernate linq: projection to DTO and columns

I've recently upgraded my Linq provider to the new AST one. (NH3 on NuGet)
With the previous provider I was using linq to do "inline projections
to my DTO"
e.g.
from o in Session.Query<MyObject>()
select new MyObjectDTO {
Name = o.Name,
SubName = o.OtherObject.Name,
Sub2NAme = o.OtherObject2.Name
}
and this would generate a
SELECT o.Name, sn1.Name, sn2.Name FROM .....
JOIN.... JOIN....
statement.
Once I upgraded my provider I found a lot of select statements being
fired off. (My projected object is more complex than above).
I have come accross Fetch/FetchMany, which might help with the number
of queries, but as far as I can tell it means the full object will
come back for each flattened field I require.
Is there a way I can get the smallest possible number of columns required for the projection to be selected, rather than loading the full object graph to the project with?
Thanks,
Chris
It must be something with your usage of the result (like iterating many times the IQueryable), something odd with the mappings, or some complexity that was removed from the example.
I just tried that exact query, and only one SQL statement was generated.

(N)Hibernate Auto-Join

I'm developing a web- application using NHibernate. Can you tell me how to write a NHibernate Query for the following SQL query:
SELECT v1.Id
FROM VIEW v1
LEFT JOIN VIEW v2 ON v1.SourceView = v2.Id
ORDER BY v1.Position
It's basically a auto-join but I don't know how to write this in Nhibernate. Lets say the property names are the same as the table column names.
You could just perform the select on the original entity and make the association between the two objects "lazy = false". As long as the entities are mapped then both will be returned and you wont get a lazyloadingexception when trying to access the object.
If you don't want to map "lazy=false" then you can also iterate through the results and perform some sort of operation (such as asking if it is null; if(v1.AssocatedObject == null){}) to ensure the data is loaded while the session is open.
Update:
I think there is actually a better one than that in, NHibernateUtil.Initialise() that can initialise a collection without having to wander through it.