View Full Version : HibernateTransactionManager without DbProvider
harald
09-22-2006, 05:15 PM
Is the HibernateTransactionManager supposed to work without DbProvider?
My original problem is, that i use Spring.NET and NHibernate for developing a winform application where the datasource is not known a priori (at least not the credentials). Thus I wrote my own SessionFactoryObject that delays the NHibernate initialization until all configuration data is collected which worked pretty fine until with the old Spring.Orm assembly.
After updating to the latest nightly build of the Spring.Data.NHibernate assembly I kept running into problems with that "solution".
First a NotImplementedException was thrown inside AfterPropertiesSet because AutodetectDataSource defaults to true and DbProvider was not set.
After setting AutodetectDataSource to false i got a NRE inside the DoBegin method where DbProvider was used without a not null check. As this was the only place without a not null check i supposed it was an error and fixed it.
Now I was at least able to initialize my application context without an error, but after trying to do my first NHibernate transaction i got a NotImplementedException inside SpringSessionSynchronization.AfterCompletion.
To circumvent the problem for now i wrote a DeferredDbProvider that takes an uninitialized DbProvider and a SessionFactory and initializes the DbProvider with the SessionFactory's ConnectionString on its first access (not during application context startup). My currently working TransactionManager definition looks like this:
<object id="TransactionManager" type="Spring.Data.NHibernate.HibernateTransactionManager , Spring.Data.NHibernate">
<property name="SessionFactory" ref="SessionFactory"/>
<property name="AutodetectDataSource" value="false"/>
<property name="DbProvider">
<object type="SpringUtils.Data.Support.DeferredDbProvider, SpringUtils">
<property name="WrappedDbProvider">
<object type="SpringUtils.Data.Support.Npgsql.NpgsqlProvider, SpringUtils"/>
</property>
<property name="SessionFactory" ref="SessionFactory"/>
</object>
</property>
</object>
As my solution involves quite a lot hacking I ponder if this is the way to go. Frankly I did not look at the code very long, but right now I can't think of a reason why the HibernateTransactionManager should depend on a DbProvider, the NHibernate session should be sufficient.
I'm awaiting your feedback so that i can start fixing this to work as expected (whatever that means :), at least i can contribute my NpgsqlProvider someone can point me to where i should submit my patch.
Mark Pollack
09-25-2006, 03:45 PM
Hi,
Just a quick reply...I've been away and am busy catching up. You are right that for "pure" NHibernate based data access the DbProvider isn't necessary. However, the current impl does require DbProvider since I want to support the mixing of ado.net (adotemplate at least) and nhibernate code within the same transaction - even on .NET 1.1 with local transaction management. The lack of a standard database provider abstraction hurts in this case as each database related library has rolled their own equivalent. Hopefully .NET 2.0 based solutions will be more standardized. It seems the only common ground between all of them would be connection string. This is an area I need to investigate. In anycase, in your situation where you need to defer the setting of user credentials you will need some solution along the lines you have done, even if the above issue is sorted out. I'll ping back here when I look into supporting AutodetectDataSource properly. I'd be interested in your code nevertheless as we could provide it in the distribution for cases where there is deferred authentication. Spring.Java has a variety of these delegating datasources for this purpose - and others.
Cheers,
Mark
harald
09-26-2006, 03:18 PM
For what I can tell you .NET 2.0 does not bear that much new stuff when it comes to database abstraction. They added some kind of a provider factory, but the factory itself is still only aware of the bundled drivers.
There are ways to make the factory aware of custom drivers via the GAC and some additional registry entries, but neither the Npgsql driver nor the mysql.net connector could be added this way.
Even the commercial drivers i tried did no work as expected. They did show up in the list of drivers provided by the factory but when trying to instanciate them everything crashed.
So because of the lack of interfaces you are still stuck to the connection string in .NET 2.0 as everything else is too much hassle.
I'll post a link to my sources as soon as we finish moving to our new server, which is expected to be somewhen next week. I also have to sort some things out first to have a more generic solution than my very use-case specific hack.
harald
11-10-2006, 10:16 AM
The promised link to my sources:
http://springutils.fluffnstuff.org/
harald
11-30-2006, 09:22 PM
I'm back on this topic after properly upgrading to NHibernate 1.2 and the latest Spring.NET sources. It seems that with the new generic DbProvider code the problem is almost solved.
If you would extract the DbProvider from the sessionfactory in the getter instead of the AfterPropertiesSet() method my issue would be more or less completely solved.
Here is a patch (also including a minor bugfix in the GetDbProvider() method): http://harald.fluffnstuff.org/attachment/wiki/2006/11/30/22.07/Spring.Net.Integration.patch
harald
11-30-2006, 09:29 PM
I'm also willing to share my Npgsql definitions, here they are:
<object id="Npgsql" type="Spring.Data.Common.DbProvider, Spring.Data">
<constructor-arg name="dbMetaData">
<object type="Spring.Data.Common.DbMetadata, Spring.Data">
<constructor-arg name="productName" value="Npgsql" />
<constructor-arg name="assemblyName" value="Npgsql, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7"/>
<constructor-arg name="connectionType" value="Npgsql.NpgsqlConnection, Npgsql, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7"/>
<constructor-arg name="commandType" value="Npgsql.NpgsqlCommand, Npgsql, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7"/>
<constructor-arg name="parameterType" value="Npgsql.NpgsqlParameter, Npgsql, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7"/>
<constructor-arg name="dataAdapterType" value="Npgsql.NpgsqlDataAdapter, Npgsql, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7"/>
<constructor-arg name="commandBuilderType" value="Npgsql.NpgsqlCommandBuilder, Npgsql, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7"/>
<constructor-arg name="commandBuilderDeriveParametersMethod" value="DeriveParameters"/>
<constructor-arg name="parameterDbType" value="NpgsqlTypes.NpgsqlDbType, Npgsql, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7"/>
<constructor-arg name="parameterDbTypeProperty" value="NpgsqlDbType"/>
<constructor-arg name="parameterIsNullableProperty" value="IsNullable"/>
<constructor-arg name="parameterNamePrefix" value="?"/>
<constructor-arg name="exceptionType" value="Npgsql.NpgsqlException, Npgsql, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7"/>
<constructor-arg name="useParameterNamePrefixInParameterCollection" value="true"/>
<constructor-arg name="bindByName" value="true"/>
<!-- this is only true for .net 1.1 kept it here just in case we want to revert back to this strategy for
obtaining error codes-->
<constructor-arg name="errorCodeExceptionExpression" value="Errors[0].Code"/>
<property name="ErrorCodes.badSqlGrammarCodes">
<value></value>
</property>
<property name="ErrorCodes.DataAccessResourceFailureCodes">
<value></value>
</property>
<property name="ErrorCodes.DataIntegrityViolationCodes">
<value></value>
</property>
<property name="ErrorCodes.CannotAcquireLockCodes">
<value></value>
</property>
<property name="ErrorCodes.DeadlockLoserCodes">
<value></value>
</property>
</object>
</constructor-arg>
</object>
I'll extend the error codes as soon as I encounter those errors :)
Mark Pollack
12-01-2006, 01:46 AM
Hi,
Thanks again. I added the bug fix and the provider definitions. I changed the name to Npgsql-1.0 so we can support multiple versions. One question though, why doesn't doing the derivation of dbprovider from sessionfactory in afterproperties set meet your needs?
Mark
harald
12-04-2006, 09:00 AM
Great! The reason for doing this is that I do not know the database credentials during context startup, thus the sessionfactory is not yet initialized (no connection string set). Deriving a dbprovider from a not initialized sessionfactory obviously doesn't work :)
The reason for having an uninitialized sessionfactory is that I also define my Views (WinForms) in the application context and link them together using DI. I could show the login dialog before starting the application context of course, but this would mean, that i cannot use NHibernate to authenticate the user (i.e. checking if a User object with the given login name exists).
For me the optimal choise was to use a DeferredSessionFactory class (http://springutils.fluffnstuff.org/browser/trunk/SpringUtils/Data/NHibernate/SessionFactoryObject.cs#L133) that gets the actual credentials injected by the login dialog and than perform a HQL query for the User object with the given login (wich implies a l/p check as the connection to the db would not be established otherwise).
For the curious: Unlike in a web application I cannot use a common l/p for the database as in a client/server environment the database is accessible from everyone but not everyone should be able to see all tables and data. I have to limit the data that can be seen per user by using Views and Database ACLs, thus i need real db users.
harald
12-07-2006, 04:32 PM
I tidied my code a bit and now it would even work with the "old" AfterPropertiesSet code. I figured out that I can initially pass a fake connection string and change it to the actual one later. NHibernate is only complaining if no connection string is set, but it doesn't check if the set one is valid.
As long as the right connection string is set before the first database access everything works fine, even deriving the dbprovider from the sessionfactory as this doesn't involve the connectionstring either.
My NHibernate related GUI code seems rather useable now, is there a place where i could put it for others to play with?
Powered by vBulletin® Version 4.1.5 Copyright © 2012 vBulletin Solutions, Inc. All rights reserved.