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()));
当然,UserRowMapper
和BookRowMapper
这两个类也可以写成匿名内部类或者lambda表达式的形式。
总结
到目前为止,我们抽象出了ResultSetMapper<T>
和RowMapper<T>
这两个重要概念,对结果集的处理进行了更加细致的责任划分。在下一篇文章中,将介绍几个通用的RowMapper
和ResultSetMapper
的实现。