• 使用JDBC笼统来说,有三步:

    • 连接数据库
    • 执行SQL语句
    • 获得结果

上一篇JDBC(上)我们主要讨论第一步:如何连接数据库(获取Connection),这一篇聊聊如何利用Connection进行CRUD。期间会引入部分设计模式不断重构代码,最终实现类似JDBCTemplate的模板类,方便其他类继承或组合以提高开发效率。

由于篇幅有限,这里直接用PreparedStatemenet,不去讲解Statement及两者区别。

主要内容:

  • 基础示例
  • 抽取重复代码(JdbcUtils工具类)
  • CRUD
  • 异常处理
  • 模板方法模式重构
  • 策略模式重构

------

## 基础示例

基本思路

通过DriverManager得到Connection,由Connection得到PreparedStatement,preparedStatement执行sql返回结果集。

img

数据库表结构

img

public class Basic {
    @Test
    public void testJdbc() throws SQLException, ClassNotFoundException {
        // 1.注册驱动
        Class.forName("com.mysql.jdbc.Driver");

        // 2.建立连接
        String url = "jdbc:mysql://192.168.136.128:3306/test";
        String user = "root";
        String password = "root";
        Connection conn = DriverManager.getConnection(url, user, password);

        // 3.创建sql模板
        String sql = "select * from t_user where id = ?";
        PreparedStatement preparedStatement = conn.prepareStatement(sql);

        // 4.设置模板参数
        preparedStatement.setInt(1, 5);

        // 4.执行语句
        ResultSet rs = preparedStatement.executeQuery();

        // 5.处理结果
        while (rs.next()) {
            System.out.println(rs.getObject(1) + "\t" + rs.getObject(2) + "\t"
                    + rs.getObject(3) + "\t" + rs.getObject(4));
        }

        // 6.释放资源
        rs.close();
        preparedStatement.close();
        conn.close();
    }
}

img

------

## 抽取重复代码

上面示例代码,有两个问题:

  • 获取Connection的步骤太复杂,需要封装
  • 资源释放太随意,不够规范

尤其第二点,一定要重视。数据库的连接数是有限的,如果不及时释放,会导致其他请求无法访问。应该把释放资源的操作放在finally中,保证资源一定会被关闭。

Basic

public class Basic {
        //请注意,这里抛了异常
    @Test
    public void testJdbc() throws SQLException {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            // 1.获取连接
            conn = JdbcUtils.getConnection();

            // 2.创建sql模板
            String sql = "select * from t_user where id = ?";
            ps = conn.prepareStatement(sql);

            // 3.设置模板参数
            ps.setInt(1, 5);

            // 4.执行语句
            rs = ps.executeQuery();

            // 5.处理结果
            while (rs.next()) {
                System.out.println(rs.getObject(1) + "\t" + rs.getObject(2) + "\t"
                        + rs.getObject(3) + "\t" + rs.getObject(4));
            }
        } finally {
            // 6.释放资源
            JdbcUtils.free(rs, ps, conn);
        }
    }
}

JdbcUtils 1.0版

public class JdbcUtils {

    private static Properties props = null;

    // 只在JdbcUtils类被加载时执行一次!
    static {
        // 给props进行初始化,即加载jdbc.properties文件到props对象中
        try {
            InputStream in = JdbcUtils.class.getClassLoader()
                    .getResourceAsStream("jdbc.properties");
            props = new Properties();
            props.load(in);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        // 加载驱动类
        try {
            Class.forName(props.getProperty("driver"));
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    // 获取连接
    public static Connection getConnection() throws SQLException {
        // 得到Connection
        return DriverManager.getConnection(props.getProperty("url"),
                props.getProperty("username"),
                props.getProperty("password"));
    }

    // 释放连接
    public static void free(ResultSet rs, Statement st, Connection conn) {
        try {
            if (rs != null)
                rs.close();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (st != null)
                    st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                if (conn != null)
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
            }
        }
    }
}

------

CRUD

目录结构

img

UserDao

public interface UserDao {
    int addUser(User user) throws SQLException;
    int update(User user) throws SQLException;
    int delete(User user) throws SQLException;
    User getUser(int Id) throws SQLException;
    User findUser(String name, int age) throws SQLException;
}

UserDaoJdbcImpl

public class UserDaoJdbcImpl implements UserDao {

    public int addUser(User user) throws SQLException {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            String sql = "insert into t_user(name,age, birthday) values (?,?,?) ";
            ps = conn.prepareStatement(sql);
            ps.setString(1, user.getName());
            ps.setInt(2, user.getAge());
            ps.setDate(3, new java.sql.Date(user.getBirthday().getTime()));
            return ps.executeUpdate();
        } finally {
            JdbcUtils.free(rs, ps, conn);
        }
    }

    public int delete(User user) throws SQLException {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            String sql = "delete from t_user where id=?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1, user.getId());
            System.out.println(sql);
            return ps.executeUpdate(sql);
        } finally {
            JdbcUtils.free(rs, ps, conn);
        }

    }

