结合使用 JDBC 和 GUI API
samplesCoffeesFrame.java
演示了如何将 JDBC 与 GUI API(特别是 Swing API)集成在一起。它在表中显示COFFEES
数据库表的内容,并包含使您可以向表中添加行的字段和按钮。以下是此示例的屏幕截图:
该示例包含五个文本字段,它们与COFFEES
表中的每一列相对应。它还包含三个按钮:
-
在表格中添加行 :根据在文本字段中 Importing 的数据在 samples 的表格中添加一行。
-
更新数据库 :根据 samples 表中的数据更新表
COFFEES
。 -
放弃更改 :检索
COFFEES
表的内容,替换 samples 表中的现有数据。
此 samples(需要CoffeesTableModel
)演示了将 JDBC 与 Swing API 集成的以下常规步骤:
Implementing javax.swing.event.TableModel
TableModel
interface使 Java Swing 应用程序可以 管理JTable
对象中的数据。samplesCoffeesTableModel.java实现了此interface。它指定JTable
对象应如何从RowSet
对象检索数据并将其显示在表中。
注意 :尽管此示例显示了 Swing 应用程序中COFFEES
表的内容,但只要其数据可以用String
对象表示,则CoffeesTableModel
类适用于任何 SQL 表。 (但是,对于其他 SQL 表,必须修改允许用户向CoffeesFrame
类中指定的COFFEES
中添加行的字段.)
在实现interfaceTableModel
的方法之前,类CoffeeTableModel
的构造函数将初始化这些实现方法所需的各种成员变量,如下所示:
public CoffeesTableModel(CachedRowSet rowSetArg)
throws SQLException {
this.coffeesRowSet = rowSetArg;
this.metadata = this.coffeesRowSet.getMetaData();
numcols = metadata.getColumnCount();
// Retrieve the number of rows.
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
interface实现了以下方法:
-
Class<?> getColumnClass(int 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
方法getColumnCount
和getRowCount
分别返回成员变量numcols
和numrows
的值:
public int getColumnCount() {
return numcols;
}
public int getRowCount() {
return numrows;
}
Implementing getColumnClass
getColumnClass
方法返回指定列的数据类型。为简单起见,此方法返回String
类,从而将表中的所有数据转换为String
对象。 JTable
类使用此方法来确定如何在 GUI 应用程序中呈现数据。
public Class getColumnClass(int column) {
return String.class;
}
Implementing getColumnName
getColumnName
方法返回指定列的名称。 JTable
类使用此方法标记其每一列。
public String getColumnName(int column) {
try {
return this.metadata.getColumnLabel(column + 1);
} catch (SQLException e) {
return e.toString();
}
}
Implementing getColumnAt
getColumnAt
方法检索行集coffeesRowSet
中指定行和列的值。 JTable
类使用此方法填充其表。请注意,SQL 从 1 开始对其行和列编号,但是TableModel
interface从 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();
}
}
Implementing isCellEditable
因为此示例不允许用户直接编辑表的内容(行由另一个窗口控件添加),所以无论rowIndex
和columnIndex
的值如何,此方法都将返回false
:
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
Implementing javax.sql.RowSetListener
CoffeesFrame
类仅实现RowSetListener
rowChanged
interface中的一种方法。当用户向表中添加一行时,将调用此方法。
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);
// Display the error in a dialog box.
JOptionPane.showMessageDialog(
CoffeesFrame.this,
new String[] {
// Display a 2-line message
ex.getClass().getName() + ": ",
ex.getMessage()
}
);
}
}
此方法更新 GUI 应用程序中的表。
布置摆幅组件
CoffeesFrame
类的构造函数初始化并布置 Swing 组件。以下语句检索COFFEES
表的内容,将内容存储在CachedRowSet
对象myCachedRowSet
中,并初始化JTable
Swing 组件:
CachedRowSet myCachedRowSet = getContentsOfCoffeesTable();
myCoffeesTableModel = new CoffeesTableModel(myCachedRowSet);
myCoffeesTableModel.addEventHandlersToRowSet(this);
// Displays the table
table = new JTable();
table.setModel(myCoffeesTableModel);
如前所述,此示例使用RowSet
对象(尤其是CachedRowSet
对象)代替了ResultSet
对象来表示COFFEES
表的内容。
方法CoffeesFrame.getContentsOfCoffeesTable
检索表COFFEES
的内容。
方法CoffeesTableModel.addEventHandlersToRowSet
将在CoffeesFrame
类中定义的事件处理程序(方法rowChanged
)添加到行集成员变量CoffeesTableModel.coffeesRowSet
。这使类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
的更多信息,请参见使用 JFC/Swing 创建 GUI中的如何使用 GridBagLayout。
请参阅CoffeesFrame.java
的源代码,以了解如何将此示例的 Swing 组件添加到布局GridBagLayout
。
为按钮添加侦听器
以下语句将一个侦听器添加到“向表添加行”按钮:
button_ADD_ROW.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(
CoffeesFrame.this, new String[] {
"Adding the following row:",
"Coffee name: [" +
textField_COF_NAME.getText() +
"]",
"Supplier ID: [" +
textField_SUP_ID.getText() + "]",
"Price: [" +
textField_PRICE.getText() + "]",
"Sales: [" +
textField_SALES.getText() + "]",
"Total: [" +
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("Updated database");
} catch (SQLException sqle) {
displaySQLExceptionDialog(sqle);
// Now revert back changes
try {
createNewTableModel();
msgline.setText("Discarded changes");
} 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
组件。