服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

cook jdbc with springframework

    我们的目标是希望在任何使用jdbc的场合都能够利用springframework的jdbc support module,同时尽可能少地引入不必要的元素。因此,我们假定不使用j2ee环境提供的任何服务,也不使用springframework的ioc容器和aop两大特性。

    首先,我们需要的自然是一个datasource。在非j2ee环境下,可以使用jakarta的commons-dbcp来得到一个datasource的实例。我们选择使用sharedpooldatasource,它提供了一个close()方法,在程序结束前调用它可以关闭所有的connection,释放相关的资源。

    接着,将spring-core.jar和spring-dao.jar置入classpath。事实上,我们将要用到的class几乎全部都在spring-dao.jar中,之所以需要spring-core.jar是因为nestedruntimeexception,所有jdbc support module中的异常都直接或间接继承自这个runtime exception。由此也可以看出,我们只是在受控制的情况下引入了springframework的一小部分而已。

    下面,我们就可以开始逐步设置和组装我们的基础设施。

org.springframework.jdbc.core.jdbctemplate

    jdbctemplate是整个package的核心,所有的jdbc调用流程都通过它来完成,开发者只需要实现对应的callback接口即可。jdbctemplate是无状态且线程安全的,也就是说在整个系统中只需要创建一个实例就够了。创建它的时候需要提供datasource作为参数。

    jdbctemplate jdbctemplate = new jdbctemplate(datasource);
    jdbctemplate.afterpropertiesset();

    afterpropertiesset()方法是对initializingbean接口的实现,如果实例是由ioc容器管理的,则容器会自动调用。由于我们并没有使用ioc容器,所以需要在创建实例后主动调用该方法。

org.springframework.transaction.support.transactiontemplate

    transactiontemplate帮助我们以编程的方式实现事务控制。同样的,开发者也是通过实现callback接口来使用它。transactiontemplate也是无状态且线程安全的。创建transactiontemplate的实例需要提供一个platformtransactionmanager的实例,在springframework中有多种platformtransactionmanager的实现类,提供多种事务控制的途径。基于前面设定的限制,我们选择datasourcetransactionmanager。


    platformtransactionmanager platformtransactionmanager = 
        new datasourcetransactionmanager(datasource);
    transactiontemplate transactiontemplate =
        new transactiontemplate(platformtransactionmanager);
    transactiontemplate.afterpropertiesset();

org.springframework.jdbc.support.incrementer.datafieldmaxvalueincrementer

    在数据库中插入数据的时候,需要获得唯一的long或integer类型的值做为主键。datafieldmaxvalueincrementer提供了这样一个途径,而具体的实现方法则因各种数据库系统而异。简单地说,如果数据库系统本身提供了sequences(例如oracle),则直接从sequences取得所需的序列值,如果数据库系统不支持sequences(例如mysql),则通过一个单独的table来保存和产生新的序列值。每个datafieldmaxvalueincrementer的实例都需要有一个对应的sequences或者table,换句话说,开发者要选择是整个系统共用同一个incrementer实例,还是对应每个table使用各自专有的incrementer实例。目前springframework已经提供了针对oracle、db2、postgresql、mysql,hsql五种数据库的实现,支持从超重量级直到超轻量级的常见数据库系统,而且,要自己编写针对其它数据库系统的实现也并非难事。在例子中我们选择使用hsql数据库,创建一个hsqlmaxvalueincrementer的实例需要提供datasource、table name、column name。

    hsqlmaxvalueincrementer incrementer = new hsqlmaxvalueincrementer(datasource,
         "id_sequence", "value");
    incrementer.afterpropertiesset();

    基础设施准备好了之后,就可以在上面进行dao的实现了,前面已经提到了开发者是通过实现callback接口来使用jdbctemplate的,现在让我们来逐个尝试。

    insert、update、delete三种情况都不需要返回结果给调用者,因此可以使用jdbctemplate的update()方法,该方法有多个重载的版本,可以根据实际情况选择一个合适的。由于数据库系统的原因,我无法测试对blob和clob的操作,但是从文档来看,相关的支持应该是满足需要的。

        long id = incrementer.nextlongvalue();
        user.setid(id);
        string sql = "insert into users values (?,?,?,?);";
        object[] params = new object[] { new long(id), user.getusername(),
                user.getpassword(), new boolean(user.isenabled()) };
        jdbctemplate.update(sql, params);

    对于query,则是使用jdbctemplate的query()方法,同样提供了多个重载的版本。通常要先实现一个rowmapper,能够将resultset中的一条记录取出来,填充到一个对象中。我们可以将它设计为一个内部类,同时它的实例也是线程安全的。

private class userrowmapper implements rowmapper
{
    public object maprow(resultset resultset, int row) throws sqlexception
    {
          user user = new user();
          user.setid(resultset.getlong("id"));
          user.setusername(resultset.getstring("username"));
          user.setpassword(resultset.getstring("password"));
          user.setenabled(resultset.getboolean("enabled"));
          return user;
    }
}

        string sql = "select * from users;";
        rowmapper mapper = new userrowmapper();
        rowmapperresultreader reader = new rowmapperresultreader(mapper);
        list users = jdbctemplate.query(sql, reader);
        return users;

    数据库操作自然离不开事务控制,使用transactiontemplate很简单,只需要调用execute()方法即可。当然前提是你实现了transactioncallback接口,并且在dointransaction()方法里进行各种数据库操作。于是这一组操作就会作为一个事务提交,一旦出现任何异常,则相应地进行事务回滚。

    transactiontemplate.execute(createuser);

    public object dointransaction(transactionstatus status)
    {
        try
        {
            userdao.insertuser(user);
            authoritydao.updateauthority(user, auths);
        }
        catch (exception exception)
        {
            status.setrollbackonly();
            log.debug(exception.getcause(), exception);
        }
        return null;
    }


    以上,就是借助springframework jdbc support的dao实现方式。我们可以将最初设定的限制条件放宽一些,假定我们处于j2ee环境下,那么datasource的实例可以通过jndi直接从j2ee容器获得,同时也可以选择通过jta进行事务控制。假定我们使用springframework的ioc容器,那么所有前面提到的对象都可以交给ioc容器中进行设置和组装,以获得更简洁、更灵活的代码实现。

    我还漏了一点——在前面的代码中看不出来——springframework对sqlexception的重新包装。sqlexception所携带的信息几乎都是通过其中的error code来传递的,而本身类的层次结构扁平,我们只好不分青红皂白全都catch住,然后再根据error code决定是否应该进行一些异常处理。而springframework的异常类层次结构很丰富,我们可以充分利用try catch机制仅catch那些我们想要处理的异常,这又使我们的代码倾向于简洁。

    结论:在一个典型的项目中,如果我们由于这样或那样的原因决定不使用ejb、hibernate、ibatis等等等等,而打算亲自操刀烹调jdbc时,同样有充分的理由使用springframework。我们只需要付出很少的学习成本,就能让我们的daos更可靠、更灵活、更一针见血。

扫描关注微信公众号