    public int update(User user) throws SQLException {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            String sql = "update t_user set name=?, age=?, birthday=? where id=? ";
            ps = conn.prepareStatement(sql);
            ps.setString(1, user.getName());
            ps.setInt(2, user.getAge());
            ps.setDate(3, new java.sql.Date(user.getBirthday().getTime()));
            ps.setInt(4, user.getId());
            return ps.executeUpdate();
        } finally {
            JdbcUtils.free(rs, ps, conn);
        }
    }

    public User findUser(String name, int age) throws SQLException {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        User user = null;
        try {
            conn = JdbcUtils.getConnection();
            String sql = "select id, name, birthday  from t_user where name=? and age=?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, name);
            ps.setInt(2, age);
            rs = ps.executeQuery();
            while (rs.next()) {
                user = new User();
                user.setId(rs.getInt("id"));
                user.setAge(rs.getInt("age"));
                user.setName(rs.getString("name"));
                user.setBirthday(rs.getDate("birthday"));
            }
        } finally {
            JdbcUtils.free(rs, ps, conn);
        }
        return user;
    }

    public User getUser(int userId) throws SQLException {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        User user = null;
        try {
            conn = JdbcUtils.getConnection();
            String sql = "select id, name, age, birthday from t_user where id=?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1, userId);
            rs = ps.executeQuery();
            while (rs.next()) {
                user = new User();
                user.setId(rs.getInt("id"));
                user.setAge(rs.getInt("age"));
                user.setName(rs.getString("name"));
                user.setBirthday(rs.getDate("birthday"));
            }
        } finally {
            JdbcUtils.free(rs, ps, conn);
        }
        return user;
    }

}

DAOTest

public class DAOTest {
    public static void main(String[] args) throws SQLException {
        UserDao userDao = new UserDaoJdbcImpl();
        User user = new User();
        user.setAge(19);
        user.setName("little ming");
        user.setBirthday(new Date());
        userDao.addUser(user);
    }
}

img

------

## 异常处理

上面的CRUD并没有捕获异常,而是直接往外抛。这会带来两个后果:

  • SQLException是编译时异常,Service在调用DAO时必须处理异常,否则编译不通过。如何处理?要么继续抛,交给Controller处理(意义不大),要么try catch(Service层代码很臃肿,不美观)。
  • DAO接口有声明异常SQLException,这等于向外界暴露DAO层是JDBC实现。而且针对该接口只能用关系型数据库,耦合度太高了。后期无法切换DAO实现。

比较好的做法是,将SQLException转为运行时异常抛出,Service层可处理也可不处理。

我们需要自定义一个DaoException:

public class DaoException extends RuntimeException {
    public DaoException() {
    }

    public DaoException(String message) {
        super(message);
    }

    public DaoException(Throwable cause) {
        super(cause);
    }

    public DaoException(String message, Throwable cause) {
        super(message, cause);
    }
}

UserDaoJdbcImpl(捕获编译时异常,转为运行时异常)

public class UserDaoJdbcImpl implements UserDao {

