zoukankan      html  css  js  c++  java
  • 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就能实现不同的查询需求,从中我们可以体会到”结果集处理器“和”行处理器“这两个抽象概念的强大之处。

  • 相关阅读:
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    互联网大厂CTO详解5大软件架构,看完这篇你就秒懂
    5-10年的DBA如何独当一面?这10个建议送给你(附图书工具推荐)
    2020 从新开始:你应该知道的Oracle认证新变化
    Centos7部署NFS实战
    你的公司,远程办公多久了?
    PostgreSQL的几种分布式架构对比
    数据库周刊 | DBA 核心技能
    理解redis 分布式中的分片机制
  • 原文地址:https://www.cnblogs.com/baiyuxuan/p/14329614.html
Copyright © 2011-2022 走看看