JDBC支持

本文介绍了如何利用Spring提供的JdbcTemplate与Template类简化JDBC的使用流程,减少繁琐的细节处理,提高开发效率。通过使用JdbcTemplate,开发者可以轻松实现数据的增删查改操作,同时支持事务管理与批处理,提供ORM转换功能,减少SQL细节关注,使数据操作更加对象化。此外,文中还提到了如何利用DataFieldMaxValueIncrementer生成主键值,以及Spring2.0中引入的NamedParameterJdbcTemplate,它允许使用命名参数替代占位符,使得SQL编写更加清晰。最后,展示了如何利用SimpleJdbcTemplate结合泛型特性简化查询操作,使代码更加简洁易读。
5.2  JDBC支持

在使用JDBC时,总是要处理繁琐的细节,例如Connection、Statement的获得、SQLException的处理、Connection、Statement的关闭等问题,Spring在JDBC的使用上提供了几个类,可以简化JDBC使用时的流程。

5.2.1  使用JdbcTemplate

在前一节介绍的DataSourceDemo项目中,UserDAO中直接使用JDBC来实现insert() 与find() 方法,当中要处理Connection的取得、Statement的建立、异常的处理、Statement的关闭、Connection的关闭等,对于一个基本的JDBC存取,这些流程都是大同小异的,每一次都必须走这样的流程着实令人厌烦。

Spring提供了org.springframework.jdbc.core.JdbcTemplate类,它被设计为线程安全(Thread-safe),当中所提供的一些操作方法封装了类似以上的流程,例如DataSourceDemo项目中的UserDAO类可以简单地使用JdbcTemplate来改写,要建立JdbcTemplate的实例,必须要有一个DataSource对象作为构造时的对象:

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

来改写一下DataSourceDemo项目中UserDAO的内容实现,可以看到使用JdbcTemplate时,在程序的编写流程上会有什么样的改进:

JdbcTemplateDemo                                                                 UserDAO.java

package onlyfun.caterpillar;

import java.util.Iterator;

import java.util.List;

import java.util.Map;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;

public class UserDAO implements IUserDAO {

    private JdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {

        jdbcTemplate = new JdbcTemplate(dataSource);

    }

    public void insert(User user) {

       String name = user.getName();

       int age = user.getAge().intValue();

       jdbcTemplate.update("INSERT INTO user (name,age) "

               + "VALUES('" + name + "'," + age + ")");

    }

    public User find(Integer id) {

        List rows = jdbcTemplate.queryForList(

          "SELECT * FROM user WHERE id=" + id.intValue());

        Iterator it = rows.iterator();

        if(it.hasNext()) {

            Map userMap = (Map) it.next();

            Integer i = new Integer(userMap.get("id").toString());

            String name = userMap.get("name").toString();

            Integer age =

                  new Integer(userMap.get("age").toString());

            User user = new User();

            user.setId(i);

            user.setName(name);

            user.setAge(age);

            return user;

        }

        return null;

    }

}

只要改写UserDAO就可以了,其他的程序与设置文件都不用变动,Spring的JdbcTemplate一如其名,主要是通过Template Method模式来实现JDBC的流程封装处理。

良葛格的话匣子<<<

如果使用Hibernate之类的ORM(Object-Relational Mapping)工具,则find()方法中对象模型与关联式模型的转换还可以进一步的简化。

Spring的JDBC封装等功能基本上可以独立于Spring来使用,除了JdbcTemplate之外,Spring还提供了其他的Template类,像是对Hibernate、JDO、iBatis等的Template实现。在数据库的事务处理方面,Spring提供了编程式与声明式的事务管理功能,简化了持久层程序的复杂度,并提供了更好的维护性。

5.2.2  执行与更新JdbcTemplate

您可以使用JdbcTemplate的execute() 方法执行SQL陈述,例如:

jdbcTemplate.execute(

     "CREATE TABLE USER (user_id integer, name varchar(100))");

如果是UPDATE或INSERT,您可以使用update() 方法,它有数个重载(Overload)版本,例如接受实现org.springframework.jdbc.core.PreparedStatementCreator接口的对象,PreparedStatementCreator接口的定义如下:

package org.springframework.jdbc.core;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.SQLException;

public interface PreparedStatementCreator {

    PreparedStatement createPreparedStatement(Connection con)

        throws SQLException;

}

例如可以将5.2.1节的JdbcTemplateDemo项目中UserDAO的insert() 方法改写如下:

...

public void insert(User user) {

   final String name = user.getName();

   final int age = user.getAge().intValue();

   jdbcTemplate.update(

     new PreparedStatementCreator() {

        public PreparedStatement createPreparedStatement(

                Connection con) throws SQLException {

           String sql =

               "INSERT INTO user (name,age) VALUES(?,?)";

           PreparedStatement ps =

                       con.prepareStatement(sql);

           ps.setString(1, name);

           ps.setInt(2, age);

           return ps;

        }

     });

}

...

在这个例子中,使用了PreparedStatement来准备SQL,JdbcTemplate实现了Template-Callback机制,update()方法中实现了Template流程,而PreparedStatement- Creator则实现了Callback对象,在执行JDBC的流程中,必要时会执行所定义的callback方法,也就是createPreparedStatement()方法,传回一个PreparedStatement对象,以完成update()方法中的Template流程。

与PreparedStatementCreator互补的接口是org.springframework.jdbc.core. PreparedStatementSetter接口:

package org.springframework.jdbc.core;

import java.sql.PreparedStatement;

import java.sql.SQLException;

public interface PreparedStatementSetter {

    void setValues(PreparedStatement ps) throws SQLException;

}

例如可以将JdbcTemplateDemo项目中UserDAO的insert() 方法改写如下:

...

public void insert(User user) {

   final String name = user.getName();

   final int age = user.getAge().intValue();

   jdbcTemplate.update(

      "INSERT INTO user (name,age) VALUES(?,?)",

      new PreparedStatementSetter() {

            public void setValues(PreparedStatement ps)

                                   throws SQLException {

               ps.setString(1, name);

               ps.setInt(2, age);

            }

         });

}

...

JdbcTemplate会自动建立PreparedStatementCreator的实例,以提供传递给setValues() 方法的PreparedStatement对象。

您也可以直接提供SQL,就如JdbcTemplateDemo项目中所示范的UserDAO中的insert() 方法一样:

...

public void insert(User user) {

   String name = user.getName();

   int age = user.getAge().intValue();

   jdbcTemplate.update("INSERT INTO user (name,age) "

           + "VALUES('" + name + "'," + age + ")");

}

...

在直接下SQL语句时,也可以使用 "?" 作为占位字符,并使用对象数组,把它作为参数传递给JdbcTemplate的update() 方法,例如改写JdbcTemplateDemo项目中所示范的UserDAO中的insert() 方法:

...

public void insert(User user) {

   jdbcTemplate.update(

       "INSERT INTO user (name, age) VALUES(?,?)",

       new Object[] {user.getName(), user.getAge()});

}

...

JdbcTemplate会自动建立PreparedStatementCreator与PreparedStatementSetter的实例,然而这些细节您不用理会,只要提供SQL与参数就好了。

如果需要批处理,可以实现org.springframework.jdbc.core.BatchPrepared- StatementSetter接口:

package org.springframework.jdbc.core;

import java.sql.PreparedStatement;

import java.sql.SQLException;

public interface BatchPreparedStatementSetter {

    void setValues(PreparedStatement ps,

                      int i) throws SQLException;

    int getBatchSize();

}

例如可以在JdbcTemplateDemo项目中的IUserDAO接口及UserDAO类上增加一个insertUsers() 方法的定义与实现,如以下所示的内容:

...

public int[] insertUsers(final List users) {

    String sql = "INSERT INTO user (name,age) VALUES(?,?)";

    BatchPreparedStatementSetter setter =

      new BatchPreparedStatementSetter() {

        public void setValues(

            PreparedStatement ps, int i) throws SQLException {

            User user = (User) users.get(i);

            ps.setString(1, user.getName());

            ps.setInt(2, user.getAge().intValue());

        }

        public int getBatchSize() {

            return users.size();

        }

      };

    return jdbcTemplate.batchUpdate(sql, setter);

}

...

如果JDBC驱动程序支持批处理,则直接使用它的功能,如果不支持, Spring则会一个一个自动处理更新以模拟批处理。

