SwingWorker + JTable:如何更改代码以便没有错误?

时间:2015-02-22 12:35:31

标签: java mysql swing jtable swingworker

我使用此代码并获取错误 java.lang.ArrayIndexOutOfBoundsException 。我知道问题是多线程和EDT 。我读到了使用方法 publish(),但我不知道如何使用它,我不知道如何更改代码以纠正错误。请告诉我如何更改代码以避免任何错误。感谢你! 错误消息:

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 1 >= 1
    at java.util.Vector.elementAt(Unknown Source)
    at javax.swing.table.DefaultTableColumnModel.getColumn(Unknown Source)
    at javax.swing.plaf.basic.BasicTableHeaderUI.getHeaderHeight(Unknown Source)
    at javax.swing.plaf.basic.BasicTableHeaderUI.createHeaderSize(Unknown Source)
    at javax.swing.plaf.basic.BasicTableHeaderUI.getPreferredSize(Unknown Source)
    at javax.swing.JComponent.getPreferredSize(Unknown Source)
    at javax.swing.ViewportLayout.preferredLayoutSize(Unknown Source)
    at java.awt.Container.preferredSize(Unknown Source)
    at java.awt.Container.getPreferredSize(Unknown Source)
    at javax.swing.JComponent.getPreferredSize(Unknown Source)
    at javax.swing.ScrollPaneLayout.layoutContainer(Unknown Source)
    at java.awt.Container.layout(Unknown Source)
    at java.awt.Container.doLayout(Unknown Source)
    at java.awt.Container.validateTree(Unknown Source)
    at java.awt.Container.validate(Unknown Source)
    at javax.swing.RepaintManager$2.run(Unknown Source)
    at javax.swing.RepaintManager$2.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at javax.swing.RepaintManager.validateInvalidComponents(Unknown Source)
    at javax.swing.RepaintManager$ProcessingRunnable.run(Unknown Source)
    at java.awt.event.InvocationEvent.dispatch(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$200(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)

主要类别:

import java.sql.*; 
import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;  
public class DatabaseTable extends JFrame { 
    private  String 
    dsn = "jdbc:mysql://localhost:3306/TestDB", 
    uid = "root", 
    pwd = "root"; 

    private static int start = 0, count = 10;

    private JProgressBar progressBar = null;
    private JButton btnNewButton = null;
    private DatabaseTableModel dbm = null;
    private Statement st = null;

    Connection connections() throws ClassNotFoundException, SQLException{
        Connection conn = null; 
        Class.forName("com.mysql.jdbc.Driver"); 
        conn = DriverManager.getConnection(dsn, uid, pwd);          
        return conn;
    }

    public DatabaseTable(){
        dbm = new DatabaseTableModel(false); 
        JTable table = new JTable(dbm);
        try { 

            JFrame frame = new JFrame("Next record"); 
            frame.setSize(400, 300); 
            frame.getContentPane().add(new JScrollPane(table)); 

            btnNewButton = new JButton("Next!");
            btnNewButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent arg0) {
                        Task task = new Task();             
                        task.execute();
                    }
                });
                frame.getContentPane().add(btnNewButton, BorderLayout.NORTH);

                progressBar = new JProgressBar();
                frame.getContentPane().add(progressBar, BorderLayout.SOUTH);
                frame.show(); 


            } catch (Exception ex) { 
                System.out.println("DatabaseTable().Exception");
            } 
    }

    class Task extends SwingWorker<Void, Void> {     
        @Override
        public Void doInBackground() throws Exception {
            btnNewButton.setEnabled(false);
            progressBar.setIndeterminate(true);
            setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

            try {
                st = connections().createStatement();
                ResultSet rs = st.executeQuery("select * from testTable LIMIT "+start+","+count); 
                start += 10;
                dbm.setDataSource(rs); 
                rs.close(); 
                connections().close();
                }catch (ClassNotFoundException  e) {
                    // TODO Auto-generated catch block
                    System.out.println("Task.ClassNotFoundException");
                }catch ( SQLException e) {
                    System.out.println("Task.SQLException");
                }catch (Exception e) {
                    // TODO Auto-generated catch block
                    System.out.println("Task.Exception");
                } 
            return null;
        }
        @Override
        public void done(){
            progressBar.setIndeterminate(false);
            setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
            progressBar.setValue(100);
            btnNewButton.setEnabled(true);
        }

    }


    public static void main(String[] args) { 
        new DatabaseTable();
    }
}

Class TableModel:

import javax.swing.*; 
import javax.swing.table.*; 
import java.sql.*; 
import java.util.*; 
public class DatabaseTableModel extends AbstractTableModel {
    private ArrayList columnNames = new ArrayList(); 
    private ArrayList columnTypes = new ArrayList();
    private ArrayList data = new ArrayList();

    public DatabaseTableModel(boolean editable) { 
        this.editable = editable; 
    } 
    private boolean editable; 

