优化 JDBC 中对 JTable 的数据调用

Posted

技术标签:

【中文标题】优化 JDBC 中对 JTable 的数据调用【英文标题】:Optimize data calling in JDBC onto JTable 【发布时间】:2015-01-18 21:02:51 【问题描述】:

目前我在 mysql 服务器中有数据,我正在通过 JDBC 将数据调用到 JTable 上。但是有 1369 行,并且它似乎有太多数据无法加载。加载通常需要 5 分钟。有什么办法可以优化流程吗?这是我的代码(我提前为混乱的代码道歉):

public class DataTable 
private String databaseName = "*****";
private String tableName = "******";
public void showDatabase()
    Connection conn = null;
    DatabaseMetaData meta = null;
    Statement stmt = null;
    ResultSet rs = null;
    int k = 0;
    try
        Class.forName("com.mysql.jdbc.Driver").newInstance();
        String connectionUrl = "jdbc:mysql://localhost:3306/" + databaseName;
        String connectionUser = "*****";
        String connectionPassword = "*****";
        conn = DriverManager.getConnection(connectionUrl, connectionUser, connectionPassword);
        stmt = conn.createStatement();
        meta = conn.getMetaData();
        dataSets(stmt, meta);


    catch(Exception e)
        e.printStackTrace();
     finally
        try  if (rs != null) rs.close();  catch (SQLException e)  e.printStackTrace(); 
        try  if (stmt != null) stmt.close();  catch (SQLException e)  e.printStackTrace(); 
        try  if (conn != null) conn.close();  catch (SQLException e)  e.printStackTrace(); 
    


//return the column size of the table
public int getColumnNumber(DatabaseMetaData meta, Statement stmt) throws SQLException

    //ResultSet rs = meta.getColumns(null, null, "practiceexample", null);
    ResultSet rs = stmt.executeQuery("SELECT * FROM " + tableName);
    ResultSetMetaData rsmd = rs.getMetaData();
    int columnsNumber = rsmd.getColumnCount();
    return columnsNumber;



//return the rowNumber of the tables
public int getRowNumber(Statement stmt) throws SQLException

    ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tableName);
    int rowCount = 0;
    while(rs.next())
        rowCount = rs.getInt(1);
    

    return rowCount;


public void dataSets(Statement stmt, DatabaseMetaData meta) throws SQLException

    String[] columnNames = new String[getColumnNumber(meta, stmt)];
    String[][] dataSets = new String[getRowNumber(stmt)][columnNames.length];


    ResultSet column = meta.getColumns(null, null, tableName, null);

    int i = 0;
    while(column.next())
    
        columnNames[i] = column.getString("COLUMN_NAME");
        //columnNames.add(i, column.getString("COLUMN_NAME"));
        i++;
    

    for(int j = 0; j < dataSets.length; j++)
    
        String[] singleRowData = new String[columnNames.length]; 
        ResultSet data = null;
        for(int k = 0; k < columnNames.length; k++)
        
            String columnName = columnNames[k];
            data = stmt.executeQuery("SELECT " + columnName + 
                    " FROM " + tableName + " LIMIT " + j + ", " + 1);
            while(data.next())
            
                singleRowData[k] = data.getString(columnName);
            

        

        dataSets[j] = singleRowData;
    


    SimpleTable table = new SimpleTable(columnNames, dataSets);
    javax.swing.SwingUtilities.invokeLater(new Runnable() 
        public void run() 
            table.createAndShowGUI();
        
    );


class SimpleTable
    String[] columns;
    String[][] dataSets;

    public SimpleTable(String[] columns, String[][] dataSets)
        this.columns = columns;
        this.dataSets = dataSets;
    

    public void createAndShowGUI()
        JPanel gui = new JPanel(new BorderLayout(3, 3));

        final JTable table = new JTable(new DefaultTableModel(dataSets, columns));
        final JScrollPane scrollPane = new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED
                , JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        Dimension dimension = table.getPreferredSize();
        scrollPane.setPreferredSize(new Dimension(dimension.width, table.getRowHeight() * 30));

        JPanel navigation = new JPanel(new FlowLayout(FlowLayout.CENTER));

        JButton next = new JButton(">");
        next.addActionListener(new ActionListener()
            public void actionPerformed(ActionEvent e)
            
                int height = table.getRowHeight() * (20-1);
                JScrollBar bar = scrollPane.getVerticalScrollBar();
                bar.setValue(bar.getValue() + height);
            
        );

        JButton previous = new JButton("<");
        previous.addActionListener( new ActionListener()
            public void actionPerformed(ActionEvent ae) 
                int height = table.getRowHeight()*(20-1);
                JScrollBar bar = scrollPane.getVerticalScrollBar();
                bar.setValue( bar.getValue()-height );
            
         );

        navigation.add(previous);
        navigation.add(next);

        gui.add(scrollPane, BorderLayout.CENTER);
        gui.add(navigation, BorderLayout.SOUTH);

        JOptionPane.showMessageDialog(null, gui);
    




