From: <fab...@us...> - 2009-01-03 17:26:41
|
Revision: 3974 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=3974&view=rev Author: fabiomaulo Date: 2009-01-03 17:26:30 +0000 (Sat, 03 Jan 2009) Log Message: ----------- Fix NH-1612 (thanks to Gerke Geurts) Modified Paths: -------------- trunk/nhibernate/src/NHibernate/Loader/Custom/CustomLoader.cs trunk/nhibernate/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs trunk/nhibernate/src/NHibernate/Loader/GeneratedCollectionAliases.cs trunk/nhibernate/src/NHibernate/Loader/Loader.cs trunk/nhibernate/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs trunk/nhibernate/src/NHibernate.Test/App.config trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1612/Mappings.hbm.xml trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1612/NativeSqlCollectionLoaderFixture.cs Modified: trunk/nhibernate/src/NHibernate/Loader/Custom/CustomLoader.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Loader/Custom/CustomLoader.cs 2009-01-02 18:52:54 UTC (rev 3973) +++ trunk/nhibernate/src/NHibernate/Loader/Custom/CustomLoader.cs 2009-01-03 17:26:30 UTC (rev 3974) @@ -369,7 +369,7 @@ { object[] resultRow; // NH Different behavior (patched in NH-1612 to solve Hibernate issue HHH-2831). - if (!hasScalars && hasTransformer) + if (!hasScalars && (hasTransformer || data.Length == 0)) { resultRow = data; } Modified: trunk/nhibernate/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs 2009-01-02 18:52:54 UTC (rev 3973) +++ trunk/nhibernate/src/NHibernate/Loader/Custom/Sql/SQLQueryParser.cs 2009-01-03 17:26:30 UTC (rev 3974) @@ -131,6 +131,7 @@ ISqlLoadableCollection collectionPersister = context.GetCollectionPersisterByAlias(aliasName); string collectionSuffix = context.GetCollectionSuffixByAlias(aliasName); + // NH Different behavior for NH-1612 if ("*".Equals(propertyName)) { if (fieldResults.Count != 0) @@ -140,37 +141,50 @@ string selectFragment = collectionPersister.SelectFragment(aliasName, collectionSuffix); aliasesFound++; - return selectFragment + ", " + ResolveProperties(aliasName, propertyName); + + // Collection may just contain elements and no entities, in which case resolution of + // collection properties is enough. + return collectionPersister.ElementType.IsEntityType + ? selectFragment + ", " + ResolveProperties(aliasName, "*") + : selectFragment; } - else if ("element.*".Equals(propertyName)) + + if (propertyName.StartsWith("element.")) { - return ResolveProperties(aliasName, "*"); - } - else - { - string[] columnAliases; + string elementPropertyName = propertyName.Substring("element.".Length); - // Let return-propertys override whatever the persister has for aliases. - if (!fieldResults.TryGetValue(propertyName,out columnAliases)) + if (collectionPersister.ElementType.IsEntityType) { - columnAliases = collectionPersister.GetCollectionPropertyColumnAliases(propertyName, collectionSuffix); + return ResolveProperties(aliasName, elementPropertyName); } - - if (columnAliases == null || columnAliases.Length == 0) + else if (elementPropertyName == "*") { - throw new QueryException("No column name found for property [" + propertyName + "] for alias [" + aliasName + "]", - originalQueryString); + throw new QueryException("Using element.* syntax is only supported for entity elements."); } - if (columnAliases.Length != 1) - { - // TODO: better error message since we actually support composites if names are explicitly listed. - throw new QueryException( - "SQL queries only support properties mapped to a single column - property [" + propertyName + "] is mapped to " - + columnAliases.Length + " columns.", originalQueryString); - } - aliasesFound++; - return columnAliases[0]; } + + string[] columnAliases; + + // Let return-propertys override whatever the persister has for aliases. + if (!fieldResults.TryGetValue(propertyName, out columnAliases)) + { + columnAliases = collectionPersister.GetCollectionPropertyColumnAliases(propertyName, collectionSuffix); + } + + if (columnAliases == null || columnAliases.Length == 0) + { + throw new QueryException("No column name found for property [" + propertyName + "] for alias [" + aliasName + "]", + originalQueryString); + } + if (columnAliases.Length != 1) + { + // TODO: better error message since we actually support composites if names are explicitly listed. + throw new QueryException( + "SQL queries only support properties mapped to a single column - property [" + propertyName + "] is mapped to " + + columnAliases.Length + " columns.", originalQueryString); + } + aliasesFound++; + return columnAliases[0]; } private string ResolveProperties(string aliasName, string propertyName) Modified: trunk/nhibernate/src/NHibernate/Loader/GeneratedCollectionAliases.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Loader/GeneratedCollectionAliases.cs 2009-01-02 18:52:54 UTC (rev 3973) +++ trunk/nhibernate/src/NHibernate/Loader/GeneratedCollectionAliases.cs 2009-01-03 17:26:30 UTC (rev 3974) @@ -18,7 +18,7 @@ private readonly IDictionary<string, string[]> userProvidedAliases; public GeneratedCollectionAliases(IDictionary<string, string[]> userProvidedAliases, ICollectionPersister persister, - string suffix) + string suffix) { this.suffix = suffix; this.userProvidedAliases = userProvidedAliases; @@ -27,7 +27,11 @@ indexAliases = GetUserProvidedAliases("index", persister.GetIndexColumnAliases(suffix)); - elementAliases = GetUserProvidedAliases("element", persister.GetElementColumnAliases(suffix)); + // NH-1612: Add aliases for all composite element properties to support access + // to individual composite element properties in <return-property> elements. + elementAliases = persister.ElementType.IsComponentType + ? GetUserProvidedCompositeElementAliases(persister.GetElementColumnAliases(suffix)) + : GetUserProvidedAliases("element", persister.GetElementColumnAliases(suffix)); identifierAlias = GetUserProvidedAlias("id", persister.GetIdentifierColumnAlias(suffix)); } @@ -35,6 +39,20 @@ public GeneratedCollectionAliases(ICollectionPersister persister, string str) : this(new CollectionHelper.EmptyMapClass<string, string[]>(), persister, str) {} + private string[] GetUserProvidedCompositeElementAliases(string[] defaultAliases) + { + var aliases = new List<string>(); + foreach (KeyValuePair<string, string[]> userProvidedAlias in userProvidedAliases) + { + if (userProvidedAlias.Key.StartsWith("element.")) + { + aliases.AddRange(userProvidedAlias.Value); + } + } + + return aliases.Count > 0 ? aliases.ToArray() : defaultAliases; + } + /// <summary> /// Returns the suffixed result-set column-aliases for columns making up the key for this collection (i.e., its FK to /// its owner). Modified: trunk/nhibernate/src/NHibernate/Loader/Loader.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Loader/Loader.cs 2009-01-02 18:52:54 UTC (rev 3973) +++ trunk/nhibernate/src/NHibernate/Loader/Loader.cs 2009-01-03 17:26:30 UTC (rev 3974) @@ -392,11 +392,11 @@ int entitySpan = EntityPersisters.Length; List<object> hydratedObjects = entitySpan == 0 ? null : new List<object>(entitySpan * 10); - + IDbCommand st = PrepareQueryCommand(queryParameters, false, session); - IDataReader rs = - GetResultSet(st, queryParameters.HasAutoDiscoverScalarTypes, queryParameters.Callable, selection, session); + IDataReader rs = GetResultSet(st, queryParameters.HasAutoDiscoverScalarTypes, queryParameters.Callable, selection, + session); // would be great to move all this below here into another method that could also be used // from the new scrolling stuff. @@ -428,9 +428,8 @@ log.Debug("result set row: " + count); } - object result = - GetRowFromResultSet(rs, session, queryParameters, lockModeArray, optionalObjectKey, hydratedObjects, keys, - returnProxies); + object result = GetRowFromResultSet(rs, session, queryParameters, lockModeArray, optionalObjectKey, hydratedObjects, + keys, returnProxies); results.Add(result); if (createSubselects) @@ -445,11 +444,11 @@ log.Debug(string.Format("done processing result set ({0} rows)", count)); } } - catch(Exception e) - { - e.Data["actual-sql-query"] = st.CommandText; - throw; - } + catch (Exception e) + { + e.Data["actual-sql-query"] = st.CommandText; + throw; + } finally { session.Batcher.CloseCommand(st, rs); Modified: trunk/nhibernate/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs 2009-01-02 18:52:54 UTC (rev 3973) +++ trunk/nhibernate/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs 2009-01-03 17:26:30 UTC (rev 3974) @@ -1386,8 +1386,7 @@ public string[] GetCollectionPropertyColumnAliases(string propertyName, string suffix) { object aliases; - collectionPropertyColumnAliases.TryGetValue(propertyName, out aliases); - if (aliases == null) + if (!collectionPropertyColumnAliases.TryGetValue(propertyName, out aliases)) { return null; } @@ -1425,13 +1424,29 @@ if (type.IsComponentType) { - IAbstractComponentType ct = (IAbstractComponentType) type; + // NH-1612: Recursively add column aliases for nested components to support the selection + // of individual component properties in native SQL queries. This also seems to provide + // a more complete solution to HHH-1019 (http://opensource.atlassian.com/projects/hibernate/browse/HHH-1019) + // because it works for <load-collection> and <return-join>. + int columnIndex = 0; + + var ct = (IAbstractComponentType) type; string[] propertyNames = ct.PropertyNames; - for (int i = 0; i < propertyNames.Length; i++) + for (int propertyIndex = 0; propertyIndex < propertyNames.Length; propertyIndex++) { - string name = propertyNames[i]; - collectionPropertyColumnAliases[aliasName + "." + name] = columnAliases[i]; - collectionPropertyColumnNames[aliasName + "." + name] = columnNames[i]; + string name = propertyNames[propertyIndex]; + IType propertyType = ct.Subtypes[propertyIndex]; + int propertyColSpan = propertyType.IsComponentType + ? ((IAbstractComponentType) propertyType).PropertyNames.Length + : 1; + + var propertyColumnAliases = new string[propertyColSpan]; + var propertyColumnNames = new string[propertyColSpan]; + System.Array.Copy(columnAliases, columnIndex, propertyColumnAliases, 0, propertyColSpan); + System.Array.Copy(columnNames, columnIndex, propertyColumnNames, 0, propertyColSpan); + InitCollectionPropertyMap(aliasName + "." + name, propertyType, propertyColumnAliases, propertyColumnNames); + + columnIndex += propertyColSpan; } } } Modified: trunk/nhibernate/src/NHibernate.Test/App.config =================================================================== --- trunk/nhibernate/src/NHibernate.Test/App.config 2009-01-02 18:52:54 UTC (rev 3973) +++ trunk/nhibernate/src/NHibernate.Test/App.config 2009-01-03 17:26:30 UTC (rev 3974) @@ -123,7 +123,7 @@ </logger> <logger name="NHibernate.SQL"> - <level value="OFF" /> + <level value="DEBUG" /> </logger> <logger name="NHibernate.Tool.hbm2ddl.SchemaExport"> Modified: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1612/Mappings.hbm.xml =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1612/Mappings.hbm.xml 2009-01-02 18:52:54 UTC (rev 3973) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1612/Mappings.hbm.xml 2009-01-03 17:26:30 UTC (rev 3974) @@ -75,7 +75,7 @@ <sql-query name="AreaStatisticsLoader" xml:space="preserve"> <load-collection role="Area.Statistics" alias="s"> - <return-property name="key" column="code" /> + <return-property name="key" column="area_code" /> <return-property name="index" column="year" /> <return-property name="element.CitizenCount" column="citizen_count" /> <return-property name="element.GDP"> Modified: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1612/NativeSqlCollectionLoaderFixture.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1612/NativeSqlCollectionLoaderFixture.cs 2009-01-02 18:52:54 UTC (rev 3973) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH1612/NativeSqlCollectionLoaderFixture.cs 2009-01-03 17:26:30 UTC (rev 3974) @@ -5,7 +5,7 @@ namespace NHibernate.Test.NHSpecificTest.NH1612 { - [TestFixture, Ignore("Not fixed yet.")] + [TestFixture] public class NativeSqlCollectionLoaderFixture : BugTestCase { #region Tests - <return-join> @@ -18,16 +18,8 @@ Assert.That(country, Is.Not.Null); Assert.That(country.Routes, Is.EquivalentTo(routes)); - - // cleanup - using (ISession session = OpenSession()) - { - using (ITransaction tx = session.BeginTransaction()) - { - session.Delete(country); - tx.Commit(); - } - } + + Cleanup(); } [Test] @@ -37,15 +29,7 @@ Country country = LoadCountryWithNativeSQL(CreateCountry(routes), "LoadCountryRoutesWithCustomAliases"); Assert.That(country, Is.Not.Null); Assert.That(country.Routes, Is.EquivalentTo(routes)); - // cleanup - using (ISession session = OpenSession()) - { - using (ITransaction tx = session.BeginTransaction()) - { - session.Delete(country); - tx.Commit(); - } - } + Cleanup(); } [Test] @@ -57,15 +41,7 @@ Assert.That(country, Is.Not.Null); Assert.That((ICollection) country.Statistics.Keys, Is.EquivalentTo((ICollection) stats.Keys), "Keys"); Assert.That((ICollection) country.Statistics.Values, Is.EquivalentTo((ICollection) stats.Values), "Elements"); - // cleanup - using (ISession session = OpenSession()) - { - using (ITransaction tx = session.BeginTransaction()) - { - session.Delete(country); - tx.Commit(); - } - } + CleanupWithPersons(); } [Test] @@ -77,6 +53,7 @@ Assert.That(country, Is.Not.Null); Assert.That((ICollection) country.Statistics.Keys, Is.EquivalentTo((ICollection) stats.Keys), "Keys"); Assert.That((ICollection) country.Statistics.Values, Is.EquivalentTo((ICollection) stats.Values), "Elements"); + CleanupWithPersons(); } [Test] @@ -88,40 +65,81 @@ Assert.That(country, Is.Not.Null); Assert.That((ICollection) country.Statistics.Keys, Is.EquivalentTo((ICollection) stats.Keys), "Keys"); Assert.That((ICollection) country.Statistics.Values, Is.EquivalentTo((ICollection) stats.Values), "Elements"); + + CleanupWithPersons(); } [Test] public void LoadEntitiesWithWithSimpleHbmAliasInjection() { City[] cities = CreateCities(); - Country country = LoadCountryWithNativeSQL(CreateCountry(cities), "LoadCountryCitiesWithSimpleHbmAliasInjection"); - Assert.That(country, Is.Not.Null); - Assert.That(country.Cities, Is.EquivalentTo(cities)); + Country country = CreateCountry(cities); + Save(country); + using (ISession session = OpenSession()) + { + var c = + session.GetNamedQuery("LoadCountryCitiesWithSimpleHbmAliasInjection").SetString("country_code", country.Code). + UniqueResult<Country>(); + Assert.That(c, Is.Not.Null); + Assert.That(c.Cities, Is.EquivalentTo(cities)); + } + CleanupWithCities(); } [Test] public void LoadEntitiesWithComplexHbmAliasInjection() { City[] cities = CreateCities(); - Country country = LoadCountryWithNativeSQL(CreateCountry(cities), "LoadCountryCitiesWithComplexHbmAliasInjection"); - Assert.That(country, Is.Not.Null); - Assert.That(country.Cities, Is.EquivalentTo(cities)); + Country country = CreateCountry(cities); + Save(country); + using (ISession session = OpenSession()) + { + var c = + session.GetNamedQuery("LoadCountryCitiesWithComplexHbmAliasInjection").SetString("country_code", country.Code). + UniqueResult<Country>(); + Assert.That(c, Is.Not.Null); + Assert.That(c.Cities, Is.EquivalentTo(cities)); + } + CleanupWithCities(); } [Test] public void LoadEntitiesWithExplicitColumnMappings() { City[] cities = CreateCities(); - Country country = LoadCountryWithNativeSQL(CreateCountry(cities), "LoadCountryCitiesWithCustomAliases"); - Assert.That(country, Is.Not.Null); - Assert.That(country.Cities, Is.EquivalentTo(cities)); + Country country = CreateCountry(cities); + Save(country); + using (ISession session = OpenSession()) + { + var c = + session.GetNamedQuery("LoadCountryCitiesWithCustomAliases").SetString("country_code", country.Code). + UniqueResult<Country>(); + Assert.That(c, Is.Not.Null); + Assert.That(c.Cities, Is.EquivalentTo(cities)); + } + + // cleanup + CleanupWithCities(); } - [Test, ExpectedException(typeof (QueryException))] + [Test] public void NativeQueryWithUnresolvedHbmAliasInjection() { IDictionary<int, AreaStatistics> stats = CreateStatistics(); - LoadCountryWithNativeSQL(CreateCountry(stats), "LoadAreaStatisticsWithFaultyHbmAliasInjection"); + try + { + LoadCountryWithNativeSQL(CreateCountry(stats), "LoadAreaStatisticsWithFaultyHbmAliasInjection"); + Assert.Fail("Expected exception"); + } + catch(QueryException) + { + // ok + } + finally + { + // cleanup + CleanupWithPersons(); + } } private Country LoadCountryWithNativeSQL(Country country, string queryName) @@ -151,10 +169,14 @@ { string[] routes = CreateRoutes(); Country country = CreateCountry(routes); - Country c = SaveAndReload(country); - Assert.That(c, Is.Not.Null, "country"); - Assert.That(c.Routes, Is.EquivalentTo(routes), "country.Routes"); - + Save(country); + using (ISession session = OpenSession()) + { + var c = session.Get<Country>(country.Code); + Assert.That(c, Is.Not.Null, "country"); + Assert.That(c.Routes, Is.EquivalentTo(routes), "country.Routes"); + } + Cleanup(); } [Test] @@ -162,10 +184,15 @@ { IDictionary<int, AreaStatistics> stats = CreateStatistics(); Country country = CreateCountry(stats); - Area a = SaveAndReload(country); - Assert.That(a, Is.Not.Null, "area"); - Assert.That((ICollection)a.Statistics.Keys, Is.EquivalentTo((ICollection)stats.Keys), "area.Keys"); - Assert.That((ICollection)a.Statistics.Values, Is.EquivalentTo((ICollection)stats.Values), "area.Elements"); + Save(country); + using (ISession session = OpenSession()) + { + var a = session.Get<Area>(country.Code); + Assert.That(a, Is.Not.Null, "area"); + Assert.That((ICollection) a.Statistics.Keys, Is.EquivalentTo((ICollection) stats.Keys), "area.Keys"); + Assert.That((ICollection) a.Statistics.Values, Is.EquivalentTo((ICollection) stats.Values), "area.Elements"); + } + CleanupWithPersons(); } [Test] @@ -173,15 +200,19 @@ { City[] cities = CreateCities(); Country country = CreateCountry(cities); - Country c = SaveAndReload(country); - Assert.That(c, Is.Not.Null, "country"); - Assert.That(c.Cities, Is.EquivalentTo(cities), "country.Cities"); + Save(country); + using (ISession session = OpenSession()) + { + var c = session.Get<Country>(country.Code); + + Assert.That(c, Is.Not.Null, "country"); + Assert.That(c.Cities, Is.EquivalentTo(cities), "country.Cities"); + } + CleanupWithCities(); } - private TArea SaveAndReload<TArea>(TArea area) where TArea : Area + private void Save<TArea>(TArea area) where TArea : Area { - //Ensure country is saved and session cache is empty to force from now on the reload of all - //persistence objects from the database. using (ISession session = OpenSession()) { using (ITransaction tx = session.BeginTransaction()) @@ -189,12 +220,7 @@ session.Save(area); tx.Commit(); } - } - using (ISession session = OpenSession()) - { - return session.Get<TArea>(area.Code); - } } #endregion @@ -261,6 +287,48 @@ #endregion + #region cleanup + + private void Cleanup() + { + using (ISession session = OpenSession()) + { + using (ITransaction tx = session.BeginTransaction()) + { + session.Delete("from Country"); + tx.Commit(); + } + } + } + + private void CleanupWithPersons() + { + using (ISession session = OpenSession()) + { + using (ITransaction tx = session.BeginTransaction()) + { + session.Delete("from Person"); + session.Delete("from Country"); + tx.Commit(); + } + } + } + + private void CleanupWithCities() + { + using (ISession session = OpenSession()) + { + using (ITransaction tx = session.BeginTransaction()) + { + session.Delete("from City"); + session.Delete("from Country"); + tx.Commit(); + } + } + } + + #endregion + #region Factory methods private static Country CreateCountry() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |