Dict.CN 在线词典, 英语学习, 在线翻译

都市淘沙者

荔枝FM Everyone can be host

统计

留言簿(23)

积分与排名

优秀学习网站

友情连接

阅读排行榜

评论排行榜

使用Spring的JdbcTemplate实现DAO(zhuan)

直接使用JDBC操作数据库来实现DAO,相对于使用持久层框架(Hibernate等) ,可以减少映射等带来的性能损失。
一般而言,使用JDBC来操作数据库,无非是以下几个步骤:
    获取数据源
    取得数据库连接
    执行SQL语句
    处理执行结果和异常
    释放数据库连接
这些步骤是每次进行数据库操作都必须进行的。很显然,可以使用模板模式来简化设计,将执行SQL语句的部分抽象出来,其它的步骤由模板自动完成。
虽然设计这样的模板并不算复杂,但是现在我们完全无需自己来实现,应为Spring已经为我们提供了一个非常实用的模板--JdbcTemplate。它位于Spring包org.springframework.jdbc.core下,是Spring的jdbc工具包的核心类之一。使用JdbcTemplate的前提是必须为其提供数据源(DataSource),并通过实用类DataSourceUtils来安全地获取和释放数据库连接(Connection对象)。可以说已经是一个完美的实现,因此我们只要放心地使用就可以了。

总体思路
    (1)数据源通过Spring的容器来提供;
    (2)DAO通过静态方式从Spring容器中获取;
    (3)针对接口编程;
    (4)提供数据操作父类,简化具体DAO实现。

一、DAO地实现
    遵循Spring的良好编程风格,针对接口编程。同时,为了简化对接口的实现,还提供了一个所有实现类的父类,提供了具体的数据库操作方法,这些方法当然就是使用JdbcTemplate来实现的了。

1、Dao父类
    主要目的是获取数据源,为子类提供具体的数据库操作方法:

