From: <fab...@us...> - 2008-11-04 12:08:39
|
Revision: 3888 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=3888&view=rev Author: fabiomaulo Date: 2008-11-04 12:08:34 +0000 (Tue, 04 Nov 2008) Log Message: ----------- Fix NH-871 (new select generator) Modified Paths: -------------- trunk/nhibernate/src/NHibernate/Id/IdentifierGeneratorFactory.cs trunk/nhibernate/src/NHibernate/Id/Insert/AbstractSelectingDelegate.cs trunk/nhibernate/src/NHibernate/NHibernate.csproj trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj Added Paths: ----------- trunk/nhibernate/src/NHibernate/Id/SelectGenerator.cs trunk/nhibernate/src/NHibernate.Test/Generatedkeys/Select/ trunk/nhibernate/src/NHibernate.Test/Generatedkeys/Select/MyEntity.cs trunk/nhibernate/src/NHibernate.Test/Generatedkeys/Select/MyEntity.hbm.xml trunk/nhibernate/src/NHibernate.Test/Generatedkeys/Select/SelectGeneratorTest.cs Modified: trunk/nhibernate/src/NHibernate/Id/IdentifierGeneratorFactory.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Id/IdentifierGeneratorFactory.cs 2008-11-02 22:55:51 UTC (rev 3887) +++ trunk/nhibernate/src/NHibernate/Id/IdentifierGeneratorFactory.cs 2008-11-04 12:08:34 UTC (rev 3888) @@ -165,6 +165,7 @@ idgenerators.Add("foreign", typeof(ForeignGenerator)); idgenerators.Add("guid", typeof(GuidGenerator)); idgenerators.Add("guid.comb", typeof(GuidCombGenerator)); + idgenerators.Add("select", typeof(SelectGenerator)); } private IdentifierGeneratorFactory() Modified: trunk/nhibernate/src/NHibernate/Id/Insert/AbstractSelectingDelegate.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Id/Insert/AbstractSelectingDelegate.cs 2008-11-02 22:55:51 UTC (rev 3887) +++ trunk/nhibernate/src/NHibernate/Id/Insert/AbstractSelectingDelegate.cs 2008-11-04 12:08:34 UTC (rev 3888) @@ -51,7 +51,7 @@ try { //fetch the generated id in a separate query - IDbCommand idSelect = session.Batcher.PrepareCommand(CommandType.Text, selectSQL, SqlTypeFactory.NoTypes); + IDbCommand idSelect = session.Batcher.PrepareCommand(CommandType.Text, selectSQL, ParametersTypes); try { BindParameters(session, idSelect, binder.Entity); @@ -91,10 +91,22 @@ /// <returns> The generated identifier </returns> protected internal abstract object GetResult(ISessionImplementor session, IDataReader rs, object entity); - /// <summary> Bind any required parameter values into the SQL command {@link #getSelectSQL}. </summary> + /// <summary> Bind any required parameter values into the SQL command <see cref="SelectSQL"/>. </summary> /// <param name="session">The session </param> - /// <param name="ps">The prepared {@link #getSelectSQL SQL} command </param> + /// <param name="ps">The prepared <see cref="SelectSQL"/> command </param> /// <param name="entity">The entity being saved. </param> - protected internal virtual void BindParameters(ISessionImplementor session, IDbCommand ps, object entity) {} + protected internal virtual void BindParameters(ISessionImplementor session, IDbCommand ps, object entity) { } + + #region NH Specific + + /// <summary> + /// Types of any required parameter values into the SQL command <see cref="SelectSQL"/>. + /// </summary> + protected internal virtual SqlType[] ParametersTypes + { + get { return SqlTypeFactory.NoTypes; } + } + + #endregion } } \ No newline at end of file Added: trunk/nhibernate/src/NHibernate/Id/SelectGenerator.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Id/SelectGenerator.cs (rev 0) +++ trunk/nhibernate/src/NHibernate/Id/SelectGenerator.cs 2008-11-04 12:08:34 UTC (rev 3888) @@ -0,0 +1,126 @@ +using System.Collections.Generic; +using System.Data; +using NHibernate.Engine; +using NHibernate.Id.Insert; +using NHibernate.Persister.Entity; +using NHibernate.SqlCommand; +using NHibernate.SqlTypes; +using NHibernate.Type; + +namespace NHibernate.Id +{ + /// <summary> + /// A generator that selects the just inserted row to determine the identifier + /// value assigned by the database. The correct row is located using a unique key. + /// </summary> + /// <remarks>One mapping parameter is required: key (unless a natural-id is defined in the mapping).</remarks> + public class SelectGenerator : AbstractPostInsertGenerator, IConfigurable + { + private string uniqueKeyPropertyName; + + #region Overrides of AbstractPostInsertGenerator + + public override IInsertGeneratedIdentifierDelegate GetInsertGeneratedIdentifierDelegate( + IPostInsertIdentityPersister persister, ISessionFactoryImplementor factory, bool isGetGeneratedKeysEnabled) + { + return new SelectGeneratorDelegate(persister, factory, uniqueKeyPropertyName); + } + + #endregion + + #region Implementation of IConfigurable + + public void Configure(IType type, IDictionary<string, string> parms, Dialect.Dialect dialect) + { + parms.TryGetValue("key", out uniqueKeyPropertyName); + } + + #endregion + + private static string DetermineNameOfPropertyToUse(IEntityPersister persister, string supplied) + { + if (supplied != null) + { + return supplied; + } + int[] naturalIdPropertyIndices = persister.NaturalIdentifierProperties; + if (naturalIdPropertyIndices == null) + { + throw new IdentifierGenerationException("no natural-id property defined; need to specify [key] in " + + "generator parameters"); + } + if (naturalIdPropertyIndices.Length > 1) + { + throw new IdentifierGenerationException("select generator does not currently support composite " + + "natural-id properties; need to specify [key] in generator parameters"); + } + ValueInclusion inclusion = persister.PropertyInsertGenerationInclusions[naturalIdPropertyIndices[0]]; + if (inclusion != ValueInclusion.None) + { + throw new IdentifierGenerationException("natural-id also defined as insert-generated; need to specify [key] " + + "in generator parameters"); + } + return persister.PropertyNames[naturalIdPropertyIndices[0]]; + } + + #region Nested type: SelectGeneratorDelegate + + /// <summary> The delegate for the select generation strategy.</summary> + public class SelectGeneratorDelegate : AbstractSelectingDelegate + { + private readonly ISessionFactoryImplementor factory; + private readonly SqlString idSelectString; + private readonly IType idType; + private readonly IPostInsertIdentityPersister persister; + + private readonly string uniqueKeyPropertyName; + private readonly IType uniqueKeyType; + + internal SelectGeneratorDelegate(IPostInsertIdentityPersister persister, ISessionFactoryImplementor factory, + string suppliedUniqueKeyPropertyName) : base(persister) + { + this.persister = persister; + this.factory = factory; + uniqueKeyPropertyName = DetermineNameOfPropertyToUse((IEntityPersister) persister, suppliedUniqueKeyPropertyName); + + idSelectString = persister.GetSelectByUniqueKeyString(uniqueKeyPropertyName); + uniqueKeyType = ((IEntityPersister) persister).GetPropertyType(uniqueKeyPropertyName); + idType = persister.IdentifierType; + } + + protected internal override SqlString SelectSQL + { + get { return idSelectString; } + } + + protected internal override SqlType[] ParametersTypes + { + get { return uniqueKeyType.SqlTypes(factory); } + } + + public override IdentifierGeneratingInsert PrepareIdentifierGeneratingInsert() + { + return new IdentifierGeneratingInsert(factory); + } + + protected internal override void BindParameters(ISessionImplementor session, IDbCommand ps, object entity) + { + object uniqueKeyValue = ((IEntityPersister) persister).GetPropertyValue(entity, uniqueKeyPropertyName, + session.EntityMode); + uniqueKeyType.NullSafeSet(ps, uniqueKeyValue, 0, session); + } + + protected internal override object GetResult(ISessionImplementor session, IDataReader rs, object entity) + { + if (!rs.Read()) + { + throw new IdentifierGenerationException("the inserted row could not be located by the unique key: " + + uniqueKeyPropertyName); + } + return idType.NullSafeGet(rs, persister.RootTableKeyColumnNames, session, entity); + } + } + + #endregion + } +} \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/NHibernate.csproj =================================================================== --- trunk/nhibernate/src/NHibernate/NHibernate.csproj 2008-11-02 22:55:51 UTC (rev 3887) +++ trunk/nhibernate/src/NHibernate/NHibernate.csproj 2008-11-04 12:08:34 UTC (rev 3888) @@ -440,6 +440,7 @@ <Compile Include="AdoNet\ResultSetWrapper.cs" /> <Compile Include="AdoNet\SqlClientBatchingBatcherFactory.cs" /> <Compile Include="AdoNet\TooManyRowsAffectedException.cs" /> + <Compile Include="Id\SelectGenerator.cs" /> <Compile Include="Properties\BackFieldStrategy.cs" /> <Compile Include="Bytecode\CodeDom\BytecodeProviderImpl.cs" /> <Compile Include="Bytecode\IAccessOptimizer.cs" /> Added: trunk/nhibernate/src/NHibernate.Test/Generatedkeys/Select/MyEntity.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Generatedkeys/Select/MyEntity.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Generatedkeys/Select/MyEntity.cs 2008-11-04 12:08:34 UTC (rev 3888) @@ -0,0 +1,25 @@ +namespace NHibernate.Test.Generatedkeys.Select +{ + public class MyEntity + { + private int id; + private string name; + protected MyEntity() {} + + public MyEntity(string name) + { + this.name = name; + } + + public virtual int Id + { + get { return id; } + } + + public virtual string Name + { + get { return name; } + set { name = value; } + } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/Generatedkeys/Select/MyEntity.hbm.xml =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Generatedkeys/Select/MyEntity.hbm.xml (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Generatedkeys/Select/MyEntity.hbm.xml 2008-11-04 12:08:34 UTC (rev 3888) @@ -0,0 +1,43 @@ +<?xml version="1.0"?> +<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" + assembly="NHibernate.Test" + namespace="NHibernate.Test.Generatedkeys.Select" + default-access="field"> + + <class name="MyEntity" table="my_entity"> + + <id name="id"> + <generator class="select"/> + </id> + <natural-id> + <property name="name"/> + </natural-id> + </class> + + <database-object> + <create> +CREATE GENERATOR MYGENERATOR; + </create> + <drop> +DROP GENERATOR MYGENERATOR; + </drop> + <dialect-scope name="NHibernate.Dialect.FirebirdDialect"/> + </database-object> + + <database-object> + <create> +CREATE TRIGGER my_entity_BI FOR my_entity +ACTIVE BEFORE INSERT +POSITION 0 +AS +BEGIN +NEW.ID = GEN_ID (MYGENERATOR, 1); +END + </create> + <drop> +DROP TRIGGER my_entity_BI; + </drop> + <dialect-scope name="NHibernate.Dialect.FirebirdDialect"/> + </database-object> + +</hibernate-mapping> \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/Generatedkeys/Select/SelectGeneratorTest.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/Generatedkeys/Select/SelectGeneratorTest.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/Generatedkeys/Select/SelectGeneratorTest.cs 2008-11-04 12:08:34 UTC (rev 3888) @@ -0,0 +1,42 @@ +using System.Collections; +using NUnit.Framework; + +namespace NHibernate.Test.Generatedkeys.Select +{ + [TestFixture] + public class SelectGeneratorTest: TestCase + { + protected override IList Mappings + { + get { return new[] { "Generatedkeys.Select.MyEntity.hbm.xml" }; } + } + + protected override string MappingsAssembly + { + get { return "NHibernate.Test"; } + } + + protected override bool AppliesTo(Dialect.Dialect dialect) + { + return dialect is Dialect.FirebirdDialect; + } + + [Test] + public void GetGeneratedKeysSupport() + { + ISession session = OpenSession(); + session.BeginTransaction(); + + MyEntity e = new MyEntity("entity-1"); + session.Save(e); + + // this insert should happen immediately! + Assert.AreEqual(1, e.Id, "id not generated through forced insertion"); + + session.Delete(e); + session.Transaction.Commit(); + session.Close(); + } + + } +} \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2008-11-02 22:55:51 UTC (rev 3887) +++ trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2008-11-04 12:08:34 UTC (rev 3888) @@ -243,6 +243,8 @@ <Compile Include="Generatedkeys\Identity\MyChild.cs" /> <Compile Include="Generatedkeys\Identity\MyEntity.cs" /> <Compile Include="Generatedkeys\Identity\MySibling.cs" /> + <Compile Include="Generatedkeys\Select\MyEntity.cs" /> + <Compile Include="Generatedkeys\Select\SelectGeneratorTest.cs" /> <Compile Include="GeneratedTest\AbstractGeneratedPropertyTest.cs" /> <Compile Include="GeneratedTest\Component.cs" /> <Compile Include="GeneratedTest\ComponentOwner.cs" /> @@ -1515,6 +1517,7 @@ <EmbeddedResource Include="Cascade\JobBatch.hbm.xml" /> <EmbeddedResource Include="Deletetransient\Person.hbm.xml" /> <Content Include="DynamicEntity\package.html" /> + <EmbeddedResource Include="Generatedkeys\Select\MyEntity.hbm.xml" /> <EmbeddedResource Include="NHSpecificTest\NH1478\Mappings.hbm.xml" /> <EmbeddedResource Include="NHSpecificTest\NH1447\Mappings.hbm.xml" /> <EmbeddedResource Include="TypesTest\EnumCharClass.hbm.xml" /> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |