Skip to content

Commit 9c98271

Browse files
oskarbhazzik
authored andcommitted
Refactor to make handling of ResultTransformer more similar to Hibernate by porting their ef46a4efb77a7028500801a9db2a2c398e7e6b7f:
HHH-5424 HHH-5425 : Put ResultTransformer in QueryKey only if data is transformed; PropertyAccessException when caching 1 result per row
1 parent 7509e26 commit 9c98271

File tree

10 files changed

+130
-39
lines changed

10 files changed

+130
-39
lines changed

src/NHibernate.Test/CacheTest/QueryKeyFixture.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ private void QueryKeyFilterDescLikeToCompare(out QueryKey qk, out QueryKey qk1)
4242
f.SetParameter("pLike", "so%");
4343
var fk = new FilterKey(filterName, f.Parameters, f.FilterDefinition.ParameterTypes, EntityMode.Poco);
4444
ISet<FilterKey> fks = new HashSet<FilterKey> { fk };
45-
qk = new QueryKey(sessions, SqlAll, new QueryParameters(), fks);
45+
qk = new QueryKey(sessions, SqlAll, new QueryParameters(), fks, null);
4646

4747
var f1 = new FilterImpl(sessions.GetFilterDefinition(filterName));
4848
f1.SetParameter("pLike", "%ing");
4949
var fk1 = new FilterKey(filterName, f.Parameters, f.FilterDefinition.ParameterTypes, EntityMode.Poco);
5050
fks = new HashSet<FilterKey> { fk1 };
51-
qk1 = new QueryKey(sessions, SqlAll, new QueryParameters(), fks);
51+
qk1 = new QueryKey(sessions, SqlAll, new QueryParameters(), fks, null);
5252
}
5353

5454
private void QueryKeyFilterDescValueToCompare(out QueryKey qk, out QueryKey qk1)
@@ -59,13 +59,13 @@ private void QueryKeyFilterDescValueToCompare(out QueryKey qk, out QueryKey qk1)
5959
f.SetParameter("pDesc", "something").SetParameter("pValue", 10);
6060
var fk = new FilterKey(filterName, f.Parameters, f.FilterDefinition.ParameterTypes, EntityMode.Poco);
6161
ISet<FilterKey> fks = new HashSet<FilterKey> { fk };
62-
qk = new QueryKey(sessions, SqlAll, new QueryParameters(), fks);
62+
qk = new QueryKey(sessions, SqlAll, new QueryParameters(), fks, null);
6363

6464
var f1 = new FilterImpl(sessions.GetFilterDefinition(filterName));
6565
f1.SetParameter("pDesc", "something").SetParameter("pValue", 11);
6666
var fk1 = new FilterKey(filterName, f.Parameters, f.FilterDefinition.ParameterTypes, EntityMode.Poco);
6767
fks = new HashSet<FilterKey> { fk1 };
68-
qk1 = new QueryKey(sessions, SqlAll, new QueryParameters(), fks);
68+
qk1 = new QueryKey(sessions, SqlAll, new QueryParameters(), fks, null);
6969
}
7070

7171
[Test]
@@ -113,15 +113,15 @@ public void ToStringWithFilters()
113113
f.SetParameter("pLike", "so%");
114114
var fk = new FilterKey(filterName, f.Parameters, f.FilterDefinition.ParameterTypes, EntityMode.Poco);
115115
ISet<FilterKey> fks = new HashSet<FilterKey> { fk };
116-
var qk = new QueryKey(sessions, SqlAll, new QueryParameters(), fks);
116+
var qk = new QueryKey(sessions, SqlAll, new QueryParameters(), fks, null);
117117
Assert.That(qk.ToString(), Is.StringContaining(string.Format("filters: ['{0}']",fk)));
118118