    public int addUser(User user) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            String sql = "insert into t_user(name,age, birthday) values (?,?,?) ";
            ps = conn.prepareStatement(sql);
            ps.setString(1, user.getName());
            ps.setInt(2, user.getAge());
            ps.setDate(3, new java.sql.Date(user.getBirthday().getTime()));
            return ps.executeUpdate();
        } catch (SQLException e) {
                        //转为DaoException(运行时异常)抛出,Service层可以不处理                         
            throw new DaoException(e.getMessage(), e);
        } finally {
            JdbcUtils.free(rs, ps, conn);
        }
    }

    public int delete(User user) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            String sql = "delete from t_user where id=?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1, user.getId());
            System.out.println(sql);
            return ps.executeUpdate(sql);
        } catch (SQLException e) {
            throw new DaoException(e.getMessage(), e);
        } finally {
            JdbcUtils.free(rs, ps, conn);
        }

    }

    public int update(User user) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            String sql = "update t_user set name=?, age=?, birthday=? where id=? ";
            ps = conn.prepareStatement(sql);
            ps.setString(1, user.getName());
            ps.setInt(2, user.getAge());
            ps.setDate(3, new java.sql.Date(user.getBirthday().getTime()));
            ps.setInt(4, user.getId());
            return ps.executeUpdate();
        } catch (SQLException e) {
            throw new DaoException(e.getMessage(), e);
        } finally {
            JdbcUtils.free(rs, ps, conn);
        }
    }

    public User findUser(String name, int age) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        User user = new User();
        try {
            conn = JdbcUtils.getConnection();
            String sql = "select id, name, birthday  from t_user where name=? and age=?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, name);
            ps.setInt(2, age);
            rs = ps.executeQuery();
            while (rs.next()) {
                user.setName(rs.getString("name"));
                user.setBirthday(rs.getDate("birthday"));
            }
        } catch (SQLException e) {
            throw new DaoException(e.getMessage(), e);
        } finally {
            JdbcUtils.free(rs, ps, conn);
        }
        return user;
    }

    public User getUser(int userId) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        User user = new User();
        try {
            conn = JdbcUtils.getConnection();
            String sql = "select id, name, age, birthday from t_user where id=?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1, userId);
            rs = ps.executeQuery();
            while (rs.next()) {
                user.setId(rs.getInt("id"));
                user.setAge(rs.getInt("age"));
                user.setName(rs.getString("name"));
                user.setBirthday(rs.getDate("birthday"));
            }
        } catch (SQLException e) {
            throw new DaoException(e.getMessage(), e);
        } finally {
            JdbcUtils.free(rs, ps, conn);
        }
        return user;
    }

}

------

## 模板方法模式重构

上面的CRUD代码太繁琐了!如果还有StudentDaoJdbcImpl、TeacherDaoJdbcImpl,那么同样的代码要写好多遍。所以,必须要重构。大体思路是:相同的代码抽取到父类AbstractDao。观察UserDao:

img

增删改除了方法名不同,其他都一样,可以归为一类;查询单独一类。

我们先考虑如何重构增删改方法(以delete为观察点)。

img

我们发现,只有sql模板和设置模板参数的代码不同。可以把sql和参数抽取成父类方法的形参。

AbstractDao

public abstract class AbstractDao {
    // 增删改
    public int update(String sql, Object...args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            // sql由调用者传入
            ps = conn.prepareStatement(sql);
            // 遍历设置模板参数
            for (int i = 0; i < args.length; i++){
                ps.setObject(i + 1, args[i]);
            }
            return ps.executeUpdate();
        } catch (SQLException e) {
            throw new DaoException(e.getMessage(), e);
        } finally {
            JdbcUtils.free(rs, ps, conn);
        }
    }
}

UserDaoImpl

public class UserDaoImpl extends AbstractDao implements UserDao {
    //增
    public int addUser(User user) {
        String sql = "insert into t_user(name, age, birthday) values (?,?,?) ";
        Object[] args = new Object[]{user.getName(), user.getAge(),
                user.getBirthday()};
        //调用父类AbstractDao的方法
        return super.update(sql, args);
    }

    //删
    public int update(User user) {
        String sql = "update t_user set name=?, age=?, birthday=? where id=? ";
        Object[] args = new Object[]{user.getName(), user.getAge(),
                user.getBirthday(), user.getId()};
        return super.update(sql, args);
    }

