02.10.06
More details about Integrating Db4o with Spring.NET
After having spent several more hours on the Db4o/Spring.NET integration issue, here are some of my conclusions concerning Spring.NET internals :
Most of the logic for integrating a Data Access/Transaction support technology to Spring.NET is to be implemented inside 3 major entities :
- The PlatformTransactionManager (Db4oPlatformTransationManager) : definitely the main entity on the system, its purpose is to manage the transactions (Commit, Rollback, and Get a (New/Existing) Transaction.
- The Utilities (Db4oUtils) : Its role is mainly to create / dispose connections. However, it does NOT blindly create connections and dispose them. In fact, it must take into account the fact that most applications are multithreaded (e.g. Web Sites), so the releasing a connection must not affect another thread’s connection.
- The Template (Db4oTemplate) : Its role is simply to remove the connection creation/release burden from user code. The template takes care of dealing with Spring.NET internals, threads, synchronization, etc. As a result, the user code just deals with a connection to the database that it can use to perform its tasks.
Let’s see how everything fits together :
- First of all, it is important to realize that connection creation is an important part of the whole system. In fact, in a multi-threaded environment, several concurrent accesses to the database will be necessary. As a result, it is not possible to have code (e.g. Data Access Objects - DAO) keep references to active connections since it woud mess the whole system. To concurrently access the same database at the same time, several threads must use different connections, so data access code should not maintain references to connections.
As a result, most parts of Spring.NET (Template, etc) will keep a reference to a Factory Object that allows the creation of connections, instead of keeping a reference to a connection directly. This Factory is the DataSource, in the case of Spring Java and IDb4oDataSource in the case of my Db4o to Spring.NET integration. - The Spring.NET templates (Db4oTemplate) allow the removal of connection references from Data Access Objects. Indeed, when using templates, DAOs provide delegates (implemented as Object callbacks for Spring Java, since delegates (function pointers) do not exist in Java) whose signature is given by the template. The delegate has the connection object passed as a parameter, so the DAO can actually perform its work without having to care about which connection it will work on. Look at Spring (Java) reference manual which gives quite a lot of information about this.
As a result, all the connection creation/release tasks are handled by the template instead of the user. This is coded once in the template, and avoids unecessary boilerplate code. Basicaly, here is how (roughly) a template Execute method is coded : - Get a Connection
- Call the delegate provided by the user : delegateMethod(Connection)
- Close the Connection
- However, Connection Creation and Release is a quite complex task. As a result, it is handled by a utilitary class : Db4oUtils. Here is why it is complex :
- Connections used by each Thread should be handled separately. Thread X should not mess with Thread Y’s connection, or it would lead to race conditions.
- It is not possible nor desirable to create a new connection each time a connection is requested. In fact, the connection should be kept until the transaction is Commit’ed or Rollback’ed, and several calls to GetConnection() can happen before, if, for example, several Db4oTemplates are used.
- Sometimes, we do not want to take part in a Transaction, so the case where there is no transaction to synchronize to should also be taken into consideration.
- Additionnally, Exceptions should be converted to Spring.NET exception Hierarchy..
- In order to handle each thread’s connection separately, a TransactionSynchronizationManager is used. Its role is simply to associate resources and give different resource-spaces to each thread.
- The resources that the TransactionSynchronizationManager deals with are not the connections direcltly, but ConnectionHolders. The subtle difference is that the holder maintains a reference to the Connection, but also keeps some information such as a reference count of users of the connection. So, in the case of nested GetConnection() calls, the reference count will indicate that the resource is still used even though a Dispose() will be done after the nested GetConnection().
- The TransactionSynchronizationManager uses an event-based approach with listeners (called Synchronizations to the resources) in order to do the cleanup of the resources; Since the TransactionSynchronizationManager works with all kinds of connections (ADO.NET, Db4o, etc), it cannot understand how it should cleanup resources after a cleanup, so this is the task of a Synchronization that listen to events thrown by the Manager.
Previous issues :