-
Notifications
You must be signed in to change notification settings - Fork 933
Support to join not associated entities in Criteria (aka Entity Join) #1545
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
aed4628
to
aaaf9fc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just a quick review for now, I should do a more thorough one later.
@@ -280,7 +284,8 @@ IQueryOver<TRoot> IQueryOver<TRoot>.ReadOnly() | |||
/// Implementation of the <see cref="IQueryOver<TRoot, TSubType>"/> interface | |||
/// </summary> | |||
[Serializable] | |||
public class QueryOver<TRoot,TSubType> : QueryOver<TRoot>, IQueryOver<TRoot,TSubType> | |||
public class QueryOver<TRoot,TSubType> : QueryOver<TRoot>, IQueryOver<TRoot,TSubType>, | |||
ISupportEntityJoinQueryOver<TRoot> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have not seen the reason for implementing this interface on this class rather than on QueryOver<TRoot>
. Why this choice?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe because that is the way it looks to be done for other similar cases. But then, ISupportEntityJoinQueryOver<TRoot>
"6.0 todo" should be "merge in IQueryOver<TRoot, TSubType>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I've just put it next to existing JoinAlias
/JoinQueryOver
methods. Looks more natural to have them in the same place.
namespace NHibernate.Criterion | ||
{ | ||
//TODO: Make interface more flexible for changes (maybe it should take only 2 params alias + EntityJoinConfig) | ||
public interface ISupportEntityJoinQueryOver |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add a " 6.0 TODO: merge with IQueryOver". Is there a reason for not aiming at this? The same apply to below interface, but of course with IQueryOver<TRoot>
.
|
||
namespace NHibernate.Impl | ||
{ | ||
public interface ISupportEntityJoinCriteria |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add a " 6.0 TODO: merge with ICriteria". Is there a reason for not aiming at this?
public static class EntityJoinExtensions | ||
{ | ||
// 6.0 TODO: merge into 'IQueryOver<TType, TSubType> | ||
public static TThis JoinEntityAlias<TThis, TAlias>(this TThis queryOver, Expression<Func<TAlias>> alias, JoinType joinType, ICriterion withClause, string entityName = null) where TThis : IQueryOver |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So that is somewhat the todo I am asking for on interface.
{ | ||
last = entityJoinInfo.Persister; | ||
lastEntity = (IPropertyMapping) last; | ||
++i; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i++;
is more common place, and we do not need pre-increment here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to add to this: if preferring ++i;
other i++;
for "performance", this is an old performance "trick", obsolete for C, potentially still relevant for C++ when applied to an object but well, we are using C#. See here. Optimizing such trivial things is not a developer job but a compiler job. I am on the side on optimizing the developer work by letting compiler responsible of such optimizations, and letting developer write most readable code possible. This includes using most common places syntaxes unless there is a real and significant impact for the application.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, of course It's not about optimization :) it's about habit - I just use ++i;
since C++ and it's more common for me.
I have no objections to change it but curious why do you think that i++
is more common for C#? Is it because VS/Resharper expands for
with i++
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P.S. I don't have much time this week but I will try to address all the questions/concerns next week.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it because VS/Resharper expands
for
withi++
?
No. Just because I have far more seen post-increments than pre-increments in C#, and I do not think I am alone. Alexander had made the same demand, switching away from ++i
to i++
on a previous PR. The opener argued for performance as far a I remember, and it was discarded as not relevant in C#.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was here.
src/NHibernate/Impl/CriteriaImpl.cs
Outdated
@@ -71,7 +72,7 @@ public CriteriaImpl(string entityOrClassName, string alias, ISessionImplementor | |||
rootAlias = alias; | |||
subcriteriaByAlias[alias] = this; | |||
} | |||
|
|||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Undue whitespace addition.
@@ -280,7 +284,8 @@ IQueryOver<TRoot> IQueryOver<TRoot>.ReadOnly() | |||
/// Implementation of the <see cref="IQueryOver<TRoot, TSubType>"/> interface | |||
/// </summary> | |||
[Serializable] | |||
public class QueryOver<TRoot,TSubType> : QueryOver<TRoot>, IQueryOver<TRoot,TSubType> | |||
public class QueryOver<TRoot,TSubType> : QueryOver<TRoot>, IQueryOver<TRoot,TSubType>, | |||
ISupportEntityJoinQueryOver<TRoot> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe because that is the way it looks to be done for other similar cases. But then, ISupportEntityJoinQueryOver<TRoot>
"6.0 todo" should be "merge in IQueryOver<TRoot, TSubType>
.
src/NHibernate/Impl/CriteriaImpl.cs
Outdated
@@ -668,6 +675,16 @@ internal Subcriteria(CriteriaImpl root, ICriteria parent, string path, string al | |||
internal Subcriteria(CriteriaImpl root, ICriteria parent, string path, JoinType joinType) | |||
: this(root, parent, path, null, joinType) { } | |||
|
|||
/// <summary> | |||
/// Entity name for "Entity Join" - join for enitity with not mapped association |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo, enitity
.
var tableAlias = translator.GetSQLAlias(entityJoinInfo.Criteria); | ||
var criteriaPath = entityJoinInfo.Criteria.Alias; //path for entity join is equal to alias | ||
var persister | ||
= entityJoinInfo.Persister as IOuterJoinLoadable; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A single line here should be enough. (And it is not an usual formatting to have the affectation operator on the next line instead of at the end of line.)
{ | ||
last = entityJoinInfo.Persister; | ||
lastEntity = (IPropertyMapping) last; | ||
++i; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to add to this: if preferring ++i;
other i++;
for "performance", this is an old performance "trick", obsolete for C, potentially still relevant for C++ when applied to an object but well, we are using C#. See here. Optimizing such trivial things is not a developer job but a compiler job. I am on the side on optimizing the developer work by letting compiler responsible of such optimizations, and letting developer write most readable code possible. This includes using most common places syntaxes unless there is a real and significant impact for the application.
{ | ||
on = on.Count == 0 | ||
? on.Append(withClause) | ||
: on.Append(" and ( ").Append(withClause).Append(" )"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For me, that is an undue change. It looks to me the way it is generated, on
is either empty or starts with " and "
. Testing on Count
here changes behavior. Better not do that and keep previous code.
if (fkColumns.Length == 0) | ||
{ | ||
_fromFragment.Add(on); | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, an additional adjustment should be done. on
is usually generated with a leading " and "
, which should be removed before adding to _fromFragment
.
@@ -62,7 +62,7 @@ protected bool AddCondition(SqlStringBuilder buffer, SqlString on) | |||
{ | |||
if (SqlStringHelper.IsNotEmpty(on)) | |||
{ | |||
if (!on.StartsWithCaseInsensitive(" and")) | |||
if (buffer.Count > 0 && !on.StartsWithCaseInsensitive(" and")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I doubt this change is legit. Was it causing a failure not having this change done? It looks to me code expects condition clauses to always potentially starts with an " and", removing it if needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The intention here is the same as in ANSIJoinFragment.AddJoin
- avoid bare withClause
to be added with " and" prefix to SQL. But yeah - maybe I should handle it in each dialect explicitly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right - I've removed this change. I've struggled with this " and" prefix and thought this change is required for Oracle (but I was wrong)
@fredericDelaporte It seems I've addressed all your requests. I also checked how to make nhibernate-core/src/NHibernate/IQueryOver.cs Line 369 in 8d2b123
So basically fetch doesn't work after I think it's actually an "interface bug" - as this
Any ideas how to resolve it properly? Maybe in 5.x we should made a more straightforward fix - use |
I think it will need to be addressed as another PR, that is why I approve this one as is.
Well, as usual, for a minor, we cannot change anything in an interface. Does it work to define a suitable extension, that would handle only NHibernate QueryOver internal implementation, while obsoleting the interface method? That should be the preferred way, with some "6.0 TODO" for merging it back into the interface. |
It seems this PR has fixed another issue btw. |
I've made last small adjustments:
|
Which is? |
I have cherry-picked tests for #1366 (NH-3506) from #268, and tested them on this PR and on its base commit: no changes, fails the same way, with the same resulting queries (catched inside SQL-Profiler). So it does not look like this PR is fixing anything additional. (NH-2029 was already fixed in 5.0.) |
So I intend to merge this soon. |
With this PR it's possible to join entities with not mapped association in QueryOver via
JoinEntityQueryOver
/JoinEntityAlias
methods:See other options in tests
"Entity Join" name comes from Hibernate (where it's implemented for hql only):
http://in.relation.to/2016/02/10/hibernate-orm-510-final-release/