【问题标题】:Retrieve entire row with ResultSet使用 ResultSet 检索整行
【发布时间】:2013-05-28 18:59:20
【问题描述】:

是否可以在不为每一列调用 getInt(..) getString(..) 的情况下检索整行?

我有多个线程,每个线程都需要将结果写入某个线程安全集合。
我希望能够将行直接写入此集合,然后解析此集合的成员并根据列类型检索值。

【问题讨论】:

  • 没有。您需要将该行解析为Objects 并将其存储在Collection 中。考虑在线程中以块的形式查询数据。
  • 如果你不想写这样的代码,看看例如Spring JDBC 模板。它使您摆脱了许多这样的样板代码,而没有 ORM 的负担。
  • 你想要一个 ORM。 stackoverflow.com/questions/tagged/… 或 google for java orm

标签: java jdbc resultset


【解决方案1】:

这是一个使用查询来计算查询大小然后将处理拆分到多个线程的示例。

我使用 MySQL,所以查询是为此编写的。您需要更改数据库引擎的查询。

public static void main(String[] args) throws Exception {
    final int count;
    try (final Connection conn = DATA_SOURCE.getConnection()) {
        final String countQuery = "SELECT COUNT(*) FROM my_table";
        try (final PreparedStatement ps = conn.prepareStatement(countQuery);
                final ResultSet resultSet = ps.executeQuery()) {
            resultSet.next();
            count = resultSet.getInt(1);
        }
    }
    final int chunksize = 1000;
    final Queue<SqlResult> results = new ConcurrentLinkedQueue<>();
    final ExecutorService es = Executors.newFixedThreadPool(10);
    for (int end = 0; end < count; end += chunksize) {
        es.execute(new ResultReader(count, end, DATA_SOURCE, results));
    }
}

private static class ResultReader implements Runnable {

    private final int start;
    private final int size;
    private final DataSource dataSource;
    private final Queue<SqlResult> results;

    public ResultReader(int start, int size, DataSource dataSource, Queue<SqlResult> results) {
        this.start = start;
        this.size = size;
        this.dataSource = dataSource;
        this.results = results;
    }

