六、添加新增记录对话框
如图所示,本节内容包括以下内容:
- 创建对话框以编辑主窗体中的每个表的数据
- 创建中间 Bean 以便在对话框和窗体之间传输数据
- 将对话框中的字段绑定到中间 Bean
- 为对话框中的按钮创建事件处理程序
1、创建产品对话框
右键单击包含类的包databaseapplication,然后选择“新建”-->“其他”;选择“Swing
GUI 窗体”-->“JDialog 窗体”模板,并将其命名为
ProductEditor。编辑对话框设计如下图所示:
选择整个 JDialog 窗体,在“属性”窗口中,将“标题”属性更改为 Product
Editor。
2、绑定产品对话框字段
现在,需要将各种字段绑定到表中的相应列。因为无法在“绑定”对话框中直接绑定到其他窗体中的组件,因此,必须创建一个 Products
类型的中间 Bean 属性以保存记录。在用户单击 "New" 按钮时,将会为该 Bean
属性分配空记录值,用户可以在对话框中对其进行修改。在用户单击对话框中的 "保存" 时,该 Bean
属性中的值将传回到主窗体和数据库。
生成中间 Bean
属性:
在
ProductEditor窗体类代码的某个位置,如构造函数下面的一行,按 Alt-Insert
组合键(或单击鼠标右键并选择“插入代码”,然后选择“添加属性”。在“添加属性”对话框中,将该属性命名为
currentRecord,将其类型指定为 Products,选择“生成 getter 和
setter”,然后选择“生成属性更改支持”。单击“确定”以生成该属性。 如下图所示:
现在,您需要定制生成的
setCurrentRecord 方法以触发属性更改通知:
触发属性更改通知:
public void
setCurrentRecord(Products currentRecord) {
Products oldRecord = this.currentRecord;
this.currentRecord = currentRecord;
propertyChangeSupport.firePropertyChange("currentRecord",
oldRecord, currentRecord);
}
现在,需要添加代码,以便在用户单击 "New" 按钮时打开 "Product Editor" 对话框。
添加代码以从主视图中打开对话框:打开 DataBaseView 类并选择“源”视图。导航至 newRecord()
方法,在该方法底部粘贴以下代码:
JFrame mainFrame =
DataBaseApplication.getApplication().getMainFrame();
ProductEditor pe = new ProductEditor(mainFrame,
false);
pe.setCurrentRecord(p);
pe.setVisible(true);
绑定Product对话框的字段:
在“设计”视图中打开
ProductEditor 类。
选择 PROD_TYPE
的文本字段,在“属性”窗口中,选择“绑定”类别,选择 text 属性,然后单击该属性旁边的省略号
(...) 按钮。在“绑定”对话框中,从“绑定源”中选择 "Form",然后从“绑定表达式”中选择 "currentRecord"
--> "prodType";
选择BRAND的文本字段,在“属性”窗口中,选择“绑定”类别,选择 text
属性,类似地在“绑定”对话框中,从“绑定源”中选择 "Form",然后从“绑定表达式”中选择 "currentRecord"
--> "brand";
选择MODEL的文本字段,在“属性”窗口中,选择“绑定”类别,选择 text
属性,类似地在“绑定”对话框中,从“绑定源”中选择 "Form",然后从“绑定表达式”中选择 "currentRecord"
--> "model";
选择PRICE的文本字段,在“属性”窗口中,选择“绑定”类别,选择 text
属性,类似地在“绑定”对话框中,从“绑定源”中选择 "Form",然后从“绑定表达式”中选择 "currentRecord"
--> "price"。
3、xx对话框中的
"保存" 和 "取消" 按钮
现在,需要完成为对话框和主窗体之间关联编写代码的工作。
首先,将对话框中的按钮与相应的事件处理代码关联。目前已经具有随框架应用程序提供的 save() 和 refresh()
操作。可以为该对话框编写代码,以使这些按钮重用这些操作。可通过在对话框中设置一个布尔属性来完成此操作:在按 "保存"
按钮时,该属性返回 true;在选择 "取消" 时,该属性返回 false。根据关闭对话框时返回的值,将从 DataBaseView
类中运行 save() 或 refresh() 操作。
要设置该属性,请执行以下操作:
打开
ProductEditor 文件并选择“源”视图;将光标放在 propertyChangeSupport 字段的上方,按组合键
Alt-Insert ,然后选择“添加属性”;在“添加属性”对话框中,键入 productConfirmed
作为属性名称。
将类型设置为布尔类型,确保选中了“生成 getter 和 setter”复选框单击“确定”关闭该对话框并生成代码。
然后在这些按钮的事件处理代码中设置该属性的值。
创建事件侦听程序和处理程序:
切换到
ProductEditor 类的“设计”视图,在 ProductEditor 窗体中选择 "Save"
按钮,在“属性”窗口中,单击“事件”按钮,单击 actionPerformed 属性旁边的省略号 (...)
按钮。在“actionPerformed 的处理程序”对话框中,添加一个名为 saveProduct 的处理程序。
在源代码编辑器中的
saveProduct 方法内(创建新处理程序后光标跳转到的位置),键入以下代码:
setProductConfirmed(true);
setVisible(false);
对于 "Cancel"
按钮,重复以上步骤,并将其处理程序命名为 cancelProduct。在 cancelProduct 方法中,键入以下内容:
setProductConfirmed(false);
setVisible(false);
在
DataBaseView 类中,导航至 newRecord() 方法,并在该方法底部添加以下代码:
if (pe.isProductConfirmed()) {
save().run();
}
else {
refresh().run();
}
由于 save() 和
refresh()
操作应用于在应用程序会话期间进行的任何更改,因此,应该将对话框设置为模态对话框,并将主窗体中的表设置为不可编辑。将对话框设置为模态对话框的另一个原因是:在用户按
"保存" 或 "取消" 按钮时,setVisible() 方法在运行事件处理程序(它包括 setProductConfirmed
方法)后才会返回内容。另外,如果不把对话框设置为模态对话框,可能还会出现其它错误(如不能新增与保存记录等)。
将对话框设置为模态对话框:
打开
ProductEditor 类的“设计”视图,选择该对话框,在“属性”窗口中,单击“属性”,然后选中 modal
属性的复选框。
这时运行项目,产品添加功能已可以正常使用,但在显示Countries数据表时,点击new按钮,仍然是添加产品。实际上,应该在new按钮的点击方法newRecord()中判断一下当前正在显示和编辑那个表,并把代码放在判断语句块中:
if(tableName
== "Products"){
databaseapplication.Products p = new
databaseapplication.Products();
......
if (pe.isProductConfirmed()) {
save().run();
}
......
}
七、添加Countries表新增记录对话框,并修改相关代码
按照新增Products记录对话框的实现方法,添加Countries表新增记录对话框,将其命名为CountryEditor,如图所示:
同样在对话框中添加Bean属性,名称为currentRecord,类型为Countries(注意要选择“生成属性更改支持”)。
同样,您需要定制生成的
setCurrentRecord 方法以触发属性更改通知:
触发属性更改通知:
public void
setCurrentRecord(Countries currentRecord) {
Countries oldRecord = this.currentRecord;
this.currentRecord = currentRecord;
propertyChangeSupport.firePropertyChange("currentRecord",
oldRecord, currentRecord);
}
在“设计”视图中打开
CountryEditor 类。
选择国家名文本字段,在“属性”窗口中,选择“绑定”类别,选择 text
属性,然后单击该属性旁边的省略号 (...) 按钮。在“绑定”对话框中,“绑定源”选择 "Form",“绑定表达式”选择
"currentRecord" --> "country"。
与产品对话框一样,还需要添加一个属性,以判断是点击了保存还是取消按钮。打开 CountryEditor 源文件,将光标放在
propertyChangeSupport 字段的上方,按组合键 Alt-Insert
,然后选择“添加属性”(或者右键选择插入代码-->添加属性);在“添加属性”对话框中,键入
countryConfirmed 作为属性名称。将类型设置为布尔类型,确保选中了“生成 getter 和
setter”复选框单击“确定”关闭该对话框并生成代码。
现在,需要添加代码,以便在显示Countries表时,当用户单击 "New" 按钮时将打开 "Country Editor"
对话框。
添加代码以从主视图中打开对话框:打开 DataBaseView 类并选择“源”视图。导航至 newRecord()
方法,在该方法底部粘贴以下代码:
if(tableName == "Countries"){
Countries c = new Countries();
entityManager.persist(c);
//list.add(c);//有错误
//int row = list.size()-1;
cList.add(c);
int row = cList.size()-1;
masterTable.setRowSelectionInterval(row, row);
masterTable.scrollRectToVisible(masterTable.getCellRect(row, 0,
true));
setSaveNeeded(true);
JFrame mainFrame =
DataBaseApplication.getApplication().getMainFrame();
CountryEditor ce = new CountryEditor(mainFrame, false);
ce.setCurrentRecord(c);
ce.setVisible(true);
if (ce.isCountryConfirmed()) {
save().run();
refresh().run();//增加这一句是因为JTable不自动显示新添加的记录内容,尚未查明原因
}
else {
refresh().run();
}
}
这时会发现系统在list.add(c)处提示一个错误,这是因为list的定义为:
private
java.util.List<databaseapplication.Products>
list;
而向list中添加的是Countries对象。要解决这个问题,需要一个Countries对象列表cList:
private
java.util.List<databaseapplication.Countries>
cList;
然后将上述方法中的list.add(c)修改为cList.add(c)。
除此之外,还需要修改以下方法代码:
public void
deleteRecord() {
if(tableName == "Products"){
//保留原代码
......
}
//添加如下代码
if(tableName == "Countries"){
int[] selected = masterTable.getSelectedRows();
List<Countries> toRemove = new
ArrayList<Countries>(selected.length);
for (int idx=0; idx<selected.length; idx++) {
Countries c =
cList.get(masterTable.convertRowIndexToModel(selected[idx]));
toRemove.add(c);
entityManager.remove(c);
}
cList.removeAll(toRemove);
setSaveNeeded(true);
}
}
SaveTask方法:
private
class SaveTask extends Task {
......
@Override protected Void doInBackground() {
try {
entityManager.getTransaction().commit();
entityManager.getTransaction().begin();
} catch (RollbackException rex) {
rex.printStackTrace();
entityManager.getTransaction().begin();
if(tableName == "Products"){
//保持原代码
......
}
//添加如下代码
if(tableName == "Countries"){
List<Countries> merged = new
ArrayList<Countries>(cList.size());
for (databaseapplication.Countries c : cList) {
merged.add(entityManager.merge(c));
}
cList.clear();
cList.addAll(merged);
}
}
return null;
}
......
}
RefreshTask方法:
将方法中的有关list的两句代码替换为下面代码:
if(tableName
== "Products"){
list.clear();
list.addAll(data);
}
if(tableName
== "Countries"){
cList.clear();
cList.addAll(data);
}
BindTable()方法:
把if(tableName ==
"Countries")语句块中的list都替换为cList,即获取Countries表记录添加到cList,并将cList绑定到JTable。
下面应该添加按钮点击事件处理方法了。创建事件侦听程序和处理程序:
切换到
CountryEditor 类的“设计”视图,在 CountryEditor 窗体中选择 "Save"
按钮,在“属性”窗口中,单击“事件”按钮,单击 actionPerformed 属性旁边的省略号 (...)
按钮。在“actionPerformed 的处理程序”对话框中,添加一个名为 saveCountry 的处理程序。
在源代码编辑器中的
saveCountry 方法内(创建新处理程序后光标跳转到的位置),键入以下代码:
setCountryConfirmed(true);
setVisible(false);
对于 "Cancel"
按钮,重复以上步骤,并将其处理程序命名为 cancelCountry。在 cancelCountry 方法中,键入以下内容:
setCountryConfirmed(false);
setVisible(false);
由于 save() 和
refresh()
操作应用于在应用程序会话期间进行的任何更改,因此,应该将对话框设置为模态对话框,并将主窗体中的表设置为不可编辑。将对话框设置为模态对话框的另一个原因是:在用户按
"保存" 或 "取消" 按钮时,setVisible() 方法在运行事件处理程序(它包括 setCountryConfirmed
方法)后才会返回内容。另外,如果不把对话框设置为模态对话框,可能还会出现其它错误(如不能新增与保存记录等)。
将对话框设置为模态对话框:
打开
CountryEditor 类的“设计”视图,选择该对话框,在“属性”窗口中,单击“属性”,然后选中 modal
属性的复选框。
八、进一步完善程序
1、验证记录删除
现在已经将大多数主要操作的访问位置从主窗体移至对话框。{wy}的例外是 Delete 操作。以前,在应用程序中通过按 "Save"
来确认删除以及按 "Refresh" 来取消删除。由于 "Save" 和 "Refresh"
按钮不再位于主窗体中(save按钮放在了对话框;增加删除验证后,refresh按钮也不再需要),因此需要替换此功能。可通过在
deleteRecord() 方法中添加确认对话框来实现此操作。如果用户单击 "OK",则会调用 save()
方法,因而将{yj}删除记录。如果用户单击 "Cancel",则会调用 refresh() 方法并xxxx。
下面是修改后的deleteRecord方法:
public void
deleteRecord() {
//按
Ctrl-Shift-I 组合键以添加缺少的 import 语句
int n = JOptionPane.showConfirmDialog(null, "Delete the records
permanently?",
"Warning",JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
null);
if (n == JOptionPane.YES_OPTION){
if(tableName == "Products"){
......
//修改setSaveNeeded(true)语句为保存记录
save().run();
}
if(tableName == "Countries"){
......
cList.removeAll(toRemove);
//修改setSaveNeeded(true)语句
save().run();
}
}
else {
refresh().run();
}
}
现在就可以在主窗体中删除refresh和save按扭了。
2、在对话框中添加编辑功能
现在,您可以运行应用程序并单击 "New" 以添加新记录。在对话框中按 "保存"
时,将保存记录。按取消时,将回滚已更改的新记录。不过,无法再编辑现有记录,因为已禁止编辑主窗体中的表。为解决该问题,将在主窗体中添加编辑按钮,以便编辑现有记录。
要添加按钮及其相应的事件处理代码,请执行以下操作:
打开
DataBaseView 类并选择“设计”视图;将 "New"
按钮略微向左拖动;将一个按钮从组件面板拖到刚挪出的空白区域;右键单击按钮,然后选择“设置操作”;在“操作”字段中选择“创建新操作”;在“操作方法”中键入
editRecord;在“文本”中键入"Edit";单击“高级”标签,然后从“启用的属性”中选择
recordSelected。这会生成一个标注属性,以确保仅在选择记录时才会启用操作的按钮和任何其他触发器(如菜单项)。单击“确定”关闭该对话框。将会显示该文件的“源”视图,并且光标位于新生成的
editRecord() 方法中。在该方法中粘贴以下代码:
public void
editRecord() {
if(tableName == "Products"){
setSaveNeeded(true);
JFrame mainFrame =
DataBaseApplication.getApplication().getMainFrame();
ProductEditor pe = new ProductEditor(mainFrame, false);
pe.setCurrentRecord(list.get(masterTable
.convertRowIndexToModel(masterTable.getSelectedRow())));
pe.setVisible(true);
if (pe.isProductConfirmed()) {
save().run();
}
else {
refresh().run();
}
}
if(tableName == "Countries"){
setSaveNeeded(true);
JFrame mainFrame =
DataBaseApplication.getApplication().getMainFrame();
CountryEditor ce = new CountryEditor(mainFrame, false);
ce.setCurrentRecord(cList.get(masterTable
.convertRowIndexToModel(masterTable.getSelectedRow())));
ce.setVisible(true);
if (ce.isCountryConfirmed()) {
save().run();
refresh().run();
}
else {
refresh().run();
}
}
}
至此,该程序以比较完善,您可以根据需要再进行其它修改。
九、多窗体应用程序的其它方法
除了以上方法开发数据库窗体应用程序外,还可以采用下述多窗体方法开发数据库应用程序。这样,在主窗体中点击不同的菜单,将弹出不同的数据显示和编辑子窗体。
1、可以创建一个桌面应用程序程序框架,右键点击项目名(或程序包名)添加JInternalFrame子窗体,在子窗体中进行数据的显示、修改等操作。在主窗体的菜单命令方法中打开子窗体。参考代码如下:
@Action
public void
DisplayProduct() {
ProductFrame childFrame=new ProductFrame();
JDesktopPane desk=new JDesktopPane();
//desk.setOpaque(true);
//desk.setBackground(Color.red);
desk.add(childFrame);
mainPanel.setLayout(new BorderLayout());
mainPanel.add(desk);
childFrame.setVisible(true);
}
2、创建Java应用程序,系统将生成只包含一个Main.java(含main方法)的程序框架。右键点击项目,添加JFrame主窗体,在窗体中添加菜单条、菜单项。参考代码如下:
public class
MainFrame extends javax.swing.JFrame {
public MainFrame() {
initComponents();
}
private void jMenuItem1ActionPerformed(java.awt.event.ActionEvent
evt)
{
//JInternalFrame child=new
JInternalFrame("字体窗体",true,true,true);
ChildFrame child=new ChildFrame();
JDesktopPane desk=new JDesktopPane();
//desk.setOpaque(true);
//desk.setBackground(Color.red);
desk.add(child);
//没有下一语句,子窗体不显示
this.getContentPane().setLayout(new BorderLayout());
this.getContentPane().add(desk);
//没有下一语句,子窗体不显示
child.setSize(300,200);
child.setVisible(true);
}
......
private javax.swing.JMenu jMenu1;
private
javax.swing.JMenuBar jMenuBar1;
private
javax.swing.JMenuItem
jMenuItem1;
......
}
右键点击主窗体所在包名,创建JInternalFrame窗体,在窗体中添加JTable组件。右键选择JTable组件,选择"绑定"-->"elements",点击将数据导入窗体,为JTable绑定数据库表。参考代码如下:
public class
ChildFrame extends javax.swing.JInternalFrame {
public ChildFrame() {
super("Products",true,true,true,true);
initComponents();
}
......
}
main.java的参考代码:
public
static void main(String[] args) {
// TODO code application logic here
new MainFrame().setVisible(true);
}
3、类似第二种方式,不过主窗体代码为手工编写,参考代码如下:
import
test.ChildFrame;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class InternalFrameDemo extends JFrame
{
private JInternalFrame inner1;
private JInternalFrame inner2;
private JDesktopPane desk;
public InternalFrameDemo(String title)
{
super(title);
JMenuBar bar=new
JMenuBar();
JMenu fileMenu=new
JMenu("文件");
JMenuItem openFont=new
JMenuItem("打开字体");
JMenuItem openText=new
JMenuItem("打开文本");
JMenuItem closeFont=new
JMenuItem("关闭字体");
JMenuItem closeText=new
JMenuItem("关闭文本");
JMenuItem exitItem=new
JMenuItem("退出");
fileMenu.add(openFont);
fileMenu.add(openText);
fileMenu.add(closeFont);
fileMenu.add(closeText);
fileMenu.addSeparator();
fileMenu.add(exitItem);
bar.add(fileMenu);
//inner1=new
JInternalFrame("字体窗体",true,true,true);
inner1=new ChildFrame();
inner2=new
JInternalFrame("文本窗体",true,true,true);
desk=new JDesktopPane();
desk.setOpaque(true);
desk.setBackground(Color.red);
desk.add(inner1);
desk.add(inner2);
this.getContentPane().add(desk);
this.setJMenuBar(bar);
exitItem.addActionListener(
new
ActionListener()
{
public
void actionPerformed(ActionEvent e)
{
System.exit(0);
}
}
);
openFont.addActionListener(
new
ActionListener()
{
public
void actionPerformed(ActionEvent e)
{
inner1.setSize(200,100);
inner1.setVisible(true);
}
}
);
openText.addActionListener(
new
ActionListener()
{
public
void actionPerformed(ActionEvent e)
{
inner2.setSize(200,100);
inner2.setVisible(true);
}
}
);
closeFont.addActionListener(
new
ActionListener()
{
public
void actionPerformed(ActionEvent e)
{
inner1.setVisible(false);
}
}
);
closeText.addActionListener(
new
ActionListener()
{
public
void actionPerformed(ActionEvent e)
{
inner2.setVisible(false);
}
}
);
this.setSize(400,300);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args)
{
new
InternalFrameDemo("主窗体");
}
}
这部分代码来自网络,其中ChildFrame为新创建的JInternalFrame,绑定了数据库表。