JDBC工具类——JdbcUtils(3)

JDBC工具类——JdbcUtils(3)

前言

本系列文章介绍JDBC工具类——JdbcUtils的封装,部分实现参考了Spring框架的JdbcTemplate

完整项目地址:https://github.com/byx2000/JdbcUtils

回顾

在上一篇文章中,我们抽象出了一个ResultSetMapper<T>接口,用于封装用户对结果集的处理。

假如用户想把结果集转换成一个User列表,则需要写如下实现类:

public class UserListResultSetMapper implements ResultSetMapper<List<User>>
{
    @Override
    public List<User> map(ResultSet rs) throws Exception
    {
        List<User> users = new ArrayList<>();
        while (rs.next())
        {
            User user = new User();
            user.setId(rs.getInt("id"));
            user.setUsername(rs.getString("username"));
            user.setPassword(rs.getString("password"));
            users.add(user);
        }
        return users;
    }
}

假如用户想把结果集转换成一个Book列表,则需要写如下实现类:

public class BookListResultSetMapper implements ResultSetMapper<List<Book>>
{
    @Override
    public List<User> map(ResultSet rs) throws Exception
    {
        List<Book> books = new ArrayList<>();
        while (rs.next())
        {
            Book book = new Book();
            book.setId(rs.getInt("id"));
            book.setName(rs.getString("name"));
            book.setAuthor(rs.getString("author"));
            books.add(book);
        }
        return books;
    }
}

仔细观察两个实现类的代码,可以发现存在着如下重复的结构:

...
while (rs.next())
{
    ...
}

这提醒我们,还可以进一步抽象。

抽象RowMapper<T>

我们知道,数据库的查询结果是一张表,而一张表中每行的格式都是相同的。所以,用户真正关心的问题不是如何处理整个结果集,而是如何处理结果集中的每一行

于是,我们可以抽象出一个RowMapper<T>接口,用来封装对数据行的处理:

/**
 * 行转换器接口
 * @param <T> 转换类型
 */
public interface RowMapper<T>
{
    T map(ResultSet rs) throws Exception;
}

仔细一看,RowMapper<T>接口和ResultSetMapper<T>接口非常像,甚至它们里面的map方法签名都是一样的。但是这两个接口有本质上的不同:ResultSetMapper<T>接口封装了对整个结果集的处理,而RowMapper<T>接口封装了对结果集中一行数据的处理。也就是说,在RowMapper<T>接口的实现类中,只能调用ResultSet.getXXX方法获取列中的值,而不会调用ResultSet.next方法。

下面是把数据行转换成User类的RowMapper实现类:

public class UserRowMapper implements RowMapper<User>
{
    @Override
    public User map(ResultSet rs) throws Exception
    {
        User user = new User();
        user.setId(rs.getInt("id"));
        user.setUsername(rs.getString("username"));
        user.setPassword(rs.getString("password"));
        return user;
    }
}

下面是把数据行转换成Book类的RowMapper实现类:

public class BookRowMapper implements RowMapper<Book>
{
    @Override
    public User map(ResultSet rs) throws Exception
    {
        Book book = new Book();
        book.setId(rs.getInt("id"));
        book.setName(rs.getString("name"));
        book.setAuthor(rs.getString("author"));
        return book;
    }
}

实现ListResultSetMapper<T>

在大部分时候,都需要将整个结果集转换成一个列表,所以我们实现一个ListResultSetMapper<T>类,并配合RowMapper<T>完成这项工作:

public class ListResultSetMapper<T> implements ResultSetMapper<List<T>>
{
    private final RowMapper<T> rowMapper;

    public ListResultSetMapper(RowMapper<T> rowMapper)
    {
        this.rowMapper = rowMapper;
    }

    @Override
    public List<T> map(ResultSet rs) throws Exception
    {
        List<T> result = new ArrayList<>();
        while (rs.next())
        {
            result.add(rowMapper.map(rs));
        }
        return result;
    }
}

有了ListResultSetMapper<T>,再加上上面的两个RowMapper<T>实现类,查询操作就可以简化成如下形式:

// 查询所有id大于5的用户
List<User> users = JdbcUtils.query("SELECT * FROM users WHERE id > ?",
                new ListResultSetMapper<>(new UserRowMapper()),
                5);

// 查询所有图书
List<Book> books = JdbcUtils.query("SELECT * FROM books",
                new ListResultSetMapper<>(new BookRowMapper()));

当然,UserRowMapperBookRowMapper这两个类也可以写成匿名内部类或者lambda表达式的形式。

总结

到目前为止,我们抽象出了ResultSetMapper<T>RowMapper<T>这两个重要概念,对结果集的处理进行了更加细致的责任划分。在下一篇文章中,将介绍几个通用的RowMapperResultSetMapper的实现。

posted @ 2021-01-26 12:02  baiyuxuan  阅读(42)  评论(0编辑  收藏  举报