    @Override
    public void run() {
        try (final Connection connection = dataSource.getConnection()) {
            final String query = "SELECT id, something, somethingElse FROM my_table LIMIT ?, ?";
            try (final PreparedStatement ps = connection.prepareStatement(query)) {
                ps.setInt(1, start);
                ps.setInt(2, size);
                try (final ResultSet rs = ps.executeQuery()) {
                    while (rs.next()) {
                        final SqlResult sqlResult = new SqlResult();
                        sqlResult.setId(rs.getInt("id"));
                        sqlResult.setSomething(rs.getString("something"));
                        sqlResult.setSomethingElse(rs.getString("somethingElse"));
                        results.add(sqlResult);
                    }
                }
            }
        } catch (SQLException ex) {
            Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

private static class SqlResult {

    private int id;
    private String something;
    private String somethingElse;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getSomething() {
        return something;
    }

    public void setSomething(String something) {
        this.something = something;
    }

    public String getSomethingElse() {
        return somethingElse;
    }

    public void setSomethingElse(String somethingElse) {
        this.somethingElse = somethingElse;
    }
}

这假设您已经使用DataSource 进行连接池。

每个工作人员使用LIMIT 运行查询并将结果处理为SqlResult 对象并将它们添加到并发Queue。

【讨论】:

  • LIMIT ?, ? 不是 MySQL 保留的密钥吗? AFAIK 它不适用于 Oracle 或 SQL Server。
  • @LuiggiMendoza 公平点。我不知道这样做的通用方法。我会修改我的答案,提到它是 MySQL 特定的。
  • 谢谢,但我的“问题”是 resultSet.getInt(1) 我想避免这种情况。
  • 附注我没有使用连接池(不认为我需要它)我正在为 4 个 theeads 创建 4 个连接。
【解决方案2】:

你可以建立一个像这样的类,它将sql数据类型映射到java数据类型:

class Row
{
    public Map <Object,Class> row;
    public static Map <String, Class> TYPE;

    static
    {
        TYPE = new HashMap<String, Class>();

        TYPE.put("INTEGER", Integer.class);
        TYPE.put("TINYINT", Byte.class);
        TYPE.put("SMALLINT", Short.class);
        TYPE.put("BIGINT", Long.class);
        TYPE.put("REAL", Float.class);
        TYPE.put("FLOAT", Double.class);
        TYPE.put("DOUBLE", Double.class);
        TYPE.put("DECIMAL", BigDecimal.class);
        TYPE.put("NUMERIC", BigDecimal.class);
        TYPE.put("BOOLEAN", Boolean.class);
        TYPE.put("CHAR", String.class);
        TYPE.put("VARCHAR", String.class);
        TYPE.put("LONGVARCHAR", String.class);
        TYPE.put("DATE", Date.class);
        TYPE.put("TIME", Time.class);
        TYPE.put("TIMESTAMP", Timestamp.class);
        // ...
    }
    
    public Row ()
    {
        row = new HashMap<Object,Class>();
    }
    
    public <T> void add (T data)
    {
        row.put(data, data.getClass());
    }

    public void add (Object data, String sqlType)
    {
        add((Row.TYPE.get(sqlType)) data);
    }

    public static void formTable (ResultSet rs, ArrayList<Row> table)
    {
        if (rs == null) return;
    
        ResultSetMetaData rsmd = rs.getMetaData();
    
        int NumOfCol = rsmd.getColumnCount();
    
        while (rs.next())
        {
            row = new Row ();
        
            for(int i = 1; i <= NumOfCol; i++)
            {
                row.add(rs.getObject(i), rsmd.getColumnTypeName(i));
            }

            table.add(row);
        }
    }
}

你可以这样使用它:

List<Row> table = new ArrayList<Row>();

Row row = null;

ResultSet rs = st.executeQuery("SELECT * FROM table_name");

Row.formTable(rs, table);

然后您可以检索字段并将它们转换为各自的数据类型:

for (Row row : table)
{

    for (Object data : row.row.getKeySet())
    {
        System.out.print(" > " + ((row.row.get(data) data));
    }
    
    System.out.println();

}

【讨论】:

  • 谢谢,好代码。一条评论:我认为单行不应表示为 Map,而应表示为 List,因为列在表中是有序的。因此,我会将 Row 的行更改为: public List> row;这里没有足够的空间,所以我将代码粘贴到下面的新条目中。
【解决方案3】:

行表示为列表:

import java.math.BigDecimal;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author Adam Dziedzic
 * 
 */
public class Row {
    public List<Entry<Object, Class>> row;
    public static Map<String, Class> TYPE;

    static {
        TYPE = new HashMap<String, Class>();

        TYPE.put("INTEGER", Integer.class);
        TYPE.put("TINYINT", Byte.class);
        TYPE.put("SMALLINT", Short.class);
        TYPE.put("BIGINT", Long.class);
        TYPE.put("REAL", Float.class);
        TYPE.put("FLOAT", Double.class);
        TYPE.put("DOUBLE", Double.class);
        TYPE.put("DECIMAL", BigDecimal.class);
        TYPE.put("NUMERIC", BigDecimal.class);
        TYPE.put("BOOLEAN", Boolean.class);
        TYPE.put("CHAR", String.class);
        TYPE.put("VARCHAR", String.class);
        TYPE.put("LONGVARCHAR", String.class);
        TYPE.put("DATE", Date.class);
        TYPE.put("TIME", Time.class);
        TYPE.put("TIMESTAMP", Timestamp.class);
        TYPE.put("SERIAL",Integer.class);
        // ...
    }

    public Row() {
        row = new ArrayList<Entry<Object, Class>>();
    }

    public <T> void add(T data) {
        row.add(new AbstractMap.SimpleImmutableEntry<Object,Class>(data, data.getClass()));
    }

    public void add(Object data, String sqlType) {
        Class castType = Row.TYPE.get(sqlType.toUpperCase());
        try {
            this.add(castType.cast(data));
        } catch (NullPointerException e) {
            e.printStackTrace();
            Logger lgr = Logger.getLogger(Row.class.getName());
            lgr.log(Level.SEVERE, e.getMessage()+" Add the type "+sqlType+" to the TYPE hash map in the Row class.", e);
            throw e;
        }
    }

    public static void formTable(ResultSet rs, List<Row> table)
            throws SQLException {
        if (rs == null)
            return;

        ResultSetMetaData rsmd;
        try {
            rsmd = rs.getMetaData();

            int NumOfCol = rsmd.getColumnCount();

            while (rs.next()) {
                Row current_row = new Row();

                for (int i = 1; i <= NumOfCol; i++) {
                    current_row.add(rs.getObject(i), rsmd.getColumnTypeName(i));
                }

                table.add(current_row);
            }
        } catch (SQLException e) {
            throw e;
        }
    }
}

用法:

List<Row> table = new ArrayList<Row>();

ResultSet rs = st.executeQuery("SELECT * FROM table_name");

Row.formTable(rs, table);

for (Row row : table)
{
    for (Entry<Object, Class> col: row.row)
    {
        System.out.print(" > " + ((col.getValue()).cast(col.getKey())));
    }
    System.out.println();
}

【讨论】:

  • TYPE.put("INTEGER", Integer.class); 可以改为 TYPE.put("INT", Integer.class);我>
  • TYPE.put("INTEGER", Integer.class); 可以改为 TYPE.put("INT", Integer.class);我>
  • OP 专门要求提供不遍历列的解决方案
  • 最好是捕获 SQL 异常并记录它,而不是仅仅捕获异常并将其扔回给调用者。
  • 跳过这个 try/catch 块可能会更好。
【解决方案4】:

我使用了 adam.cajf 答案,非常有用,但是我需要添加一行代码来添加函数,否则它会在我的特定数据集中引发错误。

if (data != null) {

【讨论】:

  • 您可以通过为引用的答案添加相关代码来改进您的答案,或者只是将您的反馈作为对答案本身的评论发布。
【解决方案5】:

没有。

根据 Java 文档,没有一种方法可以让您在不指定列标签或索引的情况下检索结果。

https://docs.oracle.com/javase/7/docs/api/java/sql/ResultSet.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-16
    • 2013-10-30
    • 2015-07-15
    • 1970-01-01
    • 2015-09-13
    • 1970-01-01
    • 2018-11-29
    相关资源
    最近更新 更多