Java SQL结果到InputStream

Cam*_*n S 10 java sql-server jtds

我需要一个Java函数,它返回SQL SELECT查询的结果,作为InputStream通过网络发送结果的另一个系统的参数.

但是,InputStream必须String使用自定义分隔符(即通常但不总是CSV).

虽然我可以轻松创建一个函数来检索结果,创建一个分隔符String,最后将其转换String为一个InputStream,但SQL结果通常会太大而无法在内存中处理.此外,在返回结果之前处理整个结果集将导致不必要的等待时间.

如何返回一个InputStream迭代SQL结果并发送从数据库返回的已处理(分隔)数据?

Yur*_*nyy 8

发布(未测试)代码片段,它应该为您提供基本的想法:

/**
 * Implementors of this interface should only convert current row to byte array and return it.
 * 
 * @author yura
 */
public interface RowToByteArrayConverter {
    byte[] rowToByteArray(ResultSet resultSet);
}

public class ResultSetAsInputStream extends InputStream {

    private final RowToByteArrayConverter converter;
    private final PreparedStatement statement;
    private final ResultSet resultSet;

    private byte[] buffer;
    private int position;

    public ResultSetAsInputStream(final RowToByteArrayConverter converter, final Connection connection, final String sql, final Object... parameters) throws SQLException {
        this.converter = converter;
        statement = createStatement(connection, sql, parameters);
        resultSet = statement.executeQuery();
    }

    private static PreparedStatement createStatement(final Connection connection, final String sql, final Object[] parameters) {
        // PreparedStatement should be created here from passed connection, sql and parameters
        return null;
    }

    @Override
    public int read() throws IOException {
        try {
            if(buffer == null) {
                // first call of read method
                if(!resultSet.next()) {
                    return -1; // no rows - empty input stream
                } else {
                    buffer = converter.rowToByteArray(resultSet);
                    position = 0;
                    return buffer[position++] & (0xff);
                }
            } else {
                // not first call of read method
                if(position < buffer.length) {
                    // buffer already has some data in, which hasn't been read yet - returning it
                    return buffer[position++] & (0xff);
                } else {
                    // all data from buffer was read - checking whether there is next row and re-filling buffer
                    if(!resultSet.next()) {
                        return -1; // the buffer was read to the end and there is no rows - end of input stream
                    } else {
                        // there is next row - converting it to byte array and re-filling buffer
                        buffer = converter.rowToByteArray(resultSet);
                        position = 0;
                        return buffer[position++] & (0xff);
                    }
                }
            }
        } catch(final SQLException ex) {
            throw new IOException(ex);
        }
    }



    @Override
    public void close() throws IOException {
        try {
            statement.close();
        } catch(final SQLException ex) {
            throw new IOException(ex);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是非常直接的实现,可以通过以下方式进行改进:

  • 可以删除read方法中if和else之间的代码重复 - 它只是为了澄清而发布
  • 而不是为每一行重新创建字节数组缓冲区(new byte[]成本高昂的操作),可以实现更复杂的逻辑来使用字节数组缓冲区,该缓冲区仅初始化一次然后重新填充.然后应该更改RowToByteArrayConverter.rowToByteArray方法的签名,int fillByteArrayFromRow(ResultSet rs, byte[] array)该签名应返回填充的字节数并填充传递的字节数组.

因为字节数组包含有符号字节,所以它可以包含-1(实际上255是无符号字节)并因此指示不正确的流结束,因此& (0xff)用于将有符号字节转换为无符号字节作为整数值.有关详细信息,请参阅Java如何将int转换为字节?.

还请注意,如果网络传输速度很慢,这可能会长时间保持打开结果集,从而给数据库带来问题.

希望这可以帮助 ...