Java教程是为JDK 8编写的。本页面中描述的示例和实践不利用后续版本中引入的改进,可能使用已不再可用的技术。
请参阅Java语言更改以了解Java SE 9及其后续版本中更新的语言特性的摘要。
请参阅JDK发行说明以获取有关所有JDK发行版的新功能、增强功能和已删除或已弃用选项的信息。
示例CoffeesFrame.java
演示了如何将JDBC与GUI API(特别是Swing API)集成。它在表格中显示了COFFEES
数据库表的内容,并包含了字段和按钮,使您能够向表格中添加行。以下是这个示例的屏幕截图:
示例包含五个文本字段,对应于COFFEES
表中的每个列。它还包含三个按钮:
COFFEES
。COFFEES
表的内容,替换示例表格中的现有数据。这个示例(需要CoffeesTableModel.java
)演示了将JDBC与Swing API集成的一般步骤:
TableModel
接口使Java Swing应用程序能够在JTable
对象中管理数据。示例CoffeesTableModel.java
实现了这个接口。它指定了JTable
对象如何从RowSet
对象中检索数据并在表格中显示。
注意:尽管这个示例在Swing应用程序中显示了COFFEES
表的内容,但CoffeesTableModel
类应适用于任何SQL表,只要它的数据可以用String
对象表示。(然而,用于向COFFEES
添加行的字段(在CoffeesFrame
类中指定)对于其他SQL表需要进行修改。)
在实现TableModel
接口的方法之前,CoffeeTableModel
类的构造函数如下所示初始化了一些必要的成员变量:
public CoffeesTableModel(CachedRowSet rowSetArg) throws SQLException { this.coffeesRowSet = rowSetArg; this.metadata = this.coffeesRowSet.getMetaData(); numcols = metadata.getColumnCount(); // 获取行数 this.coffeesRowSet.beforeFirst(); this.numrows = 0; while (this.coffeesRowSet.next()) { this.numrows++; } this.coffeesRowSet.beforeFirst(); }
以下是在这个构造函数中初始化的成员变量的描述:
CachedRowSet coffeesRowSet
:存储表COFFEES
的内容。
这个示例使用一个RowSet
对象,特别是一个CachedRowSet
对象,而不是一个ResultSet
对象,有两个原因。首先,CachedRowSet
对象允许应用程序的用户在不连接到数据库的情况下对其中的数据进行更改。此外,由于CachedRowSet
对象是一个JavaBeans组件,它可以在发生某些事件时通知其他组件。在这个示例中,当向CachedRowSet
对象添加新行时,它会通知渲染数据到表格中的Swing组件刷新自身并显示新行。
ResultSetMetaData metadata
:检索表COFFEES
中的列数以及每列的名称。
int numcols, numrows
:分别存储表COFFEES
中的列数和行数。
CoffeesTableModel.java
示例实现了TableModel
接口中的以下方法:
Class<?> getColumnClass(int columnIndex)
:返回列columnIndex
中所有单元格值的最具体的超类。int getColumnCount()
:返回模型中的列数。String getColumnName(int columnIndex)
:返回参数columnIndex
指定的列的名称。int getRowCount()
:返回模型中的行数。Object getValueAt(int rowIndex, int columnIndex)
:返回列columnIndex
和行rowIndex
的交汇处单元格的值。boolean isCellEditable(int rowIndex, int columnIndex)
:如果列rowIndex
和行columnIndex
的交汇处单元格可以编辑,则返回true。以下方法未实现,因为此示例不允许用户直接编辑表的内容:
void addTableModelListener(TableModelListener l)
:向列表中添加一个监听器,每当数据模型发生变化时通知该监听器。void removeTableModelListener(TableModelListener l)
:从列表中移除一个监听器,每当数据模型发生变化时通知该监听器。void setValueAt(Object aValue, int rowIndex, int columnIndex)
:将列索引为columnIndex
、行索引为rowIndex
的单元格的值设置为aValue
对象。getColumnCount
和getRowCount
方法分别返回成员变量numcols
和numrows
的值:
public int getColumnCount() { return numcols; } public int getRowCount() { return numrows; }
getColumnClass
方法返回指定列的数据类型。为了简单起见,该方法返回String
类,从而将表中的所有数据转换为String
对象。JTable
类使用该方法确定如何在GUI应用程序中渲染数据。
public Class getColumnClass(int column) { return String.class; }
getColumnName
方法返回指定列的名称。JTable
类使用该方法为每个列添加标签。
public String getColumnName(int column) { try { return this.metadata.getColumnLabel(column + 1); } catch (SQLException e) { return e.toString(); } }
getColumnAt
方法从行集coffeesRowSet
中检索指定行和列的值。JTable
类使用该方法填充其表格。请注意,SQL从1开始对行和列进行编号,但TableModel
接口从0开始;这就是为什么rowIndex
和columnIndex
的值要加1的原因。
public Object getValueAt(int rowIndex, int columnIndex) { try { this.coffeesRowSet.absolute(rowIndex + 1); Object o = this.coffeesRowSet.getObject(columnIndex + 1); if (o == null) return null; else return o.toString(); } catch (SQLException e) { return e.toString(); } }
因为此示例不允许用户直接编辑表格的内容(行是通过另一个窗口控件添加的),所以无论rowIndex
和columnIndex
的值如何,该方法都返回false
:
public boolean isCellEditable(int rowIndex, int columnIndex) { return false; }
类 CoffeesFrame
只实现了接口 RowSetListener
中的一个方法,即 rowChanged
。当用户向表中添加行时,将调用此方法。
public void rowChanged(RowSetEvent event) { CachedRowSet currentRowSet = this.myCoffeesTableModel.coffeesRowSet; try { currentRowSet.moveToCurrentRow(); myCoffeesTableModel = new CoffeesTableModel( myCoffeesTableModel.getCoffeesRowSet()); table.setModel(myCoffeesTableModel); } catch (SQLException ex) { JDBCTutorialUtilities.printSQLException(ex); // 在对话框中显示错误。 JOptionPane.showMessageDialog( CoffeesFrame.this, new String[] { // 显示两行消息 ex.getClass().getName() + ": ", ex.getMessage() } ); } }
此方法更新GUI应用程序中的表。
类 CoffeesFrame
的构造函数初始化并布置了 Swing 组件。以下语句检索 COFFEES
表的内容,将内容存储在 CachedRowSet
对象 myCachedRowSet
中,并初始化了 JTable
Swing 组件:
CachedRowSet myCachedRowSet = getContentsOfCoffeesTable(); myCoffeesTableModel = new CoffeesTableModel(myCachedRowSet); myCoffeesTableModel.addEventHandlersToRowSet(this); // 显示表 table = new JTable(); table.setModel(myCoffeesTableModel);
正如之前提到的,此示例中使用的是 RowSet
对象(特别是 CachedRowSet
对象)来代表 COFFEES
表的内容,而不是 ResultSet
对象。
方法 CoffeesFrame.getContentsOfCoffeesTable
检索表 COFFEES
的内容。
方法 CoffeesTableModel.addEventHandlersToRowSet
向行集成员变量 CoffeesTableModel.coffeesRowSet
添加在 CoffeesFrame
类中定义的事件处理程序,即 rowChanged
方法。这使得类 CoffeesFrame
能够通知行集 coffeesRowSet
有关任何事件,特别是当用户点击“添加行到表格”、“更新数据库”或“放弃更改”按钮时。当行集 coffeesRowSet
收到这些更改通知时,将调用方法 CoffeesFrame.rowChanged
。
语句 table.setModel(myCoffeesTableModel)
指定使用 CoffeesTableModel
对象 myCoffeesTableModel
来填充 JTable
Swing 组件 table
。
以下语句指定 CoffeesFrame
类使用布局 GridBagLayout
来布置其 Swing 组件:
Container contentPane = getContentPane(); contentPane.setComponentOrientation( ComponentOrientation.LEFT_TO_RIGHT); contentPane.setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints();
详细了解如何使用布局 GridBagLayout
的信息,请参见 如何使用 GridBagLayout 在 使用 JFC/Swing 创建 GUI 中。
查看源代码 CoffeesFrame.java
以查看如何将此示例的 Swing 组件添加到布局 GridBagLayout
中。
以下语句为按钮 Add row to table 添加监听器:
button_ADD_ROW.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog( CoffeesFrame.this, new String[] { "正在添加以下行:", "咖啡名称:[" + textField_COF_NAME.getText() + "]", "供应商 ID:[" + textField_SUP_ID.getText() + "]", "价格:[" + textField_PRICE.getText() + "]", "销售量:[" + textField_SALES.getText() + "]", "总量:[" + textField_TOTAL.getText() + "]" } ); try { myCoffeesTableModel.insertRow( textField_COF_NAME.getText(), Integer.parseInt(textField_SUP_ID.getText().trim()), Float.parseFloat(textField_PRICE.getText().trim()), Integer.parseInt(textField_SALES.getText().trim()), Integer.parseInt(textField_TOTAL.getText().trim()) ); } catch (SQLException sqle) { displaySQLExceptionDialog(sqle); } } });
当用户点击此按钮时,它执行以下操作:
CoffeesTableModel.insertRow
,将行添加到成员变量 CoffeesTableModel.coffeesRowSet
中。如果抛出SQLException
,则方法CoffeesFrame.displaySQLExceptionDialog
创建一个消息对话框,显示SQLException
的内容。
以下语句将监听器添加到按钮更新数据库:
button_UPDATE_DATABASE.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { try { myCoffeesTableModel.coffeesRowSet.acceptChanges(); msgline.setText("已更新数据库"); } catch (SQLException sqle) { displaySQLExceptionDialog(sqle); // 现在撤销更改 try { createNewTableModel(); msgline.setText("已放弃更改"); } catch (SQLException sqle2) { displaySQLExceptionDialog(sqle2); } } } } );
当用户点击此按钮时,表COFFEES
将使用myCoffeesTableModel.coffeesRowSet
的内容进行更新。
以下语句将监听器添加到按钮放弃更改:
button_DISCARD_CHANGES.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { createNewTableModel(); } catch (SQLException sqle) { displaySQLExceptionDialog(sqle); } } });
当用户点击此按钮时,将调用方法CoffeesFrame.createNewTableModel
,该方法将用COFFEES
表的内容重新填充JTable
组件。