5.2.3  JdbcTemplate查询

使用JdbcTemplate进行查询时,可以使用queryForXXX() 等方法,例如使用queryForInt()方法传回user表格中的数据数目:

jdbcTemplate.queryForInt("SELECT COUNT(*) FROM user");

也可以使用queryForObject()传回一个查询后的结果对象,例如传回一个String对象:

String name = (String) jdbcTemplate.queryForObject(

              "SELECT name FROM USER WHERE id = ?",

               new Object[] {id},

               java.lang.String.class);

上面两个例子传回的都是单独一笔数据,如果要传回多笔数据,则可以使用queryForList() 方法,例如:

List rows = jdbcTemplate.queryForList(

           "SELECT * FROM user WHERE id=" + id.intValue());

传回的List中包括的是Map对象,每个Map对象代表查询结果中的一笔数据,每笔数据包括多个字段内容,要取得字段中的值,就要使用字段名称作为"键(Key)",例如:

...

Iterator it = rows.iterator();

while(it.hasNext()) {

    Map userMap = (Map) it.next();

    System.out.println(userMap.get("id"));

    System.out.println(userMap.get("name"));

    System.out.println(userMap.get("age"));

    ...

}

...

您可以实现org.springframework.jdbc.core.RowCallbackHandler接口,在查询到数据之后先作一些处理再传回,例如修改一下5.2.1节的JdbcTemplateDemo项目中UserDAO的find()方法,如下所示,在RowCallback-Handler的processRow()方法中实现简单的ORM(Object-Relational Mapping)动作:

...

public User find(Integer id) {

    final User user = new User();

    jdbcTemplate.query(

       "SELECT * FROM user WHERE id = ?",

       new Object[] {id},

         new RowCallbackHandler() {

            public void processRow(ResultSet rs)

                                    throws SQLException {

                user.setId(new Integer(rs.getInt("id")));

                user.setName(rs.getString("name"));

                user.setAge(new Integer(rs.getInt("age")));

            }

        });

    return user;

}

...

如果一次要取回很多查询结果的对象,则可以先实现org.springframe- work.jdbc.core.RowMapper接口,例如:

package onlyfun.caterpillar;

import java.sql.ResultSet;

import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

public class UserRowMapper implements RowMapper {

    public Object mapRow(ResultSet rs,

                         int rowNum) throws SQLException {

        User user = new User();

        user.setId(new Integer(rs.getInt("id")));

        user.setName(rs.getString("name"));

        user.setAge(new Integer(rs.getInt("age")));

        return user;

    }

}

在Spring 2.0中,ResultReader接口与其实现类已经被移除,查询方法现在直接接受RowMapper实例,例如使用queryForObject()方法时:

...

    public User find(Integer id) {

        User user = (User) jdbcTemplate.queryForObject(

                "select * from user where id=?",

                new Object[] {id},

                new UserRowMapper());

        return user;

    }

...

传回的结果已使用UserRowMapper的定义,将之封装为User对象。以下是使用query()方法时的一个示范:

List users = jdbcTemplate.query("select * from user",

                new UserRowMapper());

for(int i = 0; i < users.size(); i++) {

    User user = (User) users.get(i);

    System.out.println("/tId:/t" + user.getId());

    System.out.println("/tName:/t" + user.getName());

    System.out.println("/tAge:/n" + user.getAge());

}

传回的List对象中,包括了从数据库中查询出来的结果,并已封装为User类的实例。

5.2.4  JdbcTemplate的Lob支持

在JDBC中可以使用Clob(Character large object)与Blob(Binary large object),以分别针对文字文件与二进制文件进行储存。Spring中可以通过JdbcTemplate来处理CLOB与BLOB,以避免处理特定数据库(例如Oracle 9i)的Clob、Blob差异问题。举个例子来说,假设您的MySQL数据库表格如下:

CREATE TABLE test (

    id INT AUTO_INCREMENT PRIMARY,

    txt TEXT,

    image BLOB

);

假设现在分别读取一个文字文件与二进制文件,并想将之储存至数据库中,则可以使用JdbcTemplate,例如:

final File binaryFile = new File("wish.jpg");

final File txtFile = new File("test.txt");

final InputStream is = new FileInputStream(binaryFile);

