I'm working on a system that performs bulk processing using NHibernate. I know that NHibernate was not designed for bulk processing, but nonetheless the system is working perfectly thanks to a number of optimizations.
The object at the lowest level of granularity (i.e. the root of my aggregates) has a number of string properties that cannot (or, it does not make sense to) be modeled as many-to-one's (e.g. "Comment"). In reality, the fields in the DB corresponding to these properties take only so many values (for example because most - but not all - comments are machine-generated), with the result that when hydrating tons of objects, lots of memory is wasted by having thousands and thousands of instances of 开发者_开发技巧strings with the same values.
I was thinking of optimizing this scenario transparently by creating my own NHibernate custom type that enhances NHibernate's StringType
by overriding NullSafeGet()
and doing a dictionary lookup to return the same instance of each string occurrence over and over. In other words, I would perform a kind of string interning myself. The use of a custom type allows me to select which properties of which objects should be "interned" by just specifying this type in the mapping files.
Ideally, I would like to "stick" this dictionary into the session, so that the lifetime of this string pool is tied with the lifetime of the first level cache. After all, from our system's point of view, it makes sense to intialize this string pool at the same time a session and its first-level cache are initialized, and to nuke the string pool at the same time a session is closed. It is also a desirable property that concurrent sessions are completely isolated from each other by having their own private dictionaries.
Problem is, I can't find a way to "inject" a custom implementation of NHibernate's session into NHibernate itself so that an IType
can access it at NullSafeGet()
time, short of creating my own personal NHibernate code branch.
Is there a way to provide NHibernate with a custom session implementation?
I see three different approaches to solve this:
1. Use a interceptor
In the IInterceptor, you get:
void AfterTransactionBegin(ITransaction tx);
void BeforeTransactionCompletion(ITransaction tx);
2. Wrap opening and closing the session:
Opening and closing the session is an explicit call. It should be easy to wrap this into a method.
public ISession OpenSession()
{
var session = sessionFactory.CreateSession();
StringType.Initialize();
}
You could make it much nicer. I wrote a transaction service, which has events. Then you could handle begin transaction and end transaction events.
3. Don't attach the string cache to the session
It doesn't need to be related to the session. The strings are immutable objects, it doesn't hurt when you mix them between sessions. To avoid that the cache grows unlimitedly, you could write your own or use an existing "most recently used"-cache. After growing to a certain size, it throws away the oldest items.
This would probably require some time to implement, but would be very nice and easy to use.
精彩评论