【问题讨论】:

优化整个应用程序的一种方法是使用连接池,而不是手动创建物理数据库连接。这将极大地增强您的应用程序。 对不起,我还是新手,我不太确定连接池是什么。那是在 java 库中吗? 您可以使用SwingWorker 在后台加载数据并将更新推送到 UI。虽然它不会加快整个过程,但它会让你更早地呈现 UI,给人一种速度的错觉 这个example 可能会有所帮助。 感谢大家的帮助。我能弄明白! 【参考方案1】:

恕我直言,不良性能的根源是您不必要地多次查询数据库以获取所需的数据(列、行、行数、列数等):

获取列号:

ResultSet rs = stmt.executeQuery("SELECT * FROM " + tableName);

获取行号:

ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tableName);

获取行(这是最糟糕的,因为它在循环中):

data = stmt.executeQuery("SELECT " + columnName + " FROM " + tableName + " LIMIT " + j + ", " + 1);

如何解决

只需查询一次数据库。单个ResultSet 及其关联的ResultSetMetaData 应该足以实现您的目标。另外,正如已经建议的那样,使用SwingWorker 在单独的线程中进行数据库调用。例如:

final JTable table = new JTable();

SwingWorker<Void, TableModel> worker = new SwingWorker<Void, TableModel> () 

    @Override
    protected Void doInBackground() throws Exception 

        ResultSet resultSet = stmt.executeQuery("SELECT * FROM " + tableName);
        ResultSetMetaData metaData = resultSet.getMetaData();

        int columnCount = metaData.getColumnCount(); // columns number
        String[] columnNames = new String[columnCount];
        for (int i = 1; i <= columnCount; i++) 
            columnNames[i] = metaData.getColumnName(i); // fill columns names
        

        resultSet.last();
        int rowCount = resultSet.getRow(); // get rows number
        resultSet.beforeFirst();

        Object[][] data = new Object[rowCount][columnCount];
        int currentRow = 0;
        while (resultSet.next()) 
            for (int currentColumn = 1; currentColumn <= columnCount; currentColumn++) 
                data[currentRow][currentColumn - 1] = resultSet.getObject(currentColumn); // fill data set
             
             currentRow++;
        

        TableModel model = new DefaultTableModel(data, columnNames);
        publish(model);

        return null;
    

    @Override
    protected void process(List<TableModel> chunks) 
        TableModel model = chunks.get(0);
        table.setModel(model);
    


worker.execute();

【讨论】:

感谢您的回复。我已经尝试过了,它给了我一个错误,因为我需要返回 Void 类型的函数。我不知道为什么会这样。 其实我想通了!感谢您的帮助! 哦,我忘了return nulldoInBackground() 实现的末尾。这是因为 SwingWorker 被设计为高度可重用(通过使用泛型)并让实现者告诉 doInBackground() 方法返回的类型(根据合同它必须返回一些东西):这就是 V 的意思。此值可通过get() 方法在内部使用。 OTOH 参数 T 表示由publishprocess 方法管理的类。所以在这种情况下,您不必只返回 null,因为从未使用过 get()(很少使用)。 @Kito

以上是关于优化 JDBC 中对 JTable 的数据调用的主要内容,如果未能解决你的问题,请参考以下文章

将 JDBC 数据库中的数据检索到 Jtable 中

使用复杂的 TableCellRenderer 在 JTable 中格式化来自 JDBC 的数据

JT400 - IBMi 机器拒绝 JDBC 连接,但在 Windows 机器上工作

使用来自两个数据库表 JDBC 的详细信息填充 JTable

JDBC优化策略总结

关于使按钮在 Netbeans 的 GUI 编辑器中工作的 JDBC 到 JTable 输出查询