    //改
    public int delete(User user) {
        String sql = "delete from t_user where id=?";
        Object[] args = new Object[]{user.getId()};
        return super.update(sql, args);
    }
        //待实现
    public User getUser(int Id) {
        return null;
    }
        //待实现
    public User findUser(String name, int age) {
        return null;
    }
}

示意图

img

也就是说,AbstractDao的作用仅仅是“组装sql语句并执行”,不涉及差异性代码。UserDao/StudentDao/TeacherDao...等有差异的代码仅仅是sql和参数,交给具体实现类编写。

接下来,我们考虑一下如何抽取查询方法。

查询最麻烦的地方在于结果集映射。比如,UserDaoImpl查询得到User,StudentDaoImpl查询得到Student。

img

  • 返回值:用Object接受
  • sql和模板参数:效仿update抽取即可
  • 结果集映射:???

我们发现,父类无法制定一个通用代码满足所有子类的结果集映射,因为只有子类自己知道映射规则。所以,我们只能把结果集映射的权利交还给子类去实现。子类如果需要查询,就必须自己实现AbstractDao的rowMapper方法。

AbstractDao

public abstract class AbstractDao {
    // 增删改
    public int update(String sql, Object...args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            // sql由调用者传入
            ps = conn.prepareStatement(sql);
            // 遍历设置模板参数
            for (int i = 0; i < args.length; i++){
                ps.setObject(i + 1, args[i]);
            }
            return ps.executeUpdate();
        } catch (SQLException e) {
            throw new DaoException(e.getMessage(), e);
        } finally {
            JdbcUtils.free(rs, ps, conn);
        }
    }

    //查询
    public Object query(String sql, Object[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            // sql由调用者传入
            ps = conn.prepareStatement(sql);
            // 遍历设置模板参数
            for (int i = 0; i < args.length; i++)
                ps.setObject(i + 1, args[i]);
            rs = ps.executeQuery();
            Object obj = null;
            // 结果集映射,子类必须实现抽象方法rowMapper
            if (rs.next()) {
                obj = rowMapper(rs);
            }
            return obj;
        } catch (SQLException e) {
            throw new DaoException(e.getMessage(), e);
        } finally {
            JdbcUtils.free(rs, ps, conn);
        }
    }

    // 定义成抽象方法,让子类去实现
    abstract protected Object rowMapper(ResultSet rs);
}

UserDaoImpl

public class UserDaoImpl extends AbstractDao implements UserDao {
    //增
    public int addUser(User user) {
        String sql = "insert into t_user(name, age, birthday) values (?,?,?) ";
        Object[] args = new Object[]{user.getName(), user.getAge(),
                user.getBirthday()};
        //调用父类AbstractDao的方法
        return super.update(sql, args);
    }

    //删
    public int update(User user) {
        String sql = "update t_user set name=?, age=?, birthday=? where id=? ";
        Object[] args = new Object[]{user.getName(), user.getAge(),
                user.getBirthday(), user.getId()};
        return super.update(sql, args);
    }

    //改
    public int delete(User user) {
        String sql = "delete from t_user where id=?";
        Object[] args = new Object[]{user.getId()};
        return super.update(sql, args);
    }
      
        //查询
    public User getUser(int id) {
        String sql = "select id, name, age, birthday from t_user where id=?";
        Object[] args = new Object[]{id};
        Object user = super.query(sql, args);
        return (User) user;
    }

        //查询
    public User findUser(String name, int age) {
        String sql = "select id, name, age, birthday from t_user where name=? and age=?";
        Object[] args = new Object[]{name, age};
        Object user = super.query(sql, args);
        return (User) user;
    }

    //UserDaoImpl的结果集映射器
    protected Object rowMapper(ResultSet rs) {
        User user = null;
        try {
            user = new User();
            user.setId(rs.getInt("id"));
            user.setAge(rs.getInt("age"));
            user.setName(rs.getString("name"));
            user.setBirthday(rs.getDate("birthday"));
        } catch (SQLException e) {
            throw new DaoException("mapping error");
        }
        return user;
    }
}

示意图

img

