views:

198

answers:

3

We use Spring + iBatis in all of our DAO's to fetch data from Stored Procedures.

There are two main JNDI connections. one going to the datawarehouse and another going to livedb.

recently lot of SPs have been moved from the livedb to the datawarehouse and vice versa.

This is creating issues on the java side because:

Now, each DAO does not directly JUST relate to either datawarehouse or livedb. There might be methods in DAO A which relate to datawarehouse and others might relate to livedb. In order to do this we have to change the sqlMapClientTemplate (because spring makes a dao have one to one mapping with JNDI connection). So we do this by:

this.setSqlMapClientTemplate(getSqlTemplDW()); //get connection to DW
getSqlMapClientTemplate().queryForList("dw_sps.somemapping", parmMap);
this.setSqlMapClientTemplate(getSqlTempl()); //set connection to live db

as you can see ...this is forcing us to have a lot of this same code in bunch of places.

Questions

Is it considered a design flaw to have one DAO talk to two different JNDI's? ( I know its not a design flaw in classic JDBC daos but is it different with Spring + iBatis?)

the getSqlTemplDW() method you see up there looks like:

public SqlMapClientTemplate getSqlTemplDW() {
    SqlMapClient scl = (SqlMapClient) ApplicationInitializer.getApplicationContext().getBean("SqlMapClientDW");
    DataSource dsc = (DataSource) ApplicationInitializer.getApplicationContext().getBean("DataSourceDW");
    return new SqlMapClientTemplate(dsc, scl);
}

as you can see, I am using javax.sql.DataSource. However, we have been told to not use this import!! So now I am stuck. I cant use this import (meaning cant change connections in my DAO). So I've been getting suggestions that every dao should only have one to one mapping to the JNDI.

I want to know..is there a way around this at all?

Skeleton

spring-for-ibatis.xml

<bean id="datasource1" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="jdbc/RSRC/asdf/sdf/oltp"/>
</bean>

<bean id="datasource2" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="jdbc/RSRC/asdf/efs/dw"/>
</bean>

<bean id="sqlMapClient1" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
  <property name="configLocation" value="classpath:sql-map-config-oracle.xml"/>
  <property name="dataSource" ref="datasource1"/>
</bean>

<bean id="sqlMapClient2" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
  <property name="configLocation" value="classpath:sql-map-config-dw.xml"/>
  <property name="dataSource" ref="datasource2"/>
</bean>

<!--dao bean-->
<bean id="examinationIfaceDAO" class="some.path.ExaminationIbatisDAO">
  <property name="sqlMapClient" ref="sqlMapClient1"/>
  <property name="dataSource" ref="datasource1"/>
</bean>

sql-map-config-oracle.xml

<sqlMapConfig>
   <settings enhancementEnabled="true" useStatementNamespaces="true" />
        <sqlMap resource="iBatis_file_with_sps_to_live_db.xml"/>
</sqlMapConfig>

sql-map-config-dw.xml

<sqlMapConfig>
   <settings enhancementEnabled="true" useStatementNamespaces="true" />
    <sqlMap resource="iBatis_file_with_sps_to_dw.xml" />
</sqlMapConfig>

Interface for Examination

 public interface ExaminationIfaceDAO {
    public boolean goToDW(String userId);
    public boolean goToLiveDB(String userId);
 }

ExaminationIbatisDAO

 public class ExaminationIbatisDAO implements EexaminationIfaceDAO {
    public boolean goToDW(String userId) {
        HashMap paramMap = new HashMap();
        paramMap.put("userId", userId);
        //following line will break as it does not know about this mapping file
        getSqlMapClientTemplate().queryForObject("iBatis_file_with_sps_to_dw.isAuthorized", paramMap);
        return true;
    }
    public boolean goToLiveDB(String userId) {
        HashMap paramMap = new HashMap();
        paramMap.put("userId", userId);
        //following line will be ok as it knows about this mapping file
        getSqlMapClientTemplate().queryForObject("iBatis_file_with_sps_to_live_db.isAuthorized", paramMap);
        return true;
    }
 }

calling all this from some action

examDAO = (ExaminationIfaceDAO)ApplicationInitializer.getApplicationContext().getBean("eexaminationIfaceDAO");
boolean b = reexamDAO.goToDW("myuserid");
A: 

JDBC or not, I consider that a Data Access Object abstracts one underlying data access implementation. So even if they share the same interface, I would provide two implementations if I have two data sources (whether they are two RDBMS or not).