119119
filterName = "DescriptionEqualAndValueGT";
120120
f = new FilterImpl(sessions.GetFilterDefinition(filterName));
121121
f.SetParameter("pDesc", "something").SetParameter("pValue", 10);
122122
fk = new FilterKey(filterName, f.Parameters, f.FilterDefinition.ParameterTypes, EntityMode.Poco);
123123
fks = new HashSet<FilterKey> { fk };
124-
qk = new QueryKey(sessions, SqlAll, new QueryParameters(), fks);
124+
qk = new QueryKey(sessions, SqlAll, new QueryParameters(), fks, null);
125125
Assert.That(qk.ToString(), Is.StringContaining(string.Format("filters: ['{0}']", fk)));
126126
}
127127

@@ -139,7 +139,7 @@ public void ToStringWithMoreFilters()
139139
var fvk = new FilterKey(filterName, f.Parameters, f.FilterDefinition.ParameterTypes, EntityMode.Poco);
140140

141141
ISet<FilterKey> fks = new HashSet<FilterKey> { fk, fvk };
142-
var qk = new QueryKey(sessions, SqlAll, new QueryParameters(), fks);
142+
var qk = new QueryKey(sessions, SqlAll, new QueryParameters(), fks, null);
143143
Assert.That(qk.ToString(), Is.StringContaining(string.Format("filters: ['{0}', '{1}']", fk, fvk)));
144144
}
145145
}

src/NHibernate.Test/SecondLevelCacheTest/QueryCacheFixture.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ public void SimpleProjections()
257257
tx.Commit();
258258
}
259259

260-
Assert.That(qs.CacheHitCount, Is.EqualTo(1), "hit count should not go up since we are adding a resulttransformer");
260+
Assert.That(qs.CacheHitCount, Is.EqualTo(2), "hit count should go up since the cache contains the result before the possible application of a resulttransformer");
261261

262262
using (ISession s = OpenSession())
263263
using (ITransaction tx = s.BeginTransaction())
@@ -266,7 +266,7 @@ public void SimpleProjections()
266266
tx.Commit();
267267
}
268268

269-
Assert.That(qs.CacheHitCount, Is.EqualTo(2), "hit count should go up since we are using the same resulttransformer");
269+
Assert.That(qs.CacheHitCount, Is.EqualTo(3), "hit count should go up since we are using the same resulttransformer");
270270
using (ISession s = OpenSession())
271271
using (ITransaction tx = s.BeginTransaction())
272272
{
@@ -277,8 +277,8 @@ public void SimpleProjections()
277277
tx.Commit();
278278
}
279279

280-
Assert.That(qs.CacheHitCount, Is.EqualTo(3));
281-
Assert.That(qs.CacheMissCount, Is.EqualTo(3));
280+
Assert.That(qs.CacheHitCount, Is.EqualTo(4));
281+
Assert.That(qs.CacheMissCount, Is.EqualTo(2));
282282

283283
Thread.Sleep(200);
284284

@@ -294,10 +294,10 @@ public void SimpleProjections()
294294
tx.Commit();
295295
}
296296

297-
Assert.That(qs.CacheHitCount, Is.EqualTo(3));
298-
Assert.That(qs.CacheMissCount, Is.EqualTo(4));
299-
Assert.That(qs.CachePutCount, Is.EqualTo(4));
300-
Assert.That(qs.ExecutionCount, Is.EqualTo(4));
297+
Assert.That(qs.CacheHitCount, Is.EqualTo(4));
298+
Assert.That(qs.CacheMissCount, Is.EqualTo(3));
299+
Assert.That(qs.CachePutCount, Is.EqualTo(3));
300+
Assert.That(qs.ExecutionCount, Is.EqualTo(3));
301301
Assert.That(es.FetchCount, Is.EqualTo(0)); //check that it was being cached
302302
}
303303