觉得不好理解?换个角度:

img

至此,模板方法模式重构结束。但是,代码仍然存在隐患。

------

## 策略模式重构

我们来分析一个问题:假设现在UserDao增加了一个新方法:

public interface UserDao {
    int addUser(User user);
    int update(User user);
    int delete(User user);
    User getUser(int Id);
    User findUser(String name, int age);
    //新增查询方法:根据年龄查询
    List<User> selectUsers(int age);
}

返回值是List<User>,而UserDaoImpl中实现的映射方法rowMapper()只能封装User对象:

public class UserDaoImpl extends AbstractDao implements UserDao {
        //利用rowMapper封装bean
    public User getUser(int id) {
        String sql = "select id, name, age, birthday from t_user where id=?";
        Object[] args = new Object[]{id};
        Object user = super.query(sql, args);
        return (User) user;
    }
        //无法利用rowMapper封装List
    public List<User> selectUsers(int age) {
        String sql = "select id, name, age, birthday from t_user where age=?";
        Object[] args = new Object[]{age};
        List<User> list = super.query(sql, args);
        return list;
    }

    //UserDaoImpl的结果集映射器
    protected Object rowMapper(ResultSet rs) {
        User user = null;
        try {
            user = new User();
            user.setId(rs.getInt("id"));
            user.setAge(rs.getInt("age"));
            user.setName(rs.getString("name"));
            user.setBirthday(rs.getDate("birthday"));
        } catch (SQLException e) {
            throw new DaoException("mapping error");
        }
        return user;
    }
}

也就是说,根据返回值的不同(String,User,List<User>)每个查询方法的“映射规则”有所不同。

img

应该把“映射规则”单独抽取出来,和sql、args一样,在调用父类方法时传入。比如调用getUser()和selectUser()传入的sql、args不同,映射规则也不同。如此一来,sql模板、模板参数、映射规则都高度灵活,可定制。

img

但是我们很清楚,所谓的“映射规则”其实就是一个方法,比如原先的rowMapper()方法:

        //UserDaoImpl的结果集映射器
    protected Object rowMapper(ResultSet rs) {
        User user = null;
        try {
            user = new User();
            user.setId(rs.getInt("id"));
            user.setAge(rs.getInt("age"));
            user.setName(rs.getString("name"));
            user.setBirthday(rs.getDate("birthday"));
        } catch (SQLException e) {
            throw new DaoException("mapping error");
        }
        return user;
    }

在JS中,可以把function作为参数传递,然后在另一个方法内部执行。

img

但是Java不行...怎么办?

没关系,我们之前学习动态代理时其实早就接触过这种思想:直接传方法不行?那我就把这个方法塞进一个对象里,通过对象去调用方法(把需要代理对象执行的代码写在InvocationHandler对象的invoke方法中,再把invocationHandler塞进代理对象供它调用)。

所以,这次也照做就行了。只不过这种模式其实叫策略模式,而且一般是传入接口的实现类。

img

好了,现在子类已经不需要实现父类的抽象方法了(一个规则无法满足不同返回值映射),改为由子类实现RowMapper接口传入匿名对象的方式,所以AbstractDao中的抽象方法可以删除。也就是说AbstractDao已经没有抽象方法了。于是我把它声明为普通类(可以new),并改名为MyJDBCTemplate。而且,使用MyJDBCTemplate时,我决定不再使用继承,而是选择组合方式(组合比继承灵活)。

img

RowMapper

public interface RowMapper {
    //映射结果集
    Object mapRow(ResultSet rs) throws SQLException;
}

MyJDBCTemplate

