2013-06-02 56 views
9

是否有可能檢索整行而不用爲每列調用getInt(..)getString(..)?用ResultSet檢索整行

我有多個線程,每個花紋需要將結果寫入一些線程安全收集。
我希望能夠將行直接寫入此集合,然後解析此集合的成員並根據列類型檢索值。

+2

不需要。您需要將行解析爲'Objects'並將它們存儲在'Collection'中。考慮在線程中查找塊中的數據。 –

+1

如果你不想寫這樣的代碼,看看例如Spring JDBC模板。它可以減輕許多此類鍋爐板代碼的負擔,而不會受到ORM的負擔。 –

+0

你想要一個ORM。 http://stackoverflow.com/questions/tagged/java+orm?sort=frequent&pagesize=50或者谷歌for java orm –

回答

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

+0

是不是'LIMIT?,?'一個MySQL保留的keywork? AFAIK它不適用於Oracle和SQL Server。 –

+0

@LuiggiMendoza公平點。我不知道通用的方法來做到這一點。我會修改我的答案,提到它是MySQL特定的。 –

+0

謝謝,但我的「問題」與resultSet.getInt(1)我想避免這一點。 – yuris

4

你可以建立一個類像這樣的,它的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 void add<t> (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(); 

} 
+0

謝謝,很好的一段代碼。一個評論:我認爲單個行不應該被表示爲一個Map,而應該是一個List,因爲列是在一個表中排序的。因此,我會將行的行更改爲這樣的:public List > row;這裏沒有足夠的空間,所以我在下面的一個新條目中粘貼了我的代碼。 – ady

2

以列表形式表示的行:

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(); 
} 
0

我用adam.cajf答案,是非常有用的,但是,我需要添加的代碼來添加功能一行,否則就被扔在我的特定數據錯誤組。

if (data != null) { 
+1

您可以通過爲參考答案添加相關代碼來改善您的答案,或只是將您的反饋發佈爲答案本身的評論。 –