Pascal Thivent
I'm convinced that DAO should be abstracting one data access. However, at the moment what could be my solution to get rid of all this same code in bunch of places. making each DAO go to just ONE jndi is a huge effort on our part right now
Omnipresent
@Omnipresent Why don't you inject two DAOs (maybe the same) but configured differently?
Pascal Thivent
I'm not sure if I understand what you mean or I dont know how to do it. Have an example I can see that injects two DAO's ?
Omnipresent
+1  A: 

It is not easy (for me) to understand your exact difficulty, perhaps it would help if you give us more skeleton of your DAO class and its relations with other spring managed beans. You say "spring makes a dao have one to one mapping with JNDI connection"; I don't get that. You surely can have (in your Spring container) a pair of DataSource beans (one for each database), and a corresponding pair SqlMapClientTemplate beans. Then you'd inject, into each DAO object, the two SqlMapClientTemplate beans and use (in each method) the one that points to the correct database. Am I missing something?

Update: looking at the skeleton, I see nothing that prevents you to have the two clientMaps injected int your dao, and instead of having one getSqlMapClientTemplate() having two methods: getSqlMapClientTemplateDb1() getSqlMapClientTemplateDb2() or whatever.

Perhaps there is some conceptual issue here.

A standard practice is to have the DAOs defined as interfaces and then implement the concrete classes for the particular framework or database. The goal is to ease the migration from one framework/database to another, without touching the interface. So that, for example, you can have a IUserDao interface with the method public User getUser(int id), and two different implementations -say- UserDaoPostgresql and UserDaoMysql; the methods would implement two alternative ways of doing the same thing (getting the user from alternative repositories). Typically, in this scenario, the upper layers will ignore this - and the concrete DAO to be used will be specified in the wiring (eg with Spring), and hence, fixed at deploy time. But only one implementation will be used in each deployed instance (except perhaps in some testing or migration code) and the code inside the dao (and also in the upper layers) should remain agnostic about these two alternative implementations.

But there are other scenarios. For example, when one has part of the application data in a Postgresql database, and another part in a Mysql db (or in another independent Pg db, or in some non relation db, even some logs). Then, as the role of the DAO is simply to abstract the acces to your data repositories, your IUserDao might have two methods getUser(int userid) getUserHistory(int userid) and it can perfectly happen that (in one particular implementation) each method must access a different database or resource. Here, it would not be at all bad practice to choose explicitly different datasources inside one DAO class.

Perhaps you should make clear if your scenario is the former or the later.

leonbloy
leon, I've added a skeleton. I think that will give better idea. From the skeleton, `ExaminationIbatisDAO` will break on `goToDW` method
Omnipresent
I elaborated my answer.
leonbloy
thanks for the detailed explanation leon. based on your answer, my scenarios is the later i.e. same DAO with different datasources. Now, `getSqlMapClientTemplate()` is a method in spring defined class `SqlMapClientDaoSupport`. How then can I have say `getSqlMapClientTemplateDb1` `getSqlMapClientTemplateDb2`? I think I am not understanding how in my skeleton I can inject the two clientMaps into my dao
Omnipresent
hmmm should I be doing something like this? http://static.springsource.org/spring/docs/3.0.0.M3/spring-framework-reference/html/ch14s05.html bottom of the page.
Omnipresent
You didnt say in your skel if your `ExaminationIbatisDAO` extends `SqlMapClientDaoSupport`
leonbloy
Yes, you can do a plain Dao (not extending a spring class) as in 14.5.3. And in that case you optionally can, instead of injecting a plain iBatis SqlMapClient bean, inject a Spring SqlMapClientTemplate (which wraps a SqlMapClient).
leonbloy
A: 

Better design would be to refactor your DAOs. Something like this.

public interface ExaminationIfaceDAO {
   boolean checkUser(String userId);
}

public class OracleExaminationDAO implements ExaminationIfaceDAO{
   public boolean checkUser(String userId){
      //TO:DO
   }
}
public class DWExaminationDAO implements ExaminationIfaceDAO{
  public boolean checkUser(String userId){
      //TO:DO
  }
}

Your DAOs look to be singletons and its not advisable to switch datasource like this.Also you can consider creating two different beans of same type one with livedb datasource and one with dw datasource. And use the appropriate bean for your task.

<bean id="examinationDBDAO" class="some.path.ExaminationIbatisDAO">
 <property name="sqlMapClient" ref="sqlMapClient1"/>
 <property name="dataSource" ref="datasource1"/>
</bean>
<bean id="examinationDWDAO" class="some.path.ExaminationIbatisDAO">
 <property name="sqlMapClient" ref="sqlMapClient1"/>
 <property name="dataSource" ref="datasource2"/>
</bean>
chedine