This idea is based Spring's AbstractRoutingDataSource class, but operates with SqlMapClients instead to avoid caching issues in iBATIS. Since
SqlMapClient doesn't segment its cache based on DataSources, a single SqlMapClient will merge all the results from multiple DataSources into one cache.
Below are two classes, RoutableSqlMapClient and VendorContextHolder. RoutableSqlMapClient is a class that implements the ExtendedSqlMapClient
interface but delegates all calls to other SqlMapClients. It does the delegation by using VendorContextHolder, which holds a static ThreadLocal variable, which for my needs is represents a VendorTypes enum. This ThreadLocal variable is a key into a Map of target delegate SqlMapClient objects. Each consumer calling into this code will pass along a vendor, which is used to set the ThreadLocal variable in VendorContextHolder.
Here is how the relevant Spring xml config looks:
The consumers of this API use a Service Locator/Manager that wraps Spring. When a thread wants a DAO, the code looks like this:
It is entirely possible to not have a Service Locater/Manager and have the consumer set the VendorContextHolder directly. The Service Locator/Manager exists in this example because it's a common pattern.
At this point, the DAO just calls the ExtendedSqlMapClient methods as normal, but they get routed by RoutableSqlMapClient to a vendor specific SqlMapClient, complete with its own DataSource, TransactionManager, cache, etc.
There is a limitation with this approach that should be evaluated before use. As you can see, this scheme sets a ThreadLocal variable that determines the DataSource to use for the remainder of that Thread's execution. So in my case, if the Thread needs a different vendor, it
must make that call to the Service Locator with the different vendor. But this means a Thread can effectively only hit one DataSource at a
time, it is <b>not possible</b> to intermix access to multiple DataSources from the same Thread.
For example, this won't work:
Because the DataSource is determined on a per-Thread basis, the second call to getUserDAO will set the vendor to VendorTwo. Plus there's
really only one UserDAO in the Spring Context. Regardless, the first call to update(vendorOneUser) will actually hit the VendorTwo
DataSource. And really, if you're using Spring correctly, you probably aren't using a Service Locator/Manager, so it may not matter. In that
case, you probably have a single DAO pointer and would use it something like this:
Which is not ideal because your consumer code has to know about the VendorContextHolder, but it works.
I've also attached a servlet Filter that looks for a 'vendor' parameter
in the request URL and sets the VendorContextHolder accordingly. I use
this for a fat client that talks to a remoting service via a webapp, but
it should work for all-HTML webapps too.