public class MyJDBCTemplate {
    // 增删改
    public int update(String sql, Object...args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            // sql由调用者传入
            ps = conn.prepareStatement(sql);
            // 遍历设置模板参数
            for (int i = 0; i < args.length; i++){
                ps.setObject(i + 1, args[i]);
            }
            return ps.executeUpdate();
        } catch (SQLException e) {
            throw new DaoException(e.getMessage(), e);
        } finally {
            JdbcUtils.free(rs, ps, conn);
        }
    }

    //查询,RowMapper是接口类型,需要传入接口的实现类对象
    public List<Object> query(String sql, Object[] args, RowMapper rowMapper) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        List list = new ArrayList();
        try {
            conn = JdbcUtils.getConnection();
            // sql由调用者传入
            ps = conn.prepareStatement(sql);
            // 遍历设置模板参数
            for (int i = 0; i < args.length; i++)
                ps.setObject(i + 1, args[i]);
            rs = ps.executeQuery();
            Object obj = null;
            // 映射规则由子类传入
            while (rs.next()) {
                Object o = rowMapper.mapRow(rs);
                list.add(o);
            }
            return list;
        } catch (SQLException e) {
            throw new DaoException(e.getMessage(), e);
        } finally {
            JdbcUtils.free(rs, ps, conn);
        }
    }
}

UserDaoImpl

public class UserDaoImpl implements UserDao {
    MyJDBCTemplate jdbcTemplate = new MyJDBCTemplate();

    //增
    public int addUser(User user) {
        String sql = "insert into t_user(name, age, birthday) values (?,?,?) ";
        Object[] args = new Object[]{user.getName(), user.getAge(),
                user.getBirthday()};
        //调用jdbcTemplate的update方法
        return jdbcTemplate.update(sql, args);
    }

    //删
    public int update(User user) {
        String sql = "update t_user set name=?, age=?, birthday=? where id=? ";
        Object[] args = new Object[]{user.getName(), user.getAge(),
                user.getBirthday(), user.getId()};
        //调用jdbcTemplate的update方法
        return jdbcTemplate.update(sql, args);
    }

    //改
    public int delete(User user) {
        String sql = "delete from t_user where id=?";
        Object[] args = new Object[]{user.getId()};
        //调用jdbcTemplate的update方法
        return jdbcTemplate.update(sql, args);
    }

        //查询单个
    public User getUser(int id) {
        String sql = "select id, name, age, birthday from t_user where id=?";
        Object[] args = new Object[]{id};
        //调用jdbcTemplate的query方法,传入sql,args,RowMapper匿名对象
        List list = jdbcTemplate.query(sql, args, new RowMapper() {
            public Object mapRow(ResultSet rs) throws SQLException {
                User user = new User();
                user.setId(rs.getInt("id"));
                user.setAge(rs.getInt("age"));
                user.setName(rs.getString("name"));
                user.setBirthday(rs.getDate("birthday"));
                return user;
            }
        });
        return (User)list.get(0);
    }

    public User findUser(String name, int age) {
        String sql = "select id, name, age, birthday from t_user where name=? and age=?";
        Object[] args = new Object[]{name, age};
        //调用jdbcTemplate的query方法,传入sql,args,RowMapper匿名对象
        List list = jdbcTemplate.query(sql, args, new RowMapper() {
            public Object mapRow(ResultSet rs) throws SQLException {
                User user = new User();
                user.setId(rs.getInt("id"));
                user.setAge(rs.getInt("age"));
                user.setName(rs.getString("name"));
                user.setBirthday(rs.getDate("birthday"));
                return user;
            }
        });
        return (User)list.get(0);
    }
    
        //查询多个
    public List selectUsers(int age) {
        String sql = "select id, name, age, birthday from t_user where age=?";
        Object[] args = new Object[]{age};
        //调用jdbcTemplate的query方法,传入sql,args,RowMapper匿名对象
        List list = jdbcTemplate.query(sql, args, new RowMapper() {
            public Object mapRow(ResultSet rs) throws SQLException {
                    User user = new User();
                    user.setId(rs.getInt("id"));
                    user.setAge(rs.getInt("age"));
                    user.setName(rs.getString("name"));
                    user.setBirthday(rs.getDate("birthday"));
                    return user;
            }
        });
        return list;
    }
}

测试

img

------

当然,上面的所谓JDBCTemplate是非常粗糙的,还存在很多不严谨的地方,就当是个玩具。有兴趣的朋友可以去看看Spring的JDBCTemplate。

本来还想继续介绍结果集元数据重构、及工厂模式重构的,这一篇有点长了,下一篇和连接池一起讲吧。

参考资料:

传智播客 李勇 JDBC