I have an issue with Linq to Nhibernate producing queries with outer joins.
For example:
return Session.Linq<ClientContact>().Where(c => c.Client.Id = 13).ToList();
Produces a query similar to:
SELECT...
FROM mw_crafru.clientcontact this_,
mw_crafru.client client1_,
mw_crafru.relationshiptype relationsh4_
WHERE this_.clientid = client1_.clientid(+)
AND this_.relationshiptypeid = relationsh4_.relationshiptypeid
AND client1_.clientid = :p0;:p0 = 13
Notice the outer join this.clientid = client1.clientid(+). Boo!
To resolve the issue I went back to using Session.CreateCriteria.
return Session.CreateCriteria(typeof (ClientContact)).Add(Restrictions.Eq("Client.Id", clientId)).List<ClientContact>();
Produces the following query:
SELECT...
FROM mw_crafru.clientcontact this_,
mw_crafru.client client1_,
mw_crafru.relationshiptype relationsh4_
WHERE this_.clientid = client1_.clientid
AND this_.relationshiptypeid = relationsh4_.relationshiptypeid
AND client1_.clientid = :p0;:p0 = 13
Notice no outer join this.clientid = client1.clientid. Yay!
Anyone know why this is happening?
With SQL Server at least, a many-to-one mapping with not-null="true", or Not.Nullable() using Fluent NH, causes Get operations to use an inner join instead of a left join. Try adding that to your mapping for Client in ClientContact.
Related
I need to do a join using a JOIN TABLE ON ... AND ... using Spring Data JPA criteria builder.
I know I can do a basic join like so:
Join<ReportEntity, ProductEntity> productJoin = root.join("products", JoinType.LEFT);
But can I specify extra criteria for the join? If not, is there another way to achieve this using the Criteria Builder? This is the SQL query I'd like to reproduce:
SELECT r.id, p.rare
FROM REPORT r
LEFT JOIN PRODUCT p
ON r.id = p.report_id AND p.rare = 1
WHERE p.report_id IS NULL;
Note that specifying p.rare = 1 in the above query in the WHERE clause does not give the desired result, it needs to go in the ON clause.
Join<ReportEntity, ProductEntity> productJoin = root.join("products", JoinType.LEFT);
Predicate joinPredicate = criteriaBuilder.equal(root.get("id"), productJoin.get("reportId"));
Predicate rarePredicate = criteriaBuilder.equal(productJoin.get("rare"), 1);
productJoin.on(joinPredicate, rarePredicate);
Im trying to match this SQL query in querydsl
SELECT tr.* FROM test.TRIP_REQ tr left outer join test.ADDR_BOOK ab on tr.REQ_USERID=ab.USER_ID
I know how to make left join query if you join into identity column but struggle to make it work with joining on 2 alternative columns. tr.REQ_USERID and ab.USER_ID are not identity columns
This is my querydsl:
QTripReq qTripReq = QTripReq.tripReq;
QAddressBook qABook = QAddressBook.addressBook;
JPAQuery query = new JPAQuery(entityManager);
query.from(qTripReq).leftJoin(qABook).on(qTripReq.requestorUser.id.eq(qABook.user.id)).list(qTripReq);
This throws error:
Path expected for join! [select tripReq from com.TripReq tripReq left join ADDR_BOOK addressBook with tripReq.requestorUser.id = addressBook.user.id where tripReq.assignedCompany.id = ?1]
You need to add a target entity path to leftJoin(), so that
QTripReq qTripReq = QTripReq.tripReq;
QAddressBook qABook = QAddressBook.addressBook;
JPAQuery query = new JPAQuery(entityManager);
query.from(qTripReq).leftJoin(qTripReq.addressBook, qABook).on(qTripReq.requestorUser.id.eq(qABook.user.id)).list(qTripReq);
Take a look on Using joins section in docs.
I have a question about joins in NHIBERNATE. We had an issue with our sql query that was generated but nhibernate. Our db developer optimized the raw sql so it works as we need, but we need to change the nhibernate code to make generated sql look like optimized.
the part of the original part of the query is:
FROM PERSON_VISIT this_
inner join PERSON_Basic per2_
on this_.PERSON_ID = per2_.PERSON_ID
left outer join PERSONC_QUESTIONS perint10_
on per2_.PERSON_ID = perint10_.PERSON_ID
left outer join TELEPHONE_QUESTIONS intaudit13_
on perint10_.PP_QUESTIONS_ID = intaudit13_.PP_QUESTIONS_ID
inner join C_QUESTIONS intdef14_
on perint10_.QUESTION_ID = intdef14_.QUESTION_ID
and perint10_.QUESTIONS_CODE = intdef14_.QUESTIONS_CODE
and perint10_.QUESTION_ID = intdef14_.QUESTION_ID
The optimized one is :
FROM PERSON_VISIT this_
inner join PERSON_Basic per2_
on this_.PERSON_ID = per2_.PERSON_ID
left outer join PERSONC_QUESTIONS perint10_
on per2_.PERSON_ID = perint10_.PERSON_ID
left outer join TELEPHONE_QUESTIONS intaudit13_
on perint10_.PP_QUESTIONS_ID = intaudit13_.PP_QUESTIONS_ID
left outer join C_QUESTIONS intdef14_
on perint10_.QUESTION_ID = intdef14_.QUESTION_ID
and perint10_.QUESTIONS_CODE = intdef14_.QUESTIONS_CODE
and perint10_.QUESTION_ID = intdef14_.QUESTION_ID
and intdef14_.DISCIPLINE_CODE = this_.DISCIPLINE_CODE
To change query from inner join to left outer join is easy, i changed only one line of code:
.CreateAlias("PersonInt.QuestionEntity", "IntDef", JoinType.LeftOuterJoin)
But how I can add
and intdef14_.DISCIPLINE_CODE = this_.DISCIPLINE_CODE
using nhibernate code?
There is an option to add reference from PERSON_VISIT definition to C_QUESTIONS, but the problem is that
PERSON_VISIT is used everywhere and I don't want this change to possibly break other queries, I just wnat to add only one line of code to add, how I can do that? Is there any way to have access to the raw join to change it? Or some other way to add this
and intdef14_.DISCIPLINE_CODE = this_.DISCIPLINE_CODE
To the query?
I know that somebody will say that we can add a restriction to the query through criteria.Add, but it is not an option cause db developer optimized our query taking this restriction from WHERE clause to the join.
How I can do that quickly without changing the models definitions? Just changing only this one query without changing the whole model?
It is possible using HQL and the Criteria API's.
This question gives you the answer: Adding conditionals to outer joins with nhibernate
Something like this may solve your issue.
.CreateAlias("PersonInt.QuestionEntity", "IntDef", JoinType.LeftOuterJoin,
Restrictions.EqProperty("DISCIPLINE_CODE", "IntDef.DISCIPLINE_CODE"))
Thanks for answers. We use 2.0 version of NHibernate in our project so we didn't have a chance to use new methods of .CreateAlias with restrictions.
I have fixed an issue using Interceptors:
public class SqlInterceptor : EmptyInterceptor, IInterceptor
{
SqlString IInterceptor.OnPrepareStatement(SqlString sql)
{
//manipulating with the sql
return sql;
}
}
than
var factory = Session.SessionFactory;
var session = factory.OpenSession(new SqlInterceptor());
And use my query without a change.
I've been reading Hibernate documentation, but I haven't found anything that would explain how to do the following.
I have the following SQL code that I'm trying to convert to HQL:
SELECT {msg.*}, {cmd.*}
FROM Schema.Messages AS msg
LEFT OUTER JOIN schema.send_commands AS cmd
ON cmd.message_key = msg.unique_key
AND ( lower(cmd.status) IN (lower('failed') ) )
WHERE msg.sequence_received < 10";
The mainissue I'm having is that I'm unable to have two clauses on a LEFT OUTER JOIN. HQL allows me to have
ON cmd.message_key = msg.unique_key
, but how do I add the
AND clause 2?
You can add extra join conditions using with keyword, something like this (depends on your mapping):
SELECT m, c
FROM Message m LEFT JOIN m.commands c WITH (lower(c.status) = 'failed')
WHERE m.sequenceReceived < 10
See also:
16.3. Associations and joins
I have 2 entities Role & Translation.
Role -> Role_ID, Code
Translation -> Code, Language, Name
The idea is to say for a certain role, that it has English name, French name and so on.
For example:
A Role(1, 'Rol_001') can have the relations: Translation('Rol_001', 'English', '') & Translation('Rol_001', 'French', '').
I would like to express the following SQL query in HQL:
select r.Role_ID, t.Name
from Role r left outer join Translation t
on r.Code = t.Code and t.Language = #lang;
In my mapping files I don't have any relation between the 2 entities but the following HQL query works as if it is inner join
IQuery query = session.CreateQuery("select new Lookup(r.Role, t.Name) from Role r, Translation t where r.Code = r.Code and t.Language = :language");
If I change the HQL to left outer join, I get the Path expected for join exception.
Can you help me with the following:
1- Do I need to change my mapping files?
2- If I can keep the mapping files as is, how write such a query in HQL?
3- How does HQL really works? Why such a simple outer join query is not working? I must be missing something here!
Edit:
Now I am using the following code based on the suggetion to use CreateSQL:
ISQLQuery query = session.CreateSQLQuery("select m.MedicineTypeID, t.Name, m.IsDeleted from MedicineType m left outer join Translation t on m.Code = t.Code and t.Language = :language");
query.SetString("language", language);
IList rawLookup = query.List();
IList medicineTypesLookup = new List(rawLookup.Count);
foreach (object[] lookup in rawLookup)
{
medicineTypesLookup.Add(new Lookup((int)lookup[0], (string)lookup[1], (bool)lookup[2]));
}
return medicineTypesLookup;
This is working however I want to use query.List() to get the result directly instead of converting it myself.
I tried to use query.AddEntity(typeof(Lookup)); but I get the exception NHibernate.MappingException: No persister for: DAL.Domain.Lookup.
The Lookup is just a POCO and doesn't map to any database table. Its mapping file is simply <import class="Lookup" />
Finally I found the answer:ISession session = NHibernateHelper.Session;
ISQLQuery query = session.CreateSQLQuery("select m.MedicineTypeID as ID, t.Name, m.IsDeleted from MedicineType m left outer join Translation t on m.Code = t.Code and t.Language = :language");
query.setString("language", language);
IList lookup = query.SetResultTransformer(Transformers.AliasToBean()).List();
return lookup;
And the lookup is a POCO class with a parameterless constructor and 3 properties ID, Name and IsDeleted.
I would like to thank Kelly and Diego Mijelshon for their hints. Although they don't provide the full answer the but using Session.CreateSqlQuery() was a very useful hint.
So the complete solution is Session.CreateSQLQuery and query.SetResultTransformer
Note: Transformers.AliasToBean() is so java.
Edit: http://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/impl/SQLQueryImpl.html for correct method of setString()
You must to define the relationship in the mappings or do a subquery