From: <jul...@us...> - 2010-07-20 00:54:46
|
Revision: 5017 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=5017&view=rev Author: julian-maughan Date: 2010-07-20 00:54:39 +0000 (Tue, 20 Jul 2010) Log Message: ----------- Added support for many-to-one mappings that include a formula (ref. NH-2117) Modified Paths: -------------- trunk/nhibernate/src/NHibernate/Cfg/MappingSchema/HbmManyToOne.cs trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/ClassCompositeIdBinder.cs trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/ColumnsBinder.cs trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/ValuePropertyBinder.cs trunk/nhibernate/src/NHibernate/SqlCommand/ANSIJoinFragment.cs trunk/nhibernate/src/NHibernate/SqlCommand/SelectFragment.cs trunk/nhibernate/src/NHibernate/nhibernate-mapping.xsd trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj Added Paths: ----------- trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/ trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/Address.cs trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/AddressId.cs trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/Customer.cs trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/Customer.hbm.xml trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/TypedManyToOneTest.cs Modified: trunk/nhibernate/src/NHibernate/Cfg/MappingSchema/HbmManyToOne.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Cfg/MappingSchema/HbmManyToOne.cs 2010-07-20 00:34:18 UTC (rev 5016) +++ trunk/nhibernate/src/NHibernate/Cfg/MappingSchema/HbmManyToOne.cs 2010-07-20 00:54:39 UTC (rev 5017) @@ -109,6 +109,14 @@ #endregion + /// <summary> + /// Columns and Formulas, in declared order + /// </summary> + public IEnumerable<object> ColumnsAndFormulas + { + get { return Columns.Cast<object>().Concat(Formulas.Cast<object>()); } + } + public HbmLaziness? Lazy { get { return lazySpecified ? lazy : (HbmLaziness?) null;} Modified: trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/ClassCompositeIdBinder.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/ClassCompositeIdBinder.cs 2010-07-20 00:34:18 UTC (rev 5016) +++ trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/ClassCompositeIdBinder.cs 2010-07-20 00:54:39 UTC (rev 5017) @@ -18,6 +18,8 @@ } compositeId = new Component(rootClass); + compositeId.IsKey = true; + rootClass.Identifier = compositeId; if (idSchema.name == null) Modified: trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/ColumnsBinder.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/ColumnsBinder.cs 2010-07-20 00:34:18 UTC (rev 5016) +++ trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/ColumnsBinder.cs 2010-07-20 00:54:39 UTC (rev 5017) @@ -15,25 +15,29 @@ { this.value = value; } - + + public void Bind(HbmColumn column, bool isNullable) + { + this.BindColumn(column, value.Table, isNullable); + } + public void Bind(IEnumerable<HbmColumn> columns, bool isNullable, Func<HbmColumn> defaultColumnDelegate) { var table = value.Table; - int colIndex = 0; foreach (var hbmColumn in columns) { - BindColumn(hbmColumn, table, colIndex++, isNullable); + BindColumn(hbmColumn, table, isNullable); } if (value.ColumnSpan == 0 && defaultColumnDelegate != null) { - BindColumn(defaultColumnDelegate(), table, colIndex, isNullable); + BindColumn(defaultColumnDelegate(), table, isNullable); } } - private void BindColumn(HbmColumn hbmColumn, Table table, int colIndex, bool isNullable) + private void BindColumn(HbmColumn hbmColumn, Table table, bool isNullable) { - var col = new Column {Value = value, TypeIndex = colIndex}; + var col = new Column {Value = value}; BindColumn(hbmColumn, col, isNullable); if (table != null) Modified: trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/ValuePropertyBinder.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/ValuePropertyBinder.cs 2010-07-20 00:34:18 UTC (rev 5016) +++ trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/ValuePropertyBinder.cs 2010-07-20 00:54:39 UTC (rev 5017) @@ -28,7 +28,7 @@ var formulas = propertyMapping.Formulas.ToArray(); if (formulas.Length > 0) { - BindFormula(formulas); + BindFormulas(formulas); } else { @@ -56,7 +56,7 @@ var formulas = element.Formulas.ToArray(); if (formulas.Length > 0) { - BindFormula(formulas); + BindFormulas(formulas); } else { @@ -76,14 +76,6 @@ } } - private void BindFormula(IEnumerable<HbmFormula> formulas) - { - foreach (var hbmFormula in formulas) - { - value.AddFormula(new Formula { FormulaString = hbmFormula.Text.LinesToString() }); - } - } - public void BindSimpleValue(HbmKey propertyMapping, string propertyPath, bool isNullable) { new ColumnsBinder(value, Mappings).Bind(propertyMapping.Columns, isNullable, @@ -103,7 +95,7 @@ var formulas = manyToManyMapping.Formulas.ToArray(); if (formulas.Length > 0) { - BindFormula(formulas); + BindFormulas(formulas); } else { @@ -159,7 +151,7 @@ var formulas = mapKeyMapping.Formulas.ToArray(); if (formulas.Length > 0) { - BindFormula(formulas); + BindFormulas(formulas); } else { @@ -173,27 +165,28 @@ } } - public void BindSimpleValue(HbmManyToOne manyToManyMapping, string propertyPath, bool isNullable) + public void BindSimpleValue(HbmManyToOne manyToOneMapping, string propertyPath, bool isNullable) { - var formulas = manyToManyMapping.Formulas.ToArray(); - if (formulas.Length > 0) + ColumnsBinder binder = new ColumnsBinder(value, Mappings); + object[] columnsAndFormulas = manyToOneMapping.ColumnsAndFormulas.ToArray(); + + if (columnsAndFormulas.Length > 0) { - BindFormula(formulas); + this.AddColumnsAndOrFormulas(binder, columnsAndFormulas, isNullable); } else { - new ColumnsBinder(value, Mappings).Bind(manyToManyMapping.Columns, isNullable, - () => - new HbmColumn - { - name = mappings.NamingStrategy.PropertyToColumnName(propertyPath), - notnull = manyToManyMapping.notnull, - notnullSpecified = manyToManyMapping.notnullSpecified, - unique = manyToManyMapping.unique, - uniqueSpecified = true, - uniquekey = manyToManyMapping.uniquekey, - index = manyToManyMapping.index - }); + // No formulas or columns, so add default column + binder.Bind(new HbmColumn() + { + name = mappings.NamingStrategy.PropertyToColumnName(propertyPath), + notnull = manyToOneMapping.notnull, + notnullSpecified = manyToOneMapping.notnullSpecified, + unique = manyToOneMapping.unique, + uniqueSpecified = true, + uniquekey = manyToOneMapping.uniquekey, + index = manyToOneMapping.index + }, isNullable); } } @@ -212,7 +205,7 @@ var formulas = mapKeyManyToManyMapping.Formulas.ToArray(); if (formulas.Length > 0) { - BindFormula(formulas); + BindFormulas(formulas); } else { @@ -245,5 +238,34 @@ new HbmColumn {name = mappings.NamingStrategy.PropertyToColumnName(propertyPath),}); } + + /// <summary> + /// Bind columns and formulas in the order in which they were mapped. + /// </summary> + /// <param name="binder"></param> + /// <param name="columnsAndFormulas"></param> + /// <param name="isNullable"></param> + private void AddColumnsAndOrFormulas(ColumnsBinder binder, object[] columnsAndFormulas, bool isNullable) + { + foreach (object item in columnsAndFormulas) + { + if (item.GetType() == typeof(HbmFormula)) + this.BindFormula((HbmFormula)item); + + if (item.GetType() == typeof(HbmColumn)) + binder.Bind((HbmColumn)item, isNullable); + } + } + + private void BindFormula(HbmFormula formula) + { + value.AddFormula(new Formula { FormulaString = formula.Text.LinesToString() }); + } + + private void BindFormulas(IEnumerable<HbmFormula> formulas) + { + foreach (var hbmFormula in formulas) + this.BindFormula(hbmFormula); + } } } \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/SqlCommand/ANSIJoinFragment.cs =================================================================== --- trunk/nhibernate/src/NHibernate/SqlCommand/ANSIJoinFragment.cs 2010-07-20 00:34:18 UTC (rev 5016) +++ trunk/nhibernate/src/NHibernate/SqlCommand/ANSIJoinFragment.cs 2010-07-20 00:54:39 UTC (rev 5017) @@ -42,10 +42,6 @@ for (int j = 0; j < fkColumns.Length; j++) { - if (fkColumns[j].IndexOf('.') < 1) - { - throw new AssertionFailure("missing alias"); - } buffer.Add(fkColumns[j] + "=" + alias + StringHelper.Dot + pkColumns[j]); if (j < fkColumns.Length - 1) { Modified: trunk/nhibernate/src/NHibernate/SqlCommand/SelectFragment.cs =================================================================== --- trunk/nhibernate/src/NHibernate/SqlCommand/SelectFragment.cs 2010-07-20 00:34:18 UTC (rev 5016) +++ trunk/nhibernate/src/NHibernate/SqlCommand/SelectFragment.cs 2010-07-20 00:54:39 UTC (rev 5017) @@ -75,7 +75,8 @@ { for (int i = 0; i < columnNames.Length; i++) { - AddColumn(tableAlias, columnNames[i]); + if (columnNames[i] != null) + AddColumn(tableAlias, columnNames[i]); } return this; } @@ -84,7 +85,8 @@ { for (int i = 0; i < columnNames.Length; i++) { - AddColumn(tableAlias, columnNames[i], columnAliases[i]); + if (columnNames[i] != null) + AddColumn(tableAlias, columnNames[i], columnAliases[i]); } return this; } @@ -93,7 +95,8 @@ { for (int i = 0; i < formulas.Length; i++) { - AddFormula(tableAlias, formulas[i], formulaAliases[i]); + if (formulas[i] != null) + AddFormula(tableAlias, formulas[i], formulaAliases[i]); } return this; Modified: trunk/nhibernate/src/NHibernate/nhibernate-mapping.xsd =================================================================== --- trunk/nhibernate/src/NHibernate/nhibernate-mapping.xsd 2010-07-20 00:34:18 UTC (rev 5016) +++ trunk/nhibernate/src/NHibernate/nhibernate-mapping.xsd 2010-07-20 00:54:39 UTC (rev 5017) @@ -865,10 +865,8 @@ <xs:complexType> <xs:sequence> <xs:element ref="meta" minOccurs="0" maxOccurs="unbounded" /> - <xs:choice minOccurs="0" maxOccurs="unbounded"> - <xs:element ref="column" /> - <xs:element ref="formula" /> - </xs:choice> + <xs:element ref="column" minOccurs="0" maxOccurs="unbounded" /> + <xs:element ref="formula" minOccurs="0" maxOccurs="unbounded" /> </xs:sequence> <xs:attribute name="name" use="required" type="xs:string" /> <xs:attribute name="access" type="xs:string" /> Modified: trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2010-07-20 00:34:18 UTC (rev 5016) +++ trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2010-07-20 00:54:39 UTC (rev 5017) @@ -1487,6 +1487,10 @@ <Compile Include="TransactionTest\TransactionNotificationFixture.cs" /> <Compile Include="TransformTests\AliasToBeanResultTransformerFixture.cs" /> <Compile Include="TransformTests\Simple.cs" /> + <Compile Include="TypedManyToOne\Address.cs" /> + <Compile Include="TypedManyToOne\AddressId.cs" /> + <Compile Include="TypedManyToOne\Customer.cs" /> + <Compile Include="TypedManyToOne\TypedManyToOneTest.cs" /> <Compile Include="TypeParameters\DefaultValueIntegerType.cs" /> <Compile Include="TypeParameters\DefinedTypeForIdFixture.cs" /> <Compile Include="TypeParameters\EntityCustomId.cs" /> @@ -1605,6 +1609,7 @@ <EmbeddedResource Include="Component\Basic\User.hbm.xml" /> <EmbeddedResource Include="NHSpecificTest\NH2061\Mappings.hbm.xml" /> <EmbeddedResource Include="NHSpecificTest\NH2195\Mappings.hbm.xml" /> + <EmbeddedResource Include="TypedManyToOne\Customer.hbm.xml" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="CompositeCollection\BaseClassA.hbm.xml" /> @@ -1688,6 +1693,7 @@ <Folder Include="Component\Basic" /> <Folder Include="NHSpecificTest\NH2061" /> <Folder Include="NHSpecificTest\NH2195" /> + <Folder Include="TypedManyToOne" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="NHSpecificTest\NH386\Mappings.hbm.xml" /> Property changes on: trunk/nhibernate/src/NHibernate.Test/TypedManyToOne ___________________________________________________________________ Added: bugtraq:url + http://jira.nhibernate.org/browse/%BUGID% Added: bugtraq:logregex + NH-\d+ Added: trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/Address.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/Address.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/Address.cs 2010-07-20 00:54:39 UTC (rev 5017) @@ -0,0 +1,15 @@ +using System; + +namespace NHibernate.Test.TypedManyToOne +{ + [Serializable] + public class Address + { + public virtual AddressId AddressId {get; set;} + public virtual string Street { get; set; } + public virtual string City { get; set; } + public virtual string State { get; set; } + public virtual string Zip { get; set; } + public virtual Customer Customer { get; set; } + } +} Added: trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/AddressId.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/AddressId.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/AddressId.cs 2010-07-20 00:54:39 UTC (rev 5017) @@ -0,0 +1,55 @@ +using System; + +namespace NHibernate.Test.TypedManyToOne +{ + [Serializable] + public class AddressId + { + public virtual String Type { get; set; } + public virtual String Id { get; set; } + private int? requestedHash; + + public AddressId(String type, String id) + { + Id = id; + Type = type; + } + + public AddressId() { } + + public override bool Equals(object obj) + { + return Equals(obj as AddressId); + } + + public virtual bool Equals(AddressId other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + if (ReferenceEquals(this, other)) + { + return true; + } + return other.Id == Id && Equals(other.Type, Type); + } + + public override int GetHashCode() + { + if (!requestedHash.HasValue) + { + unchecked + { + requestedHash = (Id.GetHashCode() * 397) ^ Type.GetHashCode(); + } + } + return requestedHash.Value; + } + + public override string ToString() + { + return Type + '#' + Id; + } + } +} Added: trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/Customer.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/Customer.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/Customer.cs 2010-07-20 00:54:39 UTC (rev 5017) @@ -0,0 +1,13 @@ +using System; + +namespace NHibernate.Test.TypedManyToOne +{ + [Serializable] + public class Customer + { + public virtual string CustomerId { get; set; } + public virtual string Name {get; set;} + public virtual Address BillingAddress {get; set;} + public virtual Address ShippingAddress {get; set;} + } +} Added: trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/Customer.hbm.xml =================================================================== --- trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/Customer.hbm.xml (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/Customer.hbm.xml 2010-07-20 00:54:39 UTC (rev 5017) @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + + Shows how to map a one-to-many relationship in the relational + schema to "typed" one-to-one associations in the object model. + We map the Address class twice, with different entity names, + specifying a filtering condition in each mapping. The typed + associations then reference the named entities. + +--> + +<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" + namespace="NHibernate.Test.TypedManyToOne" + assembly="NHibernate.Test"> + + <class name="Customer" + select-before-update="true" + dynamic-update="true"> + + <id name="CustomerId"> + <generator class="assigned"/> + </id> + + <property name="Name" not-null="true"/> + + <many-to-one name="BillingAddress" entity-name="BillingAddress" cascade="persist,save-update,delete" fetch="join"> + <column name="BillingAddressId" /> + <formula>(case when Name='blah' then 'SHIPPING' else 'BILLING' end)</formula> + </many-to-one> + + <many-to-one name="ShippingAddress" entity-name="ShippingAddress" cascade="persist,save-update,delete" fetch="join"> + <column name="ShippingAddressId"/> + <formula>'SHIPPING'</formula> + </many-to-one> + + </class> + + <class name="Address" + table="Address" + entity-name="BillingAddress" + where="add_type='BILLING'" + check="add_type in ('BILLING', 'SHIPPING')" + select-before-update="true" + dynamic-update="true"> + + <composite-id name="AddressId" class="AddressId"> + <key-property name="Id"/> + <key-property name="Type" column="add_type" /> + </composite-id> + + <property name="Street" not-null="true"/> + <property name="City" not-null="true"/> + <property name="State" not-null="true"/> + <property name="Zip" not-null="true"/> + + </class> + + <class name="Address" + table="Address" + entity-name="ShippingAddress" + where="add_type='SHIPPING'" + check="add_type in ('BILLING', 'SHIPPING')" + select-before-update="true" + dynamic-update="true"> + + <composite-id name="AddressId" class="AddressId"> + <key-property name="Id"/> + <key-property name="Type" column="add_type" /> + </composite-id> + + <property name="Street" not-null="true"/> + <property name="City" not-null="true"/> + <property name="State" not-null="true"/> + <property name="Zip" not-null="true"/> + + </class> + +</hibernate-mapping> Added: trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/TypedManyToOneTest.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/TypedManyToOneTest.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/TypedManyToOne/TypedManyToOneTest.cs 2010-07-20 00:54:39 UTC (rev 5017) @@ -0,0 +1,131 @@ +using System.Collections; +using NUnit.Framework; + +namespace NHibernate.Test.TypedManyToOne +{ + [TestFixture] + public class TypedManyToOneTest : TestCase + { + protected override string MappingsAssembly + { + get { return "NHibernate.Test"; } + } + + protected override IList Mappings + { + get { return new[] { "TypedManyToOne.Customer.hbm.xml" }; } + } + + [Test] + public void TestCreateQuery() + { + var cust = new Customer(); + cust.CustomerId = "abc123"; + cust.Name = "Matt"; + + var ship = new Address(); + ship.Street = "peachtree rd"; + ship.State = "GA"; + ship.City = "ATL"; + ship.Zip = "30326"; + ship.AddressId = new AddressId("SHIPPING", "xyz123"); + ship.Customer = cust; + + var bill = new Address(); + bill.Street = "peachtree rd"; + bill.State = "GA"; + bill.City = "ATL"; + bill.Zip = "30326"; + bill.AddressId = new AddressId("BILLING", "xyz123"); + bill.Customer = cust; + + cust.BillingAddress = bill; + cust.ShippingAddress = ship; + + using(ISession s = sessions.OpenSession()) + using (ITransaction t = s.BeginTransaction()) + { + s.Persist(cust); + t.Commit(); + } + + using(ISession s = sessions.OpenSession()) + using(ITransaction t = s.BeginTransaction()) + { + try + { + IList results = s.CreateQuery("from Customer cust left join fetch cust.BillingAddress where cust.CustomerId='abc123'").List(); + //IList results = s.CreateQuery("from Customer cust left join fetch cust.BillingAddress left join fetch cust.ShippingAddress").List(); + cust = (Customer)results[0]; + Assert.That(NHibernateUtil.IsInitialized(cust.ShippingAddress), Is.False); + Assert.That(NHibernateUtil.IsInitialized(cust.BillingAddress), Is.True); + Assert.That(cust.BillingAddress.Zip, Is.EqualTo("30326")); + Assert.That(cust.ShippingAddress.Zip, Is.EqualTo("30326")); + Assert.That(cust.BillingAddress.AddressId.Type, Is.EqualTo("BILLING")); + Assert.That(cust.ShippingAddress.AddressId.Type, Is.EqualTo("SHIPPING")); + t.Commit(); + } + catch + { + throw; + } + } + + using(ISession s = sessions.OpenSession()) + using(ITransaction t = s.BeginTransaction()) + { + try + { + s.SaveOrUpdate(cust); + ship = cust.ShippingAddress; + cust.ShippingAddress = null; + s.Delete("ShippingAddress", ship); + s.Flush(); + + Assert.That(s.Get("ShippingAddress", ship.AddressId), Is.Null); + s.Delete(cust); + + t.Commit(); + } + catch + { + throw; + } + } + } + + [Test] + public void TestCreateQueryNull() + { + var cust = new Customer(); + cust.CustomerId = "xyz123"; + cust.Name = "Matt"; + + using(ISession s = sessions.OpenSession()) + using(ITransaction t = s.BeginTransaction()) + { + s.Persist(cust); + t.Commit(); + } + + using(ISession s = sessions.OpenSession()) + using(ITransaction t = s.BeginTransaction()) + { + try + { + IList results = s.CreateQuery("from Customer cust left join fetch cust.BillingAddress where cust.CustomerId='xyz123'").List(); + //IList results = s.CreateQuery("from Customer cust left join fetch cust.BillingAddress left join fetch cust.ShippingAddress").List(); + cust = (Customer)results[0]; + Assert.That(cust.ShippingAddress, Is.Null); + Assert.That(cust.BillingAddress, Is.Null); + s.Delete(cust); + t.Commit(); + } + catch + { + throw; + } + } + } + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |