如何使用Spring + iBatis在我的应用程序中解决设计问题

时间:2010-06-16 10:52:56

标签: java spring ibatis

我们在所有DAO中使用Spring + iBatis来从存储过程中获取数据。

有两个主要的JNDI连接。一个进入datawarehouse,另一个进入livedb

最近很多SP已从livedb移到数据仓库,反之亦然。

这会在java方面产生问题,因为:

现在,每个DAO都不直接与datawarehouse或livedb相关。 DAO A中可能存在与数据仓库相关的方法,而其他方法可能与livedb相关。为了做到这一点,我们必须更改sqlMapClientTemplate(因为spring使得dao与JNDI连接具有一对一的映射)。所以我们这样做:

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

正如你所看到的......这迫使我们在一堆地方拥有大量相同的代码。

问题

将两个DAO与两个不同的JNDI进行对话是否被认为是一个设计漏洞? (我知道它不是经典JDBC中的设计缺陷,但它与Spring + iBatis不同吗?)

你看到的getSqlTemplDW()方法看起来像:

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

如您所见,我使用的是javax.sql.DataSource。但是,我们被告知不要使用这个导入!!所以现在我被卡住了。我无法使用此导入(意味着无法在我的DAO中更改连接)。所以我一直在得到建议,每个dao应该只有一对一映射到JNDI。

我想知道..有没有办法解决这个问题?

骨架

弹簧换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>

考试界面

 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;
    }
 }

通过某些操作调用所有这些

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

3 个答案:

答案 0 :(得分:1)

理解你的确切难度并不容易(对我来说),如果你给我们更多的DAO类骨架及其与其他春季托管bean的关系,也许会有所帮助。 你说“spring使dao与JNDI连接有一对一的映射”;我不明白。你肯定可以(在你的Spring容器中)有一对DataSource bean(每个数据库一个),以及一对相应的SqlMapClientTemplate个bean。然后,您将每个DAO对象注入两个SqlMapClientTemplate bean,并使用(在每种方法中)指向正确数据库的bean。我错过了什么吗?

更新:查看骨架,我发现什么都没有阻止你将两个clientMaps注入你的dao中,而不是让一个getSqlMapClientTemplate()有两个方法:getSqlMapClientTemplateDb1() getSqlMapClientTemplateDb2()或者其他什么。

也许这里存在一些概念问题。

标准做法是将DAO定义为接口,然后为特定框架或数据库实现具体类。目标是简化从一个框架/数据库到另一个框架/数据库的迁移,而无需触及界面。因此,例如,您可以使用方法public User getUser(int id)的IUserDao接口和两个不同的实现-say- UserDaoPostgresqlUserDaoMysql;这些方法将实现两种替代方法(从备用存储库中获取用户)。通常,在这种情况下,上层将忽略这一点 - 并且要使用的具体DAO将在布线中指定(例如使用Spring),因此在部署时固定。但是在每个部署的实例中只会使用一个实现(可能在某些测试或迁移代码中除外),dao内部(以及上层)中的代码应该对这两个替代实现保持不可知。

但还有其他情况。例如,当一个应用程序数据部分在Postgresql数据库中,而另一部分在Mysql数据库中(或在另一个独立的Pg数据库中,或在某些非关系数据库中,甚至一些日志中)。然后,由于DAO的角色只是抽象对数据存储库的访问,您的IUserDao可能有两种方法getUser(int userid) getUserHistory(int userid),它可以完全发生(在一个特定的实现中) )每个方法必须访问不同的数据库或资源。在这里,在一个DAO类中明确选择明确不同的数据源并不是一个坏习惯。

也许您应该明确您的方案是前者还是后者。

答案 1 :(得分:0)

JDBC与否,我认为Data Access Object抽象了一个底层数据访问实现。因此,即使它们共享相同的接口,如果我有两个数据源(无论它们是否是两个RDBMS),我将提供两个实现。

答案 2 :(得分:0)

更好的设计是重构你的DAO。这样的事情。

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
  }
}

你的DAO看起来像单身,不建议像这样切换数据源。你也可以考虑使用livedb数据源创建两个相同类型的不同bean,使用dw数据源创建一个。并使用适当的bean来完成任务。

<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>