From: <fab...@us...> - 2009-06-20 15:21:53
|
Revision: 4494 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=4494&view=rev Author: fabiomaulo Date: 2009-06-20 15:21:09 +0000 (Sat, 20 Jun 2009) Log Message: ----------- Fix NH-1846 Modified Paths: -------------- trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/RootClassBinder.cs trunk/nhibernate/src/NHibernate/NHibernate.csproj trunk/nhibernate/src/NHibernate/NHibernateUtil.cs trunk/nhibernate/src/NHibernate/Type/TypeFactory.cs trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj Added Paths: ----------- trunk/nhibernate/src/NHibernate/Type/DbTimestampType.cs trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/DbVersionFixture.cs trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/Group.cs trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/Permission.cs trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/User.cs trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/User.hbm.xml Modified: trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/RootClassBinder.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/RootClassBinder.cs 2009-06-20 03:21:45 UTC (rev 4493) +++ trunk/nhibernate/src/NHibernate/Cfg/XmlHbmBinding/RootClassBinder.cs 2009-06-20 15:21:09 UTC (rev 4494) @@ -76,7 +76,20 @@ BindColumns(timestampSchema, simpleValue, propertyName); if (!simpleValue.IsTypeSpecified) - simpleValue.TypeName = NHibernateUtil.Timestamp.Name; + { + switch (timestampSchema.source) + { + case HbmTimestampSource.Vm: + simpleValue.TypeName = NHibernateUtil.Timestamp.Name; + break; + case HbmTimestampSource.Db: + simpleValue.TypeName = NHibernateUtil.DbTimestamp.Name; + break; + default: + simpleValue.TypeName = NHibernateUtil.Timestamp.Name; + break; + } + } var property = new Property(simpleValue); BindProperty(timestampSchema, property, inheritedMetas); Modified: trunk/nhibernate/src/NHibernate/NHibernate.csproj =================================================================== --- trunk/nhibernate/src/NHibernate/NHibernate.csproj 2009-06-20 03:21:45 UTC (rev 4493) +++ trunk/nhibernate/src/NHibernate/NHibernate.csproj 2009-06-20 15:21:09 UTC (rev 4494) @@ -604,6 +604,7 @@ <Compile Include="Tool\hbm2ddl\SchemaMetadataUpdater.cs" /> <Compile Include="Transaction\AdoNetWithDistrubtedTransactionFactory.cs" /> <Compile Include="Transform\ToListResultTransformer.cs" /> + <Compile Include="Type\DbTimestampType.cs" /> <Compile Include="Type\DefaultCollectionTypeFactory.cs" /> <Compile Include="Bytecode\ICollectionTypeFactory.cs" /> <Compile Include="Util\NullableDictionary.cs" /> Modified: trunk/nhibernate/src/NHibernate/NHibernateUtil.cs =================================================================== --- trunk/nhibernate/src/NHibernate/NHibernateUtil.cs 2009-06-20 03:21:45 UTC (rev 4493) +++ trunk/nhibernate/src/NHibernate/NHibernateUtil.cs 2009-06-20 15:21:09 UTC (rev 4494) @@ -224,6 +224,8 @@ /// NHibernate Timestamp type /// </summary> public static readonly NullableType Timestamp = new TimestampType(); + + public static readonly NullableType DbTimestamp = new DbTimestampType(); /// <summary> /// NHibernate TrueFalse type Added: trunk/nhibernate/src/NHibernate/Type/DbTimestampType.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Type/DbTimestampType.cs (rev 0) +++ trunk/nhibernate/src/NHibernate/Type/DbTimestampType.cs 2009-06-20 15:21:09 UTC (rev 4494) @@ -0,0 +1,94 @@ +using System; +using System.Data; +using System.Data.Common; +using log4net; +using NHibernate.Engine; +using NHibernate.Exceptions; +using NHibernate.SqlCommand; +using NHibernate.SqlTypes; + +namespace NHibernate.Type +{ + /// <summary> An extension of <see cref="TimestampType"/> which + /// maps to the database's current timestamp, rather than the vm's + /// current timestamp. + /// </summary> + /// <remarks> + /// Note: May/may-not cause issues on dialects which do not properly support + /// a true notion of timestamp + /// </remarks> + [Serializable] + public class DbTimestampType : TimestampType, IVersionType + { + private static readonly ILog log = LogManager.GetLogger(typeof (DbTimestampType)); + private static readonly SqlType[] EmptyParams = new SqlType[0]; + + public override string Name + { + get { return "DbTimestamp"; } + } + + public override object Seed(ISessionImplementor session) + { + if (session == null) + { + log.Debug("incoming session was null; using current vm time"); + return base.Seed(session); + } + else if (!session.Factory.Dialect.SupportsCurrentTimestampSelection) + { + log.Debug("falling back to vm-based timestamp, as dialect does not support current timestamp selection"); + return base.Seed(session); + } + else + { + return GetCurrentTimestamp(session); + } + } + + private object GetCurrentTimestamp(ISessionImplementor session) + { + Dialect.Dialect dialect = session.Factory.Dialect; + string timestampSelectString = dialect.CurrentTimestampSelectString; + return UsePreparedStatement(timestampSelectString, session); + } + + protected virtual object UsePreparedStatement(string timestampSelectString, ISessionImplementor session) + { + var tsSelect = new SqlString(timestampSelectString); + IDbCommand ps = null; + IDataReader rs = null; + try + { + ps = session.Batcher.PrepareCommand(CommandType.Text, tsSelect, EmptyParams); + rs = session.Batcher.ExecuteReader(ps); + rs.Read(); + DateTime ts = rs.GetDateTime(0); + if (log.IsDebugEnabled) + { + log.Debug("current timestamp retreived from db : " + ts + " (tiks=" + ts.Ticks + ")"); + } + return ts; + } + catch (DbException sqle) + { + throw ADOExceptionHelper.Convert(session.Factory.SQLExceptionConverter, sqle, + "could not select current db timestamp", tsSelect); + } + finally + { + if (ps != null) + { + try + { + session.Batcher.CloseCommand(ps, rs); + } + catch (DbException sqle) + { + log.Warn("unable to clean up prepared statement", sqle); + } + } + } + } + } +} \ No newline at end of file Modified: trunk/nhibernate/src/NHibernate/Type/TypeFactory.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Type/TypeFactory.cs 2009-06-20 03:21:45 UTC (rev 4493) +++ trunk/nhibernate/src/NHibernate/Type/TypeFactory.cs 2009-06-20 15:21:09 UTC (rev 4494) @@ -232,6 +232,7 @@ RegisterType(NHibernateUtil.Date, new[] { "date" }); RegisterType(NHibernateUtil.Timestamp, new[] { "timestamp" }); + RegisterType(NHibernateUtil.DbTimestamp, new[] { "dbtimestamp" }); RegisterType(NHibernateUtil.Time, new[] { "time" }); RegisterType(NHibernateUtil.TrueFalse, new[] { "true_false" }); RegisterType(NHibernateUtil.YesNo, new[] { "yes_no" }); Modified: trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2009-06-20 03:21:45 UTC (rev 4493) +++ trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2009-06-20 15:21:09 UTC (rev 4494) @@ -1349,11 +1349,15 @@ <Compile Include="UtilityTest\ThreadSafeDictionaryFixture.cs" /> <Compile Include="UtilityTest\TypeNameParserFixture.cs" /> <Compile Include="UtilityTest\WeakHashtableFixture.cs" /> + <Compile Include="VersionTest\Db\DbVersionFixture.cs" /> + <Compile Include="VersionTest\Db\Group.cs" /> <Compile Include="VersionTest\Db\MsSQL\BinaryTimestamp.cs" /> <Compile Include="VersionTest\Db\MsSQL\ComplexDomain.cs" /> <Compile Include="VersionTest\Db\MsSQL\ComplexDomainFixture.cs" /> <Compile Include="VersionTest\Db\MsSQL\GeneratedBinaryVersionFixture.cs" /> <Compile Include="VersionTest\Db\MsSQL\SimpleVersioned.cs" /> + <Compile Include="VersionTest\Db\Permission.cs" /> + <Compile Include="VersionTest\Db\User.cs" /> <Compile Include="VersionTest\Person.cs" /> <Compile Include="VersionTest\Task.cs" /> <Compile Include="VersionTest\Thing.cs" /> @@ -1918,6 +1922,7 @@ <EmbeddedResource Include="Bytecode\Lightweight\ProductLine.hbm.xml" /> <EmbeddedResource Include="DriverTest\MultiTypeEntity.hbm.xml" /> <Content Include="DynamicEntity\package.html" /> + <EmbeddedResource Include="VersionTest\Db\User.hbm.xml" /> <EmbeddedResource Include="NHSpecificTest\NH1831\Mappings.hbm.xml" /> <EmbeddedResource Include="NHSpecificTest\NH1069\Mappings.hbm.xml" /> <EmbeddedResource Include="NHSpecificTest\NH1837\Mappings.hbm.xml" /> Added: trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/DbVersionFixture.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/DbVersionFixture.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/DbVersionFixture.cs 2009-06-20 15:21:09 UTC (rev 4494) @@ -0,0 +1,111 @@ +using System; +using System.Collections; +using System.Threading; +using NUnit.Framework; + +namespace NHibernate.Test.VersionTest.Db +{ + [TestFixture] + public class DbVersionFixture : TestCase + { + protected override IList Mappings + { + get { return new[] {"VersionTest.Db.User.hbm.xml"}; } + } + + protected override string MappingsAssembly + { + get { return "NHibernate.Test"; } + } + + [Test] + public void CollectionVersion() + { + ISession s = OpenSession(); + ITransaction t = s.BeginTransaction(); + var guy = new User { Username = "guy" }; + s.Persist(guy); + var admin = new Group {Name = "admin"}; + s.Persist(admin); + t.Commit(); + s.Close(); + + DateTime guyTimestamp = guy.Timestamp; + + // For dialects (Oracle8 for example) which do not return "true + // timestamps" sleep for a bit to allow the db date-time increment... + Thread.Sleep(1500); + + s = OpenSession(); + t = s.BeginTransaction(); + guy = s.Get<User>(guy.Id); + admin = s.Get<Group>(admin.Id); + guy.Groups.Add(admin); + admin.Users.Add(guy); + t.Commit(); + s.Close(); + + Assert.That(!NHibernateUtil.Timestamp.IsEqual(guyTimestamp, guy.Timestamp), "owner version not incremented"); + + guyTimestamp = guy.Timestamp; + Thread.Sleep(1500); + + s = OpenSession(); + t = s.BeginTransaction(); + guy = s.Get<User>(guy.Id); + guy.Groups.Clear(); + t.Commit(); + s.Close(); + + Assert.That(!NHibernateUtil.Timestamp.IsEqual(guyTimestamp, guy.Timestamp), "owner version not incremented"); + + s = OpenSession(); + t = s.BeginTransaction(); + s.Delete(s.Load<User>(guy.Id)); + s.Delete(s.Load<Group>(admin.Id)); + t.Commit(); + s.Close(); + } + + [Test] + public void CollectionNoVersion() + { + ISession s = OpenSession(); + ITransaction t = s.BeginTransaction(); + var guy = new User {Username = "guy"}; + s.Persist(guy); + var perm = new Permission {Name = "silly", Access = "user", Context = "rw"}; + s.Persist(perm); + t.Commit(); + s.Close(); + + DateTime guyTimestamp = guy.Timestamp; + + s = OpenSession(); + t = s.BeginTransaction(); + guy = s.Get<User>(guy.Id); + perm = s.Get<Permission>(perm.Id); + guy.Permissions.Add(perm); + t.Commit(); + s.Close(); + + Assert.That(NHibernateUtil.Timestamp.IsEqual(guyTimestamp, guy.Timestamp), "owner version was incremented"); + + s = OpenSession(); + t = s.BeginTransaction(); + guy = s.Get<User>(guy.Id); + guy.Permissions.Clear(); + t.Commit(); + s.Close(); + + Assert.That(NHibernateUtil.Timestamp.IsEqual(guyTimestamp, guy.Timestamp), "owner version was incremented"); + + s = OpenSession(); + t = s.BeginTransaction(); + s.Delete(s.Load<User>(guy.Id)); + s.Delete(s.Load<Permission>(perm.Id)); + t.Commit(); + s.Close(); + } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/Group.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/Group.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/Group.cs 2009-06-20 15:21:09 UTC (rev 4494) @@ -0,0 +1,16 @@ +using System; +using Iesi.Collections.Generic; + +namespace NHibernate.Test.VersionTest.Db +{ + public class Group + { + public virtual long Id { get; set; } + + public virtual DateTime Timestamp { get; set; } + + public virtual string Name { get; set; } + + public virtual ISet<User> Users { get; set; } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/Permission.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/Permission.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/Permission.cs 2009-06-20 15:21:09 UTC (rev 4494) @@ -0,0 +1,17 @@ +using System; + +namespace NHibernate.Test.VersionTest.Db +{ + public class Permission + { + public virtual long Id { get; set; } + + public virtual DateTime Timestamp { get; set; } + + public virtual string Name { get; set; } + + public virtual string Context { get; set; } + + public virtual string Access { get; set; } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/User.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/User.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/User.cs 2009-06-20 15:21:09 UTC (rev 4494) @@ -0,0 +1,18 @@ +using System; +using Iesi.Collections.Generic; + +namespace NHibernate.Test.VersionTest.Db +{ + public class User + { + public virtual long Id { get; set; } + + public virtual DateTime Timestamp { get; set; } + + public virtual string Username { get; set; } + + public virtual ISet<Group> Groups { get; set; } + + public virtual ISet<Permission> Permissions { get; set; } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/User.hbm.xml =================================================================== --- trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/User.hbm.xml (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/VersionTest/Db/User.hbm.xml 2009-06-20 15:21:09 UTC (rev 4494) @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- + Demonstrates how to control the optimistic locking behavior + of a collection (do changes to the collection result in + a version increment on the owning instance) + --> +<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" + namespace="NHibernate.Test.VersionTest.Db" + assembly="NHibernate.Test"> + + <class name="User" table="db_vers_user"> + <id name="Id" column="user_id" type="long"> + <generator class="native"/> + </id> + <timestamp name="Timestamp" column="ts" source="db"/> + <property name="Username" column="user_name" type="string" unique="true"/> + <set name="Groups" table="db_vers_user_group" batch-size="9" inverse="true" optimistic-lock="true" lazy="true" cascade="none" > + <key column="user_id"/> + <many-to-many column="group_id" class="Group" lazy="false" fetch="join" /> + </set> + <set name="Permissions" table="db_vers_user_perm" batch-size="9" inverse="false" optimistic-lock="false" lazy="true" cascade="none"> + <key column="user_id"/> + <many-to-many column="perm_id" class="Permission" lazy="false" fetch="join"/> + </set> + </class> + + <class name="Group" table="db_vers_group"> + <id name="Id" column="group_id" type="long"> + <generator class="native"/> + </id> + <timestamp name="Timestamp" column="ts" source="db"/> + <property name="Name" column="name" type="string" unique="true"/> + <set name="Users" table="db_vers_user_group" batch-size="9" inverse="false" lazy="true" cascade="none" > + <key column="group_id"/> + <many-to-many column="user_id" class="User" lazy="false" fetch="join" /> + </set> + </class> + + <class name="Permission" table="db_vers_permission"> + <id name="Id" column="perm_id" type="long"> + <generator class="native"/> + </id> + <timestamp name="Timestamp" column="ts" source="db"/> + <property name="Name" column="name" type="string" unique="true"/> + <property name="Context" column="ctx" type="string"/> + <property name="Access" column="priv" type="string"/> + </class> +</hibernate-mapping> \ No newline at end of file This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |