|
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.
|