src/NHibernate/Cache/QueryKey.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ public class QueryKey
3535
/// <param name="queryString">The query string.</param>
3636
/// <param name="queryParameters">The query parameters.</param>
3737
/// <param name="filters">The filters.</param>
38+
/// <param name="customTransformer">The result transformer; should be null if data is not transformed before being cached.</param>
3839
public QueryKey(ISessionFactoryImplementor factory, SqlString queryString, QueryParameters queryParameters,
39-
ISet<FilterKey> filters)
40+
ISet<FilterKey> filters, IResultTransformer customTransformer)
4041
{
4142
_factory = factory;
4243
_sqlQueryString = queryString;
@@ -56,10 +57,15 @@ public QueryKey(ISessionFactoryImplementor factory, SqlString queryString, Query
5657
}
5758
_namedParameters = queryParameters.NamedParameters;
5859
_filters = filters;
59-
_customTransformer = queryParameters.ResultTransformer;
60+
_customTransformer = customTransformer;
6061
_hashCode = ComputeHashCode();
6162
}
6263

64+
public IResultTransformer ResultTransformer
65+
{
66+
get { return _customTransformer; }
67+
}
68+
6369
public QueryKey SetFirstRows(int[] firstRows)
6470
{
6571
_multiQueriesFirstRows = firstRows;

src/NHibernate/Hql/HolderInstantiator.cs

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ public static HolderInstantiator GetHolderInstantiator(IResultTransformer select
1414
IResultTransformer customTransformer,
1515
string[] queryReturnAliases)
1616
{
17-
if (selectNewTransformer != null)
18-
{
19-
return new HolderInstantiator(selectNewTransformer, queryReturnAliases);
20-
}
21-
else
22-
{
23-
return new HolderInstantiator(customTransformer, queryReturnAliases);
24-
}
17+
return new HolderInstantiator(ResolveResultTransformer(selectNewTransformer, customTransformer),
18+
queryReturnAliases);
2519
}
2620

21+
22+
public static IResultTransformer ResolveResultTransformer(IResultTransformer selectNewTransformer, IResultTransformer customTransformer)
23+
{
24+
return selectNewTransformer ?? customTransformer;
25+
}
26+
2727
public static IResultTransformer CreateSelectNewTransformer(ConstructorInfo constructor, bool returnMaps,
2828
bool returnLists)
2929
{
@@ -48,14 +48,13 @@ public static IResultTransformer CreateSelectNewTransformer(ConstructorInfo cons
4848
public static HolderInstantiator CreateClassicHolderInstantiator(ConstructorInfo constructor,
4949
IResultTransformer transformer)
5050
{
51-
if (constructor != null)
52-
{
53-
return new HolderInstantiator(new AliasToBeanConstructorResultTransformer(constructor), null);
54-
}
55-
else
56-
{
57-
return new HolderInstantiator(transformer, null);
58-
}
51+
return new HolderInstantiator(ResolveClassicResultTransformer(constructor, transformer), null);
52+
}
53+
54+
public static IResultTransformer ResolveClassicResultTransformer(ConstructorInfo constructor,
55+
IResultTransformer transformer)
56+
{
57+
return constructor != null ? new AliasToBeanConstructorResultTransformer(constructor) : transformer;
5958
}
6059

6160
public HolderInstantiator(IResultTransformer transformer, string[] queryReturnAliases)

src/NHibernate/Impl/MultiCriteriaImpl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ private IList ListUsingQueryCache()
109109

110110
MultipleQueriesCacheAssembler assembler = new MultipleQueriesCacheAssembler(resultTypesList);
111111
QueryParameters combinedParameters = CreateCombinedQueryParameters();
112-
QueryKey key = new QueryKey(session.Factory, SqlString, combinedParameters, filterKeys)
112+
QueryKey key = new QueryKey(session.Factory, SqlString, combinedParameters, filterKeys, null)
113113
.SetFirstRows(firstRows)
114114
.SetMaxRows(maxRows);
115115

src/NHibernate/Impl/MultiQueryImpl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ private IList ListUsingQueryCache()
697697

698698
MultipleQueriesCacheAssembler assembler = new MultipleQueriesCacheAssembler(resultTypesList);
699699

700-
QueryKey key = new QueryKey(session.Factory, SqlString, combinedParameters, filterKeys)
700+
QueryKey key = new QueryKey(session.Factory, SqlString, combinedParameters, filterKeys, null)
701701
.SetFirstRows(firstRows)
702702
.SetMaxRows(maxRows);
703703

src/NHibernate/Loader/Criteria/CriteriaLoader.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ public IList List(ISessionImplementor session)
7575
return List(session, translator.GetQueryParameters(), querySpaces, resultTypes);
7676
}
7777

78+
protected override IResultTransformer ResolveResultTransformer(IResultTransformer resultTransformer)
79+
{
80+
return translator.RootCriteria.ResultTransformer;
81+
}
82+
83+
protected override bool AreResultSetRowsTransformedImmediately(IResultTransformer transformer)
84+
{
85+
// comparing to null just in case there is no transformer
86+
// (there should always be a result transformer;
87+
return ResolveResultTransformer(transformer) != null;
88+
}
89+
7890
protected override object GetResultColumnOrRow(object[] row, IResultTransformer customResultTransformer, IDataReader rs,
7991
ISessionImplementor session)
8092
{
@@ -109,7 +121,8 @@ protected override object GetResultColumnOrRow(object[] row, IResultTransformer
109121
result = row;
110122
aliases = userAliases;
111123
}
112-
return translator.RootCriteria.ResultTransformer.TransformTuple(result, aliases);
124+
125+
return ResolveResultTransformer(customResultTransformer).TransformTuple(result, aliases);
113126
}
114127

115128
protected override SqlString ApplyLocks(SqlString sqlSelectString, IDictionary<string, LockMode> lockModes,
@@ -164,7 +177,7 @@ public override LockMode[] GetLockModes(IDictionary<string, LockMode> lockModes)
164177

165178
public override IList GetResultList(IList results, IResultTransformer resultTransformer)
166179
{
167-
return translator.RootCriteria.ResultTransformer.TransformList(results);
180+
return ResolveResultTransformer(resultTransformer).TransformList(results);
168181
}
169182

170183
protected override IEnumerable<IParameterSpecification> GetParameterSpecifications()

src/NHibernate/Loader/Custom/CustomLoader.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,12 @@ protected override object GetResultColumnOrRow(object[] row, IResultTransformer
283283
return rowProcessor.BuildResultRow(row, rs, resultTransformer != null, session);
284284
}
285285

286+
287+
protected override IResultTransformer ResolveResultTransformer(IResultTransformer resultTransformer)
288+
{
289+
return HolderInstantiator.ResolveResultTransformer(null, resultTransformer);
290+
}
291+
286292
public override IList GetResultList(IList results, IResultTransformer resultTransformer)
287293
{
288294
// meant to handle dynamic instantiation queries...(Copy from QueryLoader)

src/NHibernate/Loader/Hql/QueryLoader.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,11 @@ public override IList GetResultList(IList results, IResultTransformer resultTran
319319
}
320320
}
321321

322+
protected override IResultTransformer ResolveResultTransformer(IResultTransformer resultTransformer)
323+
{
324+
return HolderInstantiator.ResolveResultTransformer(_selectNewTransformer, resultTransformer);
325+
}
326+
322327
protected override object GetResultColumnOrRow(object[] row, IResultTransformer resultTransformer, IDataReader rs,
323328
ISessionImplementor session)
324329
{

src/NHibernate/Loader/Loader.cs

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,29 @@ private static void EndCollectionLoad(object resultSetId, ISessionImplementor se
627627
collectionPersister);
628628
}
629629

630+
631+
/// <summary>
632+
/// Determine the actual ResultTransformer that will be used to transform query results.
633+
/// </summary>
634+
/// <param name="resultTransformer">The specified result transformer.</param>
635+
/// <returns>The actual result transformer.</returns>
636+
protected virtual IResultTransformer ResolveResultTransformer(IResultTransformer resultTransformer)
637+
{
638+
return resultTransformer;
639+
}
640+
641+
642+
/// <summary>
643+
/// Are rows transformed immediately after being read from the ResultSet?
644+
/// </summary>
645+
/// <param name="transformer">The specified result transformer.</param>
646+
/// <returns>True, if getResultColumnOrRow() transforms the results; false, otherwise</returns>
647+
protected virtual bool AreResultSetRowsTransformedImmediately(IResultTransformer transformer)
648+
{
649+
return false;
650+
}
651+
652+
630653
public virtual IList GetResultList(IList results, IResultTransformer resultTransformer)
631654
{
632655
return results;
@@ -1477,7 +1500,10 @@ private IList ListUsingQueryCache(ISessionImplementor session, QueryParameters q
14771500
IQueryCache queryCache = _factory.GetQueryCache(queryParameters.CacheRegion);
14781501

14791502
ISet<FilterKey> filterKeys = FilterKey.CreateFilterKeys(session.EnabledFilters, session.EntityMode);
1480-
QueryKey key = new QueryKey(Factory, SqlString, queryParameters, filterKeys);
1503+
QueryKey key = new QueryKey(Factory, SqlString, queryParameters, filterKeys,
1504+
AreResultSetRowsTransformedImmediately(queryParameters.ResultTransformer)
1505+
? queryParameters.ResultTransformer
1506+
: null);
14811507

14821508
IList result = GetResultFromQueryCache(session, queryParameters, querySpaces, resultTypes, queryCache, key);
14831509

@@ -1526,6 +1552,24 @@ private IList GetResultFromQueryCache(ISessionImplementor session, QueryParamete
15261552
{
15271553
persistenceContext.DefaultReadOnly = defaultReadOnlyOrig;
15281554
}
1555+
1556+
// If there is a result transformer, but the loader is not expecting the data to be
1557+
// transformed yet, then the loader expects result elements that are Object[].
1558+
// The problem is that StandardQueryCache.get(...) does not return a tuple when
1559+
// resultTypes.length == 1. The following changes the data returned from the cache
1560+
// to be a tuple.
1561+
// TODO: this really doesn't belong here, but only Loader has the information
1562+
// to be able to do this.
1563+
if (result != null &&
1564+
resultTypes.Length == 1 &&
1565+
key.ResultTransformer == null &&
1566+
ResolveResultTransformer(queryParameters.ResultTransformer) != null)
1567+
{
1568+
for (int i = 0; i < result.Count; i++)
1569+
{
1570+
result[i] = new[] {result[i]};
1571+
}
1572+
}
15291573
}
15301574
return result;
15311575
}
@@ -1535,7 +1579,25 @@ private void PutResultInQueryCache(ISessionImplementor session, QueryParameters
15351579
{
15361580
if ((session.CacheMode & CacheMode.Put) == CacheMode.Put)
15371581
{
1538-
bool put = queryCache.Put(key, resultTypes, result, queryParameters.NaturalKeyLookup, session);
1582+
// If there is a result transformer, but the data has not been transformed yet,
1583+
// then result elements are Object[]. The problem is that StandardQueryCache.put(...)
1584+
// does not expect a tuple when resultTypes.length == 1. The following changes the
1585+
// data being cached to what StandardQueryCache.put(...) expects.
1586+
// TODO: this really doesn't belong here, but only Loader has the information
1587+
// to be able to do this.
1588+
IList cachedResult = result;
1589+
if (resultTypes.Length == 1 &&
1590+
key.ResultTransformer == null &&
1591+
ResolveResultTransformer(queryParameters.ResultTransformer) != null)
1592+
{
1593+
cachedResult = new ArrayList(result.Count);
1594+
for (int i = 0; i < result.Count; i++)
1595+
{
1596+
cachedResult.Add(((object[]) result[i])[0]);
1597+
}
1598+
}
1599+
1600+
bool put = queryCache.Put(key, resultTypes, cachedResult, queryParameters.NaturalKeyLookup, session);
15391601
if (put && _factory.Statistics.IsStatisticsEnabled)
15401602
{
15411603
_factory.StatisticsImplementor.QueryCachePut(QueryIdentifier, queryCache.RegionName);

0 commit comments

Comments
 (0)