public class Dao {
    
    
/**
     * 日志
     
*/

    
protected Log log = LogFactory.getLog(this.getClass().getName());
    
    
/**
     * 执行查询SQL文
     *  
     * 
@param strSql
     
*/

    
protected void doQuery(String strSql) {

        JdbcTemplate jt 
new JdbcTemplate(getDataSource());    //使用
JdbcTemplate
        
        
try {
            List result 
= jt.queryForList(strSql);
            
this.resultList = result;
            
            
this.resultCount = result.size();
            
            log.debug(logHeader 
+ strSql);
            
        }
 catch (DataAccessException de) {
            
            log.error(logHeader 
+ de);
        }

        
    }
    
    
    
/**
     * 执行更新SQL文
     * 
     * 
@param strSql
     
*/

    
protected boolean doUpdate(String strSql) {
        
        JdbcTemplate jt 
= new JdbcTemplate(getDataSource());
        
        
try {
            
this.affectedRows = jt.update(strSql);
            
            log.debug(logHeader 
+ strSql);
            
            
return true;
            
        }
 catch (DataAccessException de) {
            
            log.error(logHeader 
+ de);
            
            
return false;
        }

    }


    
/**
     * 获取执行结果的一行
     * 
     * 
@return 一行结果用Map保存
     
*/

    
protected Object getResult() {
        
if (this.resultList == null || this.resultList.size() == 0{
            
return null;
        }
 else {
            
return this.resultList.get(0);
        }

    }

    
    
/**
     * 获取查询结果
     * 
     * 
@return 查询结果以List保存
     
*/

    
protected List getResultList() {
        
return this.resultList;
    }

    
    
/**
     * 获取更新所影响的行记录数
     * 
     * 
@return 整数
     
*/

    
protected int getAffectedRows() {
        
return this.affectedRows;
    }

        
    
/**
     * 获取查询结果总行数
     * 
     * 
@return 整数
     
*/

    
protected long getResultCount() {
        
return this.resultCount;
    }
    

    
/** 日志头部 */
    
protected String logHeader = LogUtil.getLogHeader();
    
    
/**
     * 
@return dataSource
     
*/

    
public DataSource getDataSource() {
        
return dataSource;
    }


    
/**
     * 
@param dataSource 设置 dataSource
     
*/

    
public void setDataSource(DataSource dataSource) {
        
this.dataSource = dataSource;   //获取数据源
    }
    
    
    
//-------------------------------------------------------------------------

    
private List resultList = null;
    
private long resultCount = 0;
    
private int affectedRows = 0;    
    
private DataSource dataSource = null;
}

此处为dataSource提供了get/set方法,可以从使用Spring的Ioc中获益,即可以使用配置的方法为之选择数据源(本文的实现方式);同时,也可以通过代码手工提供数据源。

2、UserDao接口定义:

public interface UserDao {
    
    
/**
     * 根据用户ID,获取User对象
     * 
     * 
@param 用户ID
     * 
@return User対象
     
*/

    
public User getUserByLoginId(String loginId);

    
}

就一个方法,仅仅是示例而已 ^_^

3、UserDaoImpl实现:

public class UserDaoImpl extends Dao implements UserDao {

    
/*
     * (non-Javadoc)
     * @see my.test.dao.UserDao#getUserByLoginId(java.lang.String)
     
*/

    
public User getUserByLoginId(String loginId) {
        
        User user 
= null;
        
        doQuery(Sql.S_M_MEMBER_01(loginId));
        
if (getResultCount() > 0{            
            user 
= new User();
            
            Map result 
= (Map) getResult();
            user.setMemberId(String.valueOf(result.get(
"MEMBER_ID")));
           
//以下省略
        }

        
        
return user;
    }


}

这里只是简单地实现了平面(二维的数据库)和立体(对象)的转换,并没有涉及到对关系数据库中的关系的转化,这些都要靠自己去实现,还是有些不方便。自己实现的麻烦之处就是要手工编写所有的SQL语句...但一切都在自己的掌控制中,也没什么不好,呵呵。
现在看看红色的部分用起来是不是简便了很多了。

罗罗嗦嗦地一堆代码,还不如看图来得清爽:


二、DAO的使用
    既然使用了JdbcTemplate,为什么不再多使用一点Spring的功能呢?比如通过配置为DAO提供数据源等。但问题是,整个应用系统如果没有使用Spring的容器来管理,如何才能使用Ioc功能呢?还好,可以将Spring的应用上下文(ApplicationContext)单独地使用。我们可以将DAO作为单独的bean分别定义,然后从ApplicationContext中取得这些bean的实例。Spring会根据定义为我们自动生成所需的DAO的实例。只要在定义的时候,为DAO提供Datasoure即可实现数据源的自动配置了。
1、daoBeans.xml

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">

<beans>
    
    
<bean id="dataSource" 
        class
="org.springframework.jdbc.datasource.DriverManagerDataSource"
        destroy-method
="close">
        
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
        
<property name="url" value="jdbc:oracle:thin:@192.168.***.***:1521:***"/>
        
<property name="username" value="username"/>
        
<property name="password" value="password"/>
        
<property name="validationQuery" value="SELECT 1 FROM DUAL"/>
    
</bean>

    
<bean id="userDao" class="my.test.dao.impl.UserDaoImpl">
        
<property name="dataSource">      <--为UserDao设置数据源
            
<ref bean="dataSource"/>     
        
</property>
    
</bean>

</beans>

使用了Spring的数据源管理功能,通过定义的方式为UserDao提供了数据源(在生成UserDao实例的时候,setDataSource方法就会被调用从而将dataSource载入)。

2、DAO获取工具的实现
    编写了一个工具类,提供根据DAO名(daoBeans.xml中定义的名字)获取DAO对象的静态方法。

public class DaoBeanFactory {
    
    
private static Log log = LogFactory.getLog(DaoBeanFactory.class.getName());
    
    
private static final String DAO_BEANS= "/daoBeans.xml";
    
private static ApplicationContext ctx = null;
    
    
static {
        ctx 
= new ClassPathXmlApplicationContext(DAO_BEANS);
        log.debug(DAO_BEANS 
+ "被成功载入。");
    }

    
    
public static Object getDao(String daoName){        
        
return ctx.getBean(daoName);
    }

}

这里首先通过读取daoBeans.xml初始化应用上下文(ApplicationContext )。由于是静态方法,只会在第一次被使用时执行一次;然后就可从上下文中获取所需要的指名DAO对象了:

private UserDao userDao = (UserDao) DaoBeanFactory.getDao("userDao");


到此为止,可以说从实现到使用都已经大功告成了,happy一下。

三、进一步改进
    并不是说一切都是完美的。比如,第一次执行DaoBeanFactory.getDao方法时,会进行应用上下文的初始化,耗时较多;想改善数据源的管理,比如使用连接池;更进一步,如果大部分的工作都是进行查询,想提高系统的查询效率,该如何呢?
    时间不够,请等待下回分解。


1、对ApplicationContex初始化的改进
    DAO的获取工具类DaoBeanFactory首次被使用的时候会花上几秒钟执行ApplicationContex的初始化工作。如果用户在进行登录的时候,输入了用户名和密码之后要等上几秒钟后才被告知可以进入了,这样的系统给人的第一印象就是--怎么这么慢啊!为了尽量避免这种情况出现,既然是初始化而且还耗时,我们就应该尽量将这种初始化工作放到系统启动的时候去做。
    实现的方法很多,我就简单写了一个初始化Servlet,在Servlet的init中调用一次DaoBeanFactory即可触发ApplicationContex的初始化:

public class AppInitServlet extends HttpServlet {

    
/**
     * 
     
*/

    
private static final long serialVersionUID = 2987034642754889458L;
    
    
public void init() {
        log.info(
"应用系统启动开始…");
        
        
if (DaoBeanFactory.getDao("dataSource"!= null{
            log.info(
"应用系统启动正常终了。");
        
        }
 else {        
            log.info(
"应用系统启动异常终了。");
        }

    }

    
    
private Log log = LogFactory.getLog(AppInitServlet.class.getName());

}

然后在web.xml中设置为启动执行就可以了:

   <servlet>
        
<servlet-name>AppInit</servlet-name>
        
<servlet-class>my.test.common.AppInitServlet</servlet-class>
        
<load-on-startup>1</load-on-startup>
    
</servlet>

重新启动时,就可以看到Log打印出的启动信息。再次登录,快多了吧!

2、更换数据源管理
    dataSource使用的是Spring的DriverManagerDataSource。一般情况下,这已经足够了。但它没有提供连接池功能,对系统的整体性能有所影响。于是,更换具有连接池功能的吧。我用的是开源的DBCP连接池,配置也很简便:

    <bean id="dataSource"
        class
="org.apache.commons.dbcp.BasicDataSource"
        destroy-method
="close">
        
<property name="driverClassName">
            
<value>oracle.jdbc.driver.OracleDriver</value>
        
</property>
        
<property name="url">
            
<value>jdbc:oracle:thin:@192.168.4.***:1521:***</value>
        
</property>
        
<property name="username">
            
<value>username</value>
        
</property>
        
<property name="password">
            
<value>password</value>
        
</property>
        
<property name="validationQuery">            
            
<value>SELECT 1 FROM DUAL</value>
        
</property>                                                       
        
<property name="testOnBorrow">               
            
<value>true</value>                                   
        
</property>                                                       
    
</bean>

这里也有要注意的地方。当数据库服务器重新启动或者出现故障后,连接池中的连接是要被清空的。但是DBCP似乎没有去自动检测连接是否还有效。为了安全起见,最好在使用前测试其有效性。灰色背景部分的配置就是这个目的。

3、增加查询缓存
    在使用Hibernate的时候,可以很容易地获得缓存带来的系统性能优化。这里,我了达到相似的效果,也增加了缓存。其实Spring已经提供了使用各种缓存的接口,它位于org.springframework.cache.ehcache包下。
    如何实现缓存功能呢?当每个DAO执行查询方法的时候,就先到缓存中查找历史缓存数据,存在则返回,不存在才去访问数据库。势必要为每一个这样的查询方法都做一个if判断。若是日后想取消或撤换缓存的时候,这就成了一个艰巨的修改任务。这就和事务管理类似,我可以使用也可以不使用。很自然地,就想到了使用Spring的AOP功能,对每个查询方法作横切处理,增加对缓存的判断。
    很幸运,这个世界上有很多很勤快的人,让我们可以变得越来越懒。Pieter Coucke已经实现了一个非常实用的AOP Cache。根据他提供的例子,很容易就实现了一个基于AOP的DAO缓存。首先下载Spring AOP Cache的jar文件以及EHCache的jar文件(我使用的是EHCache,当然也可以使用其它的),加入到系统工程中。剩下的任务就是写配置文件:

    <!-- Cache Configuration -->    
    
<bean id="cacheInterceptor" class="org.springframework.aop.interceptor.cache.EHCacheInterceptor">
        
<property name="refreshPeriods">
            
<props>
                
<prop key="my.test.dao.UserDao@getUserByLoginId">900</prop>
            
</props>
        
</property>

        
<property name="identifiers">
            
<props>
                
<prop key="java.lang.String">toString</prop>
            
</props>
        
</property>
    
</bean>
    
    
<bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        
<property name="advice">
            
<ref bean="cacheInterceptor"/>
        
</property>
        
<property name="patterns">
            
<list>
                
<value>.*get.*</value>         <!-- 这里就是匹配查询方法了 -->
            
</list>
        
</property>
    
</bean>
    
<bean id="cacheProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        
<property name="beanNames">
            
<value>*Dao</value>
        
</property>
        
<property name="interceptorNames">
            
<list>
                
<value>advisor</value>
            
</list>
        
</property>
    
</bean>

重新启动后,登录完成后再登录一次试试看?

cache.CacheInterceptor (CacheInterceptor.java:229)     - Returning cached data for key [efdd56@java.lang.String#fn] in cache [my.test.dao.UserDao@getUserByLoginId]

看到上面的Log信息代替了原有的访问数据库的部分,高兴了吧!

好了,拍拍手,收工 ^_^!

posted on 2009-01-17 09:30 都市淘沙者 阅读(3201) 评论(0)  编辑  收藏


只有注册用户登录后才能发表评论。


网站导航: