使用each api的一个痛点

image.png

public static void build(Config config, ResultSet rs, BiFunction<Record, Boolean, Boolean> func) throws SQLException {
    ResultSetMetaData rsmd = rs.getMetaData();
    int columnCount = rsmd.getColumnCount();
    String[] labelNames = new String[columnCount + 1];
    int[] types = new int[columnCount + 1];
    ModelBuilder.me.buildLabelNamesAndTypes(rsmd, labelNames, types);
    Record previous = null;
    while (rs.next()) {
        Record record = new Record();
        CPI.setColumnsMap(record, config.getContainerFactory().getColumnsMap());
        Map<String, Object> columns = record.getColumns();
        for (int i = 1; i <= columnCount; i++) {
            Object value;
            if (types[i] < Types.BLOB) {
                value = rs.getObject(i);
            } else {
                if (types[i] == Types.CLOB) {
                    value = ModelBuilder.me.handleClob(rs.getClob(i));
                } else if (types[i] == Types.NCLOB) {
                    value = ModelBuilder.me.handleClob(rs.getNClob(i));
                } else if (types[i] == Types.BLOB) {
                    value = ModelBuilder.me.handleBlob(rs.getBlob(i));
                } else {
                    value = rs.getObject(i);
                }
            }
            columns.put(labelNames[i], value);
        }
        if (func != null && previous != null) {
            if (!func.apply(previous, false)) {
                break;
            }
        }
        previous = record;
    }
    if (func != null && previous != null) {
        func.apply(previous, true);
    }
}


评论区

JFinal

2021-01-12 15:21

这个没有好的设计方法,因为迭代是在从 JDBC ResultSet 中读数据的时候进行的

而那个时候, resultSet 只是一个迭代器,也不知道是不是处在最后一条记录

就是说封装受限于被封装功能提供的特性

JFinal

2021-01-12 15:22

补充一下,代码不要发截图,社区发文功能提供了输入代码的功能,认真尝试一下,第二个按钮就是代码按钮

chcode

2021-01-12 15:23

@JFinal 但是JDBC 的RetSet 的确提供isLast()方法可以知道是否最后一条

JFinal

2021-01-12 15:46

@chcode 不同数据库可能不支持, JDBC 底层的很多东东都不靠谱, 而且针对每条记录都判断一次 isLast(), 这个对性能肯定是有影响的,而且大概率影响很大

因为 ResultSet 中的数据不是一次性从数据库读出来的,是不断有 io 操作的

JFinal

2021-01-12 15:55

@chcode 简单说就是 ReultSet 并不是一开始就知道共有多少条记录被查出来,那么 isLast() 就需要有 io 操作,强制得到所有数据才能得到 isLast(),这样的话,就算数据量再大也要一次性得到所有数据,性能会拉低

也可能是由于这类原因,不同的数据库对 JDBC 规范的实现有差异,有些干脆不去实现

便如,jfinal 在开发 active record 模块的 generate 模块的时候,获取一些 meta 信息生成 model、baseModel 时,有些数据库行为就是不一样,造成很多麻烦

chcode

2021-01-12 15:58

@JFinal 换一种思路 while (rs.next()) 不成立时再调用一次func.apply() 此时应该知道后续不会有数据了吧

JFinal

2021-01-12 16:06

@chcode 本末倒置了,满足了你这个需求, 原有的需求无法满足

chcode

2021-01-12 16:09

@JFinal 哈哈,明白了 这类需求还是适合自己进行扩展,不适合在框架层面去实现

zzutligang

2021-01-12 18:54

@JFinal,我跟着追问一个问题,Db.each方法,如果提供的sql语句可能会命中几千万行数据,不会因为命中数据太多,而导致内存占用过多,或则引起OOM吧。如果是流的方式,那我正好可以把这个用法用在把数据库表里的数据导入到ignite里。

JFinal

2021-01-12 20:03

@zzutligang 数据多不会一次性读取,而是分批次读取,批次大小取决于你 mysql 配置的缓冲区

当然,上面讲的偏向于 JDBC 与数据库这一端,如果你的代码这一端一次性读很多数据肯定是会 OOM 的

chcode

2021-01-14 10:19

@JFinal 我又补充了代码,麻烦您抽空看下是否可行?是否兼顾原有需求又解决了我的问题?