JDBC工具类——JdbcUtils(4)

JDBC工具类——JdbcUtils(4)

前言

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

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

回顾

在上一篇文章中,我们实现了下面两个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;
    }
}

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;
    }
}

这两个RowMapper虽然代码有很大的不同,但是它们本质上都在做同一件事:把结果集中的一行转换成一个JavaBean。当然,这要求JavaBean中的属性名与结果集中的列名一一对应,同时数据类型也要匹配。可以想象,如果我们的项目中有n个不同的JavaBean,那么就要写n个不同的RowMapper,而且每个RowMapper的实现都是十分枯燥乏味的。

要解决这个问题,我们需要实现一个通用的JavaBean行转换器。

实现BeanRowMapper<T>

以下是BeanRowMapper<T>的实现,用于将ResultSet的当前行转换成指定的JavaBean:

public class BeanRowMapper<T> implements RowMapper<T>
{
    private final Class<T> type;

    public BeanRowMapper(Class<T> type)
    {
        this.type = type;
    }

    @Override
    public T map(ResultSet rs) throws Exception
    {
        T t = type.getDeclaredConstructor().newInstance();
        BeanUtils.populate(t, new MapRowMapper().map(rs));
        return t;
        ResultSetMetaData metaData = rs.getMetaData();
        int count = metaData.getColumnCount();
        T bean = type.getDeclaredConstructor().newInstance();
        for (int i = 1; i <= count; i++)
        {
            PropertyDescriptor pd = new PropertyDescriptor(metaData.getColumnLabel(i), type);
            Method setter = pd.getWriteMethod();
            setter.invoke(bean, rs.getObject(i));
        }
        return bean;
    }
}

BeanRowMapper<T>的构造函数中,传入了JavaBean的Class<T>

public BeanRowMapper(Class<T> type)
{
    this.type = type;
}

map方法中,首先获取了ResultSet的总列数:

ResultSetMetaData metaData = rs.getMetaData();
int count = metaData.getColumnCount();

然后在for循环中遍历ResultSet的所有列。在for循环体内,首先获取当前列名,然后通过调用JavaBean的getter方法,对JavaBean的同名属性赋值,其中用到了JDK中的反射和内省机制:

for (int i = 1; i <= count; i++)
{
    PropertyDescriptor pd = new PropertyDescriptor(metaData.getColumnLabel(i), type);
    Method setter = pd.getWriteMethod();
    setter.invoke(bean, rs.getObject(i));
}

有了BeanRowMapper<T>,再配合上一篇文章中实现的ListResultSetMapper<T>,列表查询需求就能进一步简化:

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

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

所有对JavaBean的转换都能复用BeanRowMapper<T>,用户再也不用单独对每一种实体类型都写一个转换器了!

实现SingleRowRecordMapper<T>

有时候,查询的结果集中只有一行,例如查询指定id的用户:

SELECT * FROM users WHERE id = 1001

对于这种情况,可以写一个SingleRowRecordMapper<T>来从结果集中获取第一行数据:

public class SingleRowRecordMapper<T> implements RecordMapper<T>
{
    private final RowMapper<T> rowMapper;

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

    @Override
    public T map(Record record)
    {
        if (record.next())
        {
            return rowMapper.map(record.getCurrentRow());
        }
        return null;
    }
}

rowMapper字段可以传入预定义的行处理器,也可以传入用户自定义的行处理器。这个类可以配合BeanRowMapper<T>一起使用:

// 查询id为1001的用户
User user = JdbcUtils.query("SELECT * FROM users WHERE id = ?",
                new SingleRowRecordMapper<>(new BeanRowMapper<>(User.class)),
                1001);

注意,由于query函数是泛型函数,所以编译器可以自动推断返回类型。

实现SingleColumnRowMapper<T>

有时候,查询的结果集中只有一个值,例如查询用户总数:

SELECT COUNT(*) FROM users

对于这种情况,可以写一个SingleColumnRowMapper<T>来从行中获取唯一的列值:

public class SingleColumnRowMapper<T> implements RowMapper<T>
{
    private final Class<T> type;

    public SingleColumnRowMapper(Class<T> type)
    {
        this.type = type;
    }

    @Override
    public T map(ResultSet rs) throws Exception
    {
        return type.cast(rs.getObject(1));
    }
}

这里的type字段用来指定列值的类型(一般来说是IntegerString等基本类型)。

为实现查询用户总数的需求,需要配合前面的SingleRowRecordMapper<T>使用:

// 查询用户总数
Integer count = JdbcUtils.query("SELECT COUNT(*) FROM users",
                new SingleRowRecordMapper<>(new SingleColumnRowMapper<>(Integer.class)));

总结

这次我们实现了几个通用的RowMapperResultSetMapper,用户通过组合不同的RowMapperResultSetMapper就能实现不同的查询需求,从中我们可以体会到”结果集处理器“和”行处理器“这两个抽象概念的强大之处。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值