    public int getRowCount() { 
    synchronized (data) { 
        return data.size(); 
    } 
    } 
    public int getColumnCount() { 
        return columnNames.size(); 
    } 
    public Class getColumnClass(int column) { 
        return (Class)columnTypes.get(column); 
    } 
    public String getColumnName(int column) { 
        return (String)columnNames.get(column); 
    } 
    public Object getValueAt(int row, int column) { 
    synchronized (data) { 
        return ((ArrayList)data.get(row)).get(column); 
    } 
    } 
    public boolean isEditable(int row, int column) { 
        return editable; 
    } 
    public void setValueAt(Object value, int row, int column){ 
        synchronized (data) { 
            ((ArrayList)data.get(row)).set(column, value); 
        } 
    } 
    public void setDataSource(ResultSet rs) throws Exception { 
        data.clear(); 
        columnNames.clear(); 
        columnTypes.clear(); 

        ResultSetMetaData rsmd = rs.getMetaData(); 
        int columnCount = rsmd.getColumnCount(); 
        for ( int i=0; i<columnCount; i++) { 
            columnNames.add(rsmd.getColumnName(i+1)); 
            Class type = Class.forName(rsmd.getColumnClassName(i+1)); 
            columnTypes.add(type); 
        } 
        fireTableStructureChanged(); 
        while ( rs.next() ) { 

            ArrayList row = new ArrayList(); 
            for ( int i=0; i<columnCount; i++) { 
                if (columnTypes.get(i) == String.class) 
                    row.add(rs.getString(i+1)); 
                else 
                    row.add(rs.getObject(i+1)); 
            } 
            synchronized (data) { 
                data.add(row); 
                fireTableRowsInserted(data.size()-1, data.size()-1); 
            } 
        } 
    }
}

2 个答案:

答案 0 :(得分:1)

不需要&#34;同步&#34; TableModel中的逻辑。当事件调度线程被添加到JTable时,应该更新TableModel。

所以我建议不要使用:

    dbm = new DatabaseTableModel(false); 
    JTable table = new JTable(dbm);

您在SwingWorker中创建TableModel。然后在&#34;完成()&#34;您可以使用的SwingWorker方法:

table.setModel( )

&#34;完成()&#34;中的代码方法在Event Dispatch Thread上执行,因此您可以安全地重置表的模型。

编辑:

使用以下代码创建一个新的TableModel:

DatabaseTableModel dbm = new DatabaseTableModel(false); 
dbm.setDataSource(rs); 

然后将TableModel传递给&#34; done()&#34;方法,以便您可以使用新的TableModel重置表。

阅读Simple Background Tasks上Swing教程中的部分,了解从&#34; doInBackground()&#34;传递数据的示例。 &#34; done()&#34;的方法方法使用&#34; return&#34;声明和&#34; get()&#34;方法

答案 1 :(得分:1)

看起来两个线程同时访问数据库表模型。每次DatabaseTableModel.setDataSource 调用方法,清除columnNamescolumnTypes字段。从堆栈跟踪中我得到的印象是 EDT正在布置表头并尝试获取列数据,而数据库表模型被另一个擦除 线程。

SwingWorker文档(http://docs.oracle.com/javase/8/docs/api/javax/swing/SwingWorker.html)中,一名工作人员 描述了一个线程,其中应用程序可以在不进行GUI相关活动的情况下完成耗时的工作。在你的 例如,doInBackground()方法可以查询数据库并以正确的格式获取数据。 done()方法可以 然后更新数据库表模型并通知所有更改的侦听器(JTable对象)。

您当前的doInBackground()方法执行与GUI相关的工作。你可以通过存储数据(和列信息)来改变这一点 在单独的字段中(在Task类中)。之前可以将前三行移动到btnNewButton动作侦听器 致电task.execute()

获取doInBackground()方法中的数据/列可能看起来有点像这个虚拟示例:

// Dummy data to test without a database.
if (columnNames.size() == 0) {
    columnNames = new ArrayList<>(Arrays.asList("A", "B", "C"));
    columnTypes = new ArrayList<>(Arrays.asList(String.class, String.class, String.class));
}
data = new ArrayList();
data.add(new ArrayList<>(Arrays.asList(data.size(), data.size() + 1, data.size() + 2)));

可以将此代码添加到done()方法的开头,以将数据/列传输到数据库表模型 并通知听众:

dbm.updateModel(columnNames, columnTypes, data);
System.out.println("Task.done - dbm.getRowCount() = " + dbm.getRowCount());
if (dbm.getRowCount() == 1)
    dbm.fireTableStructureChanged();
dbm.fireTableRowsInserted(dbm.getRowCount() - 1, dbm.getRowCount() - 1);

更新数据库表模型的方法如下所示:

public void updateModel(ArrayList columnNames, ArrayList columnTypes, ArrayList data) {
    if (columnNames != null && columnTypes != null) {
        this.columnNames = columnNames;
        this.columnTypes = columnTypes;
    }

    this.data = data;
    //this.data.addAll(data);
}

此Stack Overflow问题&amp;回答处理类似的问题: Java Swing: Jtable ArrayIndexOutOfBoundsException