final Reader reader = new FileReader(txtFile);

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

final LobHandler lobHandler = new DefaultLobHandler();

jdbcTemplate.execute("INSERT INTO test (txt, image) VALUES(?, ?)",

  new AbstractLobCreatingPreparedStatementCallback(lobHandler) {

     protected void setValues(PreparedStatement pstmt,

     LobCreator lobCreator) throws SQLException,DataAccessException {

         lobCreator.setClobAsCharacterStream(

                pstmt, 1, reader, (int) txtFile.length());

         lobCreator.setBlobAsBinaryStream(

                pstmt, 2, is, (int) binaryFile.length());

     }

});

reader.close();

is.close();

在建立AbstractLobCreatingPreparedStatementCallback对象时,要传递一个LobHandler实例,对于MySQL(MS SQL Server或Oracle 10g),使用DefaultLobHandler即可,对于Oracle 9i特定的LOB处理,可以使用OracleLobHandler。在setValues()方法实现中,使用LogCreator来分别设置Blob与Clob的来源串流,索引1、2表示第一与第二个占位字符'?'的位置,并指定读取长度。

如果要从数据库中将数据读取出来,并另存为文件,可以使用以下的程序:

final Writer writer = new FileWriter("test_bak.txt");

final OutputStream os = new FileOutputStream(new File(wish_bak.jpg"));

jdbcTemplate.query("SELECT txt,image FROM test WHERE id = ?",

   new Object[] {new Integer(1)},
   new AbstractLobStreamingResultSetExtractor() {

     protected void streamData(ResultSet rs)

            throws SQLException, IOException, DataAccessException {

         FileCopyUtils.copy(

                lobHandler.getClobAsCharacterStream(rs, 1), writer);

         FileCopyUtils.copy(

                lobHandler.getBlobAsBinaryStream(rs, 2), os);

     }

  });

writer.close();

os.close();

在这里使用FileCopyUtils的copy()方法,将LogHandler取得的串流直接转接给文件输出FileWriter、FileOutputStream对象。

5.2.5  以对象方式进行操作

就JdbcTemplate上的各种方法来看,它封装了JDBC处理的细节,让您不用接触底层的数据库技术,然而JdbcTemplate的各种方法仍须熟悉如何使用SQL语法,如果想让一些程序开发人员不用接触到SQL语法或是重用某些SQL,在Spring中,您可以进一步建立可重用的操作对象,建立之后,在设计数据库时,将设计完成的可重用对象给开发人员使用,他们就无须接触到SQL的细节了,从他们的观点来看,数据库操作更接近对象导向的操作方式。

Spring提供了org.springframework.jdbc.object包,可以让您以更对象导向的方式来设计数据库操作方面的程序,只要事先继承或直接实例化相对应的类并进行编译,之后就可以重复利用这个实例,执行它的方法来进行数据库相关操作。

org.springframework.jdbc.object.RdbmsOperation是个抽象类,代表RDBMS(Relational Database Management System)的查询、更新、预存程序等操作,您可以使用其子类org.springframework.jdbc.object.SqlUpdate、org.springframework.jdbc. object.MappingSqlQuery等类,它们被设计为线程安全(Thread-safe),所以可以在多线程的环境下重复使用这些类的实例。

SqlFunction用来执行SQL Function,例如可以执行COUNT(),然后回传一个整数表示查询到的数据笔数,下面是一个例子:

SqlFunction sf = new SqlFunction(

    dataSource, "SELECT COUNT(*) from user");

sf.compile();

sf.run();

RdbmsOperation的子类在设置好DataSource、SQL以及相关参数后,必须先执行compile()进行编译,之后就可以重复使用这个实例了。在设计时可以继承SqlFunction来封装SQL,例如修改5.2.1节中JdbcTemplateDemo项目的内容,新增一个类如下所示:

RdbmsDemo                                                                       UserFunction.java

package onlyfun.caterpillar;

import javax.sql.DataSource;

import org.springframework.jdbc.object.SqlFunction;

public class UserFunction extends SqlFunction {

    public UserFunction(DataSource dataSource) {

        super(dataSource, "SELECT COUNT(*) from user");

        compile();

    }

}

SqlUpdate类用来表示一个SQL的更新操作,您也可以设置相关参数,设计时也可以继承它来进行SQL的封装,例如:

RdbmsDemo                                                                        UserUpdate.java

package onlyfun.caterpillar;

import java.sql.Types;

import javax.sql.DataSource;

import org.springframework.jdbc.object.SqlUpdate;

public class UserUpdate extends SqlUpdate {

    public UserUpdate(DataSource dataSource) {

        super(dataSource,

               "INSERT INTO user (name,age) VALUES(?,?)");

        int[] types = {Types.VARCHAR, Types.INTEGER};

        setTypes(types);

        compile();

    }

}

setTypes()方法用来设置SQL中 "?" 占位字符所要插入的数据类型,之后执行update()方法时,可以仅指定对象数组作为参数来进行查询,每一个数组值将实际取代 "?" 占位字符,例如可以这么使用:

...

    SqlUpdate userUpdate = new UserUpdate(dataSource);

    ...

    userUpdate.update(

        new Object[] {user.getName(), user.getAge()});

SqlQuery类表示一个SQL查询操作,但通常很少直接使用这个类,因为查询到数据之后,会作一些处理。例如封装User类的实例,可以用它的子类org.spring- framework.jdbc.object.MappingSqlMapping来进行这个动作,例如:

RdbmsDemo                                                                          UserQuery.java

package onlyfun.caterpillar;

import java.sql.ResultSet;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.springframework.jdbc.object.MappingSqlQuery;

public class UserQuery extends MappingSqlQuery {

    public UserQuery(DataSource dataSource) {

        super(dataSource, "SELECT * FROM user");

        compile();

    }

    protected Object mapRow(ResultSet rs,

                                 int rowNum) throws SQLException {

        User user = new User();

        user.setId(new Integer(rs.getInt("id")));

        user.setName(rs.getString("name"));

        user.setAge(new Integer(rs.getInt("age")));

        return user;

    }   

}

设计好这个类之后,可以这么使用它:

...

SqlQuery userQuery = new UserQuery(dataSource);

...

List users = userQuery.execute();

执行完execute()方法后,实际上传回的List中会有User类的实例,并且它封装了查询后的每一笔数据。

配合以上的几个实现,可以再改写一下IUserDAO接口与UserDAO实现,例如:

RdbmsDemo                                                                           IUserDAO.java

package onlyfun.caterpillar;

import java.util.List;

public interface IUserDAO {

    public void insert(User user);

    public List allUser();

    public int count();

}

假设您是程序开发人员,有其他的开发人员负责数据库存取方面的程序,并提供了以上的UserFunction、UserUpdate与UserQuery类,那么您就不用关心实际的SQL是如何编写的了,您可以重用UserFunction、UserUpdate与UserQuery类来设计您的UserDAO类:

RdbmsDemo                                                                           UserDAO.java

package onlyfun.caterpillar;

import java.util.List;

import javax.sql.DataSource;

import org.springframework.jdbc.object.SqlFunction;

import org.springframework.jdbc.object.SqlQuery;

import org.springframework.jdbc.object.SqlUpdate;

public class UserDAO implements IUserDAO {

    private SqlUpdate userUpdate;

    private SqlQuery userQuery;

    private SqlFunction userFunction;

    public void setDataSource(DataSource dataSource) {

        userUpdate = new UserUpdate(dataSource);

        userQuery = new UserQuery(dataSource);

        userFunction = new UserFunction(dataSource);

    }

    public void insert(User user) {

        userUpdate.update(

          new Object[] {user.getName(), user.getAge()});

    }

    public List allUser() {

        return userQuery.execute();

    }

    public int count() {

        return userFunction.run();

    }

}

将SQL封装重用之后,从UserDAO程序编写的角度来看,完全是用对象操作的方式来进行的,配合程序的修改,可以写个简单的程序来看看运作是否正常:

RdbmsDemo                                                                  SpringDAODemo.java

package onlyfun.caterpillar;

import org.springframework.context.ApplicationContext;

import org.springframework.context.

              support.ClassPathXmlApplicationContext;

import java.util.List;

public class SpringDAODemo {

    public static void main(String[] args) {

        ApplicationContext context =

            new ClassPathXmlApplicationContext(

                    "beans-config.xml");

        User user = new User();

        user.setName("just933");

        user.setAge(new Integer(26));

        IUserDAO userDAO =

            (IUserDAO) context.getBean("userDAO");

        userDAO.insert(user);

        System.out.println("笔数: " + userDAO.count());

        List list = userDAO.allUser();

        for(int i = 0; i < list.size(); i++) {

            User next = (User) list.get(i);

            System.out.println("/n/tId:/t" + next.getId());

            System.out.println("/tName:/t" + next.getName());

            System.out.println("/tAge:/t" + next.getAge());

        }

    }

}

良葛格的话匣子<<<

为什么UserDAO中的UserUpdate、UserQuery、UserFunction等不使用IoC依赖注入呢?除了简化范例实现之外,还有一个观念:不要为了IoC而IoC!每个对象不管三七二十一都要来个依赖注入的话,被注入依赖的对象上,将会有一大堆冗长的Setter方法。建议有两个以上的对象需要注入相同的依赖时,才考虑使用Spring IoC容器来进行依赖注入。

5.2.6  DataFieldMaxValueIncrementer

在插入数据至数据库中时,您会需要设置主键,主键的设置最好是与业务键值无关,在Spring中,您可以使用org.springframework.jdbc.support.incrementer.DataFieldMaxValue- Incrementer来为您产生主键值,DataFieldMaxValueIncrementer有许多针对不同数据库的实例,例如DB2SequenceMaxValueIncrementer、HsqlMaxValueIncrementer、MySQL- MaxValueIncrementer、OracleSequenceMaxValueIncrementer、PostgreSQLSequence- MaxValueIncrementer,您可以操作它的nextIntValue()、nextLongValue() 或nextStringValue() 来产生下一个主键值。

5.2.7  Spring 2.0的NamedParameterJdbcTemplate

在Spring 2.0中新增了NamedParameterJdbcTemplate类,这样您在编写JDBC的SQL陈述时就不必使用占位字符'?'了,而是使用实际的命名参数(Named parameter)来保留SQL中会变动的数据部分,例如原本您是这么编写一个查询的:

String sql = "SELECT * FROM user WHERE id=?";

List rows = jdbcTemplate.queryForList(sql,

          new Object[] {id.intValue()});

现在可以使用NamedParameterJdbcTemplate改写为以下的方式:

String sql = "SELECT * FROM user WHERE id=:userId";

SqlParameterSource namedParameters =

        new MapSqlParameterSource("userId", id);

NamedParameterJdbcTemplate jdbcTemplate =

                new NamedParameterJdbcTemplate(dataSource);

List rows = jdbcTemplate.queryForList(sql, namedParameters);

注意到命名参数":userId"实际值的指定方式,在这里使用的是SqlParameterSource,在构造时直接指定命名参数与实际值。您也可以使用Map对象来指定多个命名参数的实际值,例如:

String sql = "INSERT INTO user (name,age) VALUES(:userName, :userAge)";

Map namedParameters = new HashMap();

namedParameters.put("userName", name);

namedParameters.put("userAge", age);

NamedParameterJdbcTemplate jdbcTemplate =

                new NamedParameterJdbcTemplate(dataSource);

jdbcTemplate.update(sql, namedParameters);

您也可以将一个POJO(Plain Old Java Object)对象的值作为命名参数值的依据,例如:

String sql = "INSERT INTO user (name,age) VALUES(:name, :age)";

SqlParameterSource namedParameters =

           new BeanPropertySqlParameterSource(user);

NamedParameterJdbcTemplate jdbcTemplate =

                new NamedParameterJdbcTemplate(dataSource);

jdbcTemplate.update(sql, namedParameters);

其中"user"所参考的对象是JdbcTemplateDemo项目中的User实例。下面具体将JdbcTemplateDemo项目的UserDAO类改写为使用NameParameterJdbcTemplate的方式:

NamedParameterDemo                                                             UserDAO.java

package onlyfun.caterpillar;

import java.util.Iterator;

import java.util.List;

import java.util.Map;

import javax.sql.DataSource;

import org.springframework.jdbc.core.namedparam

                        .BeanPropertySqlParameterSource;

import org.springframework.jdbc.core.namedparam

                        .MapSqlParameterSource;

import org.springframework.jdbc.core.namedparam

                        .NamedParameterJdbcTemplate;

import org.springframework.jdbc.core.namedparam.SqlParameterSource;

public class UserDAO implements IUserDAO {

    private NamedParameterJdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {

        jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);

    }

    public void insert(User user) {    

       String sql = "INSERT INTO user (name,age) VALUES(:name, :age)";

       SqlParameterSource namedParameters =

           new BeanPropertySqlParameterSource(user);

       jdbcTemplate.update(sql, namedParameters);

    }

    public User find(Integer id) {

        String sql = "SELECT * FROM user WHERE id=:userId";

        SqlParameterSource namedParameters =

                 new MapSqlParameterSource("userId", id);

        List rows = jdbcTemplate.queryForList(sql, namedParameters);

        Iterator it = rows.iterator();

        if(it.hasNext()) {

            Map userMap = (Map) it.next();

            Integer i = new Integer(userMap.get("id").toString());

            String name = userMap.get("name").toString();

            Integer age =

                  new Integer(userMap.get("age").toString());

            User user = new User();

            user.setId(i);

            user.setName(name);

            user.setAge(age);

            return user;

        }

        return null;

    }

}

5.2.8  Spring 2.0的SimpleJdbcTemplate

如果您使用的JDK 是5.0以上的版本,则可以利用Spring 2.0中SimpleJdbcTemplate所提供的泛型(Generic)功能,例如原来您可能是这么查询数据的:

public User find(Integer id) {

   String sql = "SELECT * FROM user WHERE id=?";

   RowMapper mapper = new RowMapper() {

        public Object mapRow(ResultSet rs, int rowNum)

                                             throws SQLException {

            User user = new User();

            user.setId(new Integer(rs.getInt("id")));

            user.setName(rs.getString("name"));

            user.setAge(new Integer(rs.getInt("age")));

            return user;

        }

    };

    JdbcTemplate simpleJdbcTemplate =

          new JdbcTemplate(this.getDataSource());

    return (User) jdbcTemplate.queryForObject(sql, mapper, id);

}

若改用SimpleJdbcTemplate,则可以如下编写:

public User find(Integer id) {

   String sql = "SELECT * FROM user WHERE id=?";

   ParameterizedRowMapper mapper =

    new ParameterizedRowMapper () {

        public User mapRow(ResultSet rs, int rowNum)

                                             throws SQLException {

            User user = new User();

            user.setId(new Integer(rs.getInt("id")));

            user.setName(rs.getString("name"));

            user.setAge(new Integer(rs.getInt("age")));

            return user;

        }

    };

    SimpleJdbcTemplate simpleJdbcTemplate =

        new SimpleJdbcTemplate(this.getDataSource());

    return simpleJdbcTemplate.queryForObject(sql, mapper, id);

}

可以注意到,当中使用了泛型功能,可以直接在mapRow()方法之后传回User实例,而SimpleJdbcTemplate的queryForObject()方法也可以直接传回查询结果的User实例。下面将JdbcTemplateDemo项目的UserDAO改写为使用SimpleJdbcTemplate的方式:

SimpleJdbcTemplateDemo                                                       UserDAO.java

package onlyfun.caterpillar;

import java.sql.ResultSet;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.springframework.jdbc.core.simple.ParameterizedRowMapper;

import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;

public class UserDAO implements IUserDAO {

    private SimpleJdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {

        jdbcTemplate = new SimpleJdbcTemplate(dataSource);

    }

    public void insert(User user) {

        String sql = "INSERT INTO user (name,age) VALUES(?, ?)";

        String name = user.getName();

        Integer age = user.getAge();

        jdbcTemplate.update(sql, new Object[] {name, age});

     }

    public User find(Integer id) {

        String sql = "SELECT * FROM user WHERE id=?";

        ParameterizedRowMapper mapper =

          new ParameterizedRowMapper () {

            public User mapRow(ResultSet rs, int rowNum)

                                            throws SQLException {

                User user = new User();

                user.setId(new Integer(rs.getInt("id")));

                user.setName(rs.getString("name"));

                user.setAge(new Integer(rs.getInt("age")));

                return user;

            }

        };

        return jdbcTemplate.queryForObject(sql, mapper, id);

    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值