The Problem: ADF Application with multiple Data Sources or Database connections.
This is a pretty normal use-case, but can be a bit puzzling to new developers. Most apps have more than one database that they connect to. One way of dealing with this is to hide the fact there are multiple databases under the DB layer through the use of synonyms and DBlinks, but sometimes such access is not possible or desirable. Here I discuss how to use multiple databases from ADF BC.
Solution: Multiple Root Application Modules
The basic element here is the notion of the Root Application Module. For starters, the Root Application Module defines the transactional context. You need to create two application Modules, each one connecting to its own Database. You could also connect the very same application module to two different databases if you need to, more on that later. Nesting one application module inside another to get access to it won’t work as nested application modules share the same root.
So the way to do this is to create a *new* _Root_ Application Module directly from the BC4J configuration. This ensures that the connection associated with that Application module’s configuration is used. The ADF model layer usually takes care of handling instances of you Application Modules, and here, you are doing it manually. This brings some risk, and be careful to release the Application Module after use.
Create Project
Start out by creating one or more model projects in your application. In most cases you’ll only require one project, but if you need to create the ADF BC objects from database tables, you will likely need two projects so you don’t have to switch connections in the same project at design time. The “Connection” you see in the Project Properties > Business Components (click on Business Components) is used only during design time to generate ADF BC objects from a DB. An Application Module’s Configuration(bc4j.xml) defines the connection it uses at run time .
Figure 1
The two model projects will house the Application Modules that connect to the two different Databases.
Configuration
Once the model projects are created, open the Configuration dialog box for the Application modules. The connection an Application Module uses at runtime is defined in the Application Module’s configuration and not the project properties.
The Manage Configurations dialog lists the Configurations available for that AM. The configuration for an AM encapsulates the parameters for the AM like the connection it uses. The configuration name is important as we’ll later use it to identify a particular named configuration and use that named configuration to create an Application Module instance. To specify the parameters inside the named configuration, select the configuration name, and choose Edit
I’m assuming that a Data Source will be used. If using a JDBC URL, the steps are the same. A valid Data Source name is defined for the first DB and the steps should be repeated the same for the second Application Module connecting to the second DB. These Data sources need to be available at runtime, so either they need to exist in the Application server you are deploying to, or you need to deploy them as part of your application. I’m using the automatic deployment of DS provided by JDeveloper (this might not be suitable for production) so JDeveloper automatically provides a Data Source entry in this wizard based on the connections defined at the Application or IDE level. These Data sources have name that is the connection name appended with the two letters ‘DS’. After editing the Data source, you have effectively created a Configuration called DB_One_ServiceLocal (see screenshot) that uses the Data Source /jdbc/database_1DS . This configuration can now be used anytime to create a root application module with the configured parameters (like an alternate Data Source ).
A note about Data SourcesSee this for how to deploy a Data source manually. This explains how to make use of JDeveloper’s automatic data source deployment. |
Here is where most people get puzzled. After creating two Application Modules, you would be tempted to nest your second application module inside your first. This does not work, as the transaction context is defined for the rootapplication module, and when you nest, the root application module does not change, and so the connection does not change. The connection used is the root Application Module’s connection. The solution is to create a new RootApplication Module directly from the named BC4J configuration we created. This being a root application module on its own ensures that the connection associated with that Application module’s configuration is used.
The Code
The following screenshot shows the classes generated by JDeveloper, nothing out of the ordinary.
The second app module exposes a method on its client interface that the first application module invokes.
Here is the method the second AM exposes on its client interface:
- /**
- * Method exposed by this AM to provide the query results from its VO.
- * Since this AM's configuration connects it to the second database,
- * the VO query is executed agaist that DB
- *
- * @return
- */
- public String querySecondDB() {
- getDBTwoVO().executeQuery();
- return ((DBTwoVORowImpl) getDBTwoVO().first()).getSampledata();
- }
Now in order for the first application module t access and query the second data base, it creates a new root application module instance of the second application module using the named configuration that connects it to the second database. Once the new root application module instance is obtained, the exposed method is invoked and depending on the scenario, the root application module instance is released. The method used is Configuration. createRootApplicationModule()
- public String queryMulipleDBs(){
- getDBOneVO().executeQuery();
- String fromDBOne;
- String fromSecondDB;
- fromDBOne = ((DBOneVORowImpl) getDBOneVO().first()).getSampledata();
- // Create new root AppModule from the named Configuration
- DB_Two_Service service = (DB_Two_Service)Configuration.createRootApplicationModule("sample.model2.services.DB_Two_Service", "DB_Two_ServiceLocal");
- // Query the second DB
- fromSecondDB = service.querySecondDB();
- Configuration.releaseRootApplicationModule(service,true); //releases and removes the AppModule
- return "from first DB : " + fromDBOne + " from second DB : " +fromSecondDB;
- }
If you have the queryMulipleDBs() method of the first Application Module exposed on its client interface, you can use the Business components test to validate the whole thing since with 11g, the Business components tester can work with Data sources as well.
A note about multiple Projects and Application ModulesHere we used a separate project and an Application Module to connect to our second DB. This however is a personal preference, I like this because it keeps the code easily readable and separates the artifacts cleanly. The real crux of the technique is an alternative root application module using a particular configuration. So ineffect you could have two ApplicationModules in the same project with thier configurations connecting to two DBs or you could have the same ApplicationModule definition with just an alternative Configuration(that connects to the other DB) and instantiate a new instance of the same ApplicationModule definition as a root AM, with the alternate Configuration using the same code.
|