Skip to content

Commit f1a3b97

Browse files
ggeurtshazzik
authored andcommitted
Fix generating join aliases for subcriteria
Fixes #2175
1 parent 2d7097c commit f1a3b97

File tree

5 files changed

+387
-33
lines changed

5 files changed

+387
-33
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using System.Collections.Generic;
12+
using NHibernate.Cfg.MappingSchema;
13+
using NHibernate.Criterion;
14+
using NHibernate.Mapping.ByCode;
15+
using NUnit.Framework;
16+
17+
namespace NHibernate.Test.NHSpecificTest.GH2175
18+
{
19+
using System.Threading.Tasks;
20+
using System.Threading;
21+
[TestFixture]
22+
public class FixtureAsync : TestCaseMappingByCode
23+
{
24+
protected override HbmMapping GetMappings()
25+
{
26+
var mapper = new ModelMapper();
27+
mapper.Class<Concept>(m =>
28+
{
29+
m.Lazy(false);
30+
m.Id(c => c.Id, id =>
31+
{
32+
id.Column("concept_id");
33+
id.Generator(Generators.Native);
34+
});
35+
m.Set(c => c.Mappings,
36+
collection =>
37+
{
38+
collection.Fetch(CollectionFetchMode.Subselect);
39+
collection.Lazy(CollectionLazy.NoLazy);
40+
collection.Table("concept_mapping");
41+
collection.Key(key =>
42+
{
43+
key.Column("concept_id");
44+
});
45+
},
46+
element =>
47+
{
48+
element.Component(component =>
49+
{
50+
component.Property(c => c.Relationship, p => p.Column("relationship"));
51+
component.ManyToOne(c => c.Code, manyToOne =>
52+
{
53+
manyToOne.Column("code_id");
54+
manyToOne.Fetch(FetchKind.Join);
55+
manyToOne.Cascade(NHibernate.Mapping.ByCode.Cascade.None);
56+
});
57+
});
58+
});
59+
});
60+
61+
mapper.Class<ConceptCode>(m =>
62+
{
63+
m.Table("concept_code");
64+
m.Lazy(false);
65+
66+
m.Id(c => c.Id, id =>
67+
{
68+
id.Column("code_id");
69+
id.Generator(Generators.Native);
70+
});
71+
72+
m.Property(c => c.CodeSource, p =>
73+
{
74+
p.Column("source");
75+
});
76+
m.Property(c => c.Value, p =>
77+
{
78+
p.Column("`value`");
79+
});
80+
});
81+
82+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
83+
}
84+
85+
[Test]
86+
public async Task SubcriteriaOnNestedComponentAsync()
87+
{
88+
using (var session = OpenSession())
89+
using (var tx = session.BeginTransaction())
90+
{
91+
var concepts = new List<Concept>();
92+
for (var i = 1; i < 10; i++)
93+
{
94+
var concept = new Concept { DisplayName = $"Concept{i}" };
95+
concept.Mappings.Add(new ConceptCodeMapping(ConceptCodeRelationship.SameAs,
96+
await (CreatePersistentCodeAsync(session, "src-x", "C" + i))));
97+
concept.Mappings.Add(new ConceptCodeMapping(ConceptCodeRelationship.SameAs,
98+
await (CreatePersistentCodeAsync(session, "src-y", "CX" + (0x1000 + i).ToString("X")))));
99+
concepts.Add(concept);
100+
await (session.SaveAsync(concept));
101+
}
102+
103+
var criteria = session.CreateCriteria<Concept>()
104+
.CreateAlias(nameof(Concept.Mappings), "cm")
105+
.CreateCriteria("cm." + nameof(ConceptCodeMapping.Code), "code")
106+
.Add(Restrictions.Eq(nameof(ConceptCode.CodeSource), "src-y"))
107+
.Add(Restrictions.In(nameof(ConceptCode.Value), new object[] { "CX1001", "CX1003", "CX1008" }));
108+
109+
var persistentConcepts = await (criteria.ListAsync<Concept>());
110+
Assert.That(persistentConcepts, Is.EquivalentTo(new[] { concepts[0], concepts[2], concepts[7] }));
111+
}
112+
}
113+
114+
private async Task<ConceptCode> CreatePersistentCodeAsync(ISession session, string codeSource, string value, CancellationToken cancellationToken = default(CancellationToken))
115+
{
116+
var code = new ConceptCode(codeSource, value);
117+
await (session.SaveAsync(code, cancellationToken));
118+
return code;
119+
}
120+
}
121+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace NHibernate.Test.NHSpecificTest.GH2175
5+
{
6+
public class Concept
7+
{
8+
public int Id { get; set; }
9+
public string DisplayName { get; set; }
10+
public ISet<ConceptCodeMapping> Mappings { get; private set; } = new HashSet<ConceptCodeMapping>();
11+
}
12+
13+
public class ConceptCodeMapping
14+
{
15+
/// <summary>
16+
/// Constructs empty <see cref="ConceptCodeMapping" /> instance. Intended for use by persistence
17+
/// frameworks only.
18+
/// </summary>
19+
protected ConceptCodeMapping()
20+
{ }
21+
22+
public ConceptCodeMapping(ConceptCodeRelationship relationship, ConceptCode code)
23+
{
24+
this.Relationship = relationship;
25+
this.Code = code;
26+
}
27+
28+
public ConceptCodeRelationship Relationship { get; protected set; }
29+
public ConceptCode Code { get; protected set; }
30+
31+
public static bool operator ==(ConceptCodeMapping left, ConceptCodeMapping right)
32+
{
33+
if (ReferenceEquals(left, right)) return true;
34+
if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) return false;
35+
36+
return left.Relationship == right.Relationship
37+
&& Equals(left.Code, right.Code);
38+
}
39+
40+
public static bool operator !=(ConceptCodeMapping left, ConceptCodeMapping right)
41+
{
42+
return !(left == right);
43+
}
44+
45+
public bool Equals(ConceptCodeMapping other)
46+
{
47+
return this == other;
48+
}
49+
50+
public override bool Equals(object obj)
51+
{
52+
return this == obj as ConceptCodeMapping;
53+
}
54+
55+
public override int GetHashCode()
56+
{
57+
return this.Code?.GetHashCode() ?? 0;
58+
}
59+
}
60+
61+
public enum ConceptCodeRelationship
62+
{
63+
Undefined = 0,
64+
SameAs = '=',
65+
NarrowerThan = '<',
66+
BroaderThan = '>'
67+
}
68+
69+
public class ConceptCode : IEquatable<ConceptCode>
70+
{
71+
protected ConceptCode()
72+
{ }
73+
74+
public ConceptCode(string codeSource, string value)
75+
{
76+
this.CodeSource = codeSource;
77+
this.Value = value;
78+
}
79+
80+
public int Id { get; set; }
81+
public string CodeSource { get; protected set; }
82+
public string Value { get; protected set; }
83+
84+
public static bool operator ==(ConceptCode left, ConceptCode right)
85+
{
86+
if (ReferenceEquals(left, right)) return true;
87+
if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) return false;
88+
89+
return left.Value == right.Value
90+
&& left.CodeSource == right.CodeSource;
91+
}
92+
93+
public static bool operator !=(ConceptCode left, ConceptCode right)
94+
{
95+
return !(left == right);
96+
}
97+
98+
public bool Equals(ConceptCode other)
99+
{
100+
return this == other;
101+
}
102+
103+
public override bool Equals(object obj)
104+
{
105+
return this == obj as ConceptCode;
106+
}
107+
108+
public override int GetHashCode()
109+
{
110+
unchecked
111+
{
112+
return ((this.CodeSource?.GetHashCode() ?? 0) * 397)
113+
^ (this.Value?.GetHashCode() ?? 0);
114+
}
115+
}
116+
117+
public override string ToString()
118+
{
119+
return this.CodeSource + "::" + this.Value;
120+
}
121+
}
122+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System.Collections.Generic;
2+
using NHibernate.Cfg.MappingSchema;
3+
using NHibernate.Criterion;
4+
using NHibernate.Mapping.ByCode;
5+
using NUnit.Framework;
6+
7+
namespace NHibernate.Test.NHSpecificTest.GH2175
8+
{
9+
[TestFixture]
10+
public class Fixture : TestCaseMappingByCode
11+
{
12+
protected override HbmMapping GetMappings()
13+
{
14+
var mapper = new ModelMapper();
15+
mapper.Class<Concept>(m =>
16+
{
17+
m.Lazy(false);
18+
m.Id(c => c.Id, id =>
19+
{
20+
id.Column("concept_id");
21+
id.Generator(Generators.Native);
22+
});
23+
m.Set(c => c.Mappings,
24+
collection =>
25+
{
26+
collection.Fetch(CollectionFetchMode.Subselect);
27+
collection.Lazy(CollectionLazy.NoLazy);
28+
collection.Table("concept_mapping");
29+
collection.Key(key =>
30+
{
31+
key.Column("concept_id");
32+
});
33+
},
34+
element =>
35+
{
36+
element.Component(component =>
37+
{
38+
component.Property(c => c.Relationship, p => p.Column("relationship"));
39+
component.ManyToOne(c => c.Code, manyToOne =>
40+
{
41+
manyToOne.Column("code_id");
42+
manyToOne.Fetch(FetchKind.Join);
43+
manyToOne.Cascade(NHibernate.Mapping.ByCode.Cascade.None);
44+
});
45+
});
46+
});
47+
});
48+
49+
mapper.Class<ConceptCode>(m =>
50+
{
51+
m.Table("concept_code");
52+
m.Lazy(false);
53+
54+
m.Id(c => c.Id, id =>
55+
{
56+
id.Column("code_id");
57+
id.Generator(Generators.Native);
58+
});
59+
60+
m.Property(c => c.CodeSource, p =>
61+
{
62+
p.Column("source");
63+
});
64+
m.Property(c => c.Value, p =>
65+
{
66+
p.Column("`value`");
67+
});
68+
});
69+
70+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
71+
}
72+
73+
[Test]
74+
public void SubcriteriaOnNestedComponent()
75+
{
76+
using (var session = OpenSession())
77+
using (var tx = session.BeginTransaction())
78+
{
79+
var concepts = new List<Concept>();
80+
for (var i = 1; i < 10; i++)
81+
{
82+
var concept = new Concept { DisplayName = $"Concept{i}" };
83+
concept.Mappings.Add(new ConceptCodeMapping(ConceptCodeRelationship.SameAs,
84+
CreatePersistentCode(session, "src-x", "C" + i)));
85+
concept.Mappings.Add(new ConceptCodeMapping(ConceptCodeRelationship.SameAs,
86+
CreatePersistentCode(session, "src-y", "CX" + (0x1000 + i).ToString("X"))));
87+
concepts.Add(concept);
88+
session.Save(concept);
89+
}
90+
91+
var criteria = session.CreateCriteria<Concept>()
92+
.CreateAlias(nameof(Concept.Mappings), "cm")
93+
.CreateCriteria("cm." + nameof(ConceptCodeMapping.Code), "code")
94+
.Add(Restrictions.Eq(nameof(ConceptCode.CodeSource), "src-y"))
95+
.Add(Restrictions.In(nameof(ConceptCode.Value), new object[] { "CX1001", "CX1003", "CX1008" }));
96+
97+
var persistentConcepts = criteria.List<Concept>();
98+
Assert.That(persistentConcepts, Is.EquivalentTo(new[] { concepts[0], concepts[2], concepts[7] }));
99+
}
100+
}
101+
102+
private ConceptCode CreatePersistentCode(ISession session, string codeSource, string value)
103+
{
104+
var code = new ConceptCode(codeSource, value);
105+
session.Save(code);
106+
return code;
107+
}
108+
}
109+
}

src/NHibernate/Loader/Criteria/CriteriaJoinWalker.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ protected override void AddAssociations()
8181
{
8282
var tableAlias = translator.GetSQLAlias(entityJoinInfo.Criteria);
8383
var criteriaPath = entityJoinInfo.Criteria.Alias; //path for entity join is equal to alias
84-
var criteriaAlias = entityJoinInfo.Criteria.Alias;
84+
var criteriaPathAlias = entityJoinInfo.Criteria.Alias;
8585
var persister = entityJoinInfo.Persister as IOuterJoinLoadable;
86-
AddExplicitEntityJoinAssociation(persister, tableAlias, translator.GetJoinType(criteriaPath, criteriaAlias), criteriaPath, criteriaAlias);
86+
AddExplicitEntityJoinAssociation(persister, tableAlias, translator.GetJoinType(criteriaPath, criteriaPathAlias), criteriaPath, criteriaPathAlias);
8787
IncludeInResultIfNeeded(persister, entityJoinInfo.Criteria, tableAlias, criteriaPath);
8888
//collect mapped associations for entity join
8989
WalkEntityTree(persister, tableAlias, criteriaPath, 1);

0 commit comments

Comments
 (0)