| 本教程向您介绍了使用 Sun Java Studio Creator 集成开发环境 (Integrated Development Environment, IDE) 生成一个可以创建、检索、更新和删除数据库行的 Web 应用程序的过程。 |
| 该应用程序提供主数据的下拉列表以及同步的详细信息表。用户可以在详细信息表及其关联数据库中添加、更新和删除记录。 |
| 本教程使用了许多在其他基础教程中介绍的概念。如果您不具备 IDE 及其设计组件的基本知识,请考虑首先阅读基础教程,如 Java Studio Creator 入门指南、使用虚拟表单和使用数据绑定组件访问数据库。 |
|
目录
|
![[spacer]](/im/a.gif) |
 |
本教程使用随 Java Studio Creator IDE 提供的捆绑数据库。如果要使用其他数据库,则需要对本教程和 Java Studio Creator 数据源进行适当的更改。例如,在使用其他数据库时,您需要打开“服务器”窗口并添加数据源。
创建项目
首先,创建一个 Web 应用程序项目:
- 在“欢迎”页中,单击“创建新项目”。
- 从“类别”窗格中选择 "Web",并从“项目”窗格中选择“JSF Web 应用程序”,然后单击“下一步”,将会创建一个基于 JavaServer Faces (JSF) 技术的 Web 应用程序。
- 为该项目提供一个标识名称,如
InsertUpdateDelete。单击“完成”。
设计页面布局
在本教程中,将创建包含一个页面的 Web 应用程序。现在将设计页面布局,使其显示一个人名及其相应行程。
- 打开组件面板的“组件”标签,然后展开“基本”类别以查看将在该页中使用的组件。
- 将以下组件拖动到可视设计器上:
- “下拉列表”组件。将其放在页面的左上角。
- “消息组”组件。将其放在下拉列表的右侧。“消息组”组件显示验证和转换错误,以及由
info() 和 error() 方法写入到 Java Server Faces 上下文的消息。如果需要调试项目时,这些消息会很有用。
- “表”组件。将其放在其他两个组件的下方。
此时,您的页面应如下图所示。
图 1:初始页面布局 |
配置下拉列表
-
在可视设计器中选择“下拉列表”组件,然后在“属性”窗口中,将其“常规”类别下的
id 属性更改为 personDD。
- 在可视设计器中右键单击 personDD 下拉列表,然后从弹出式菜单中选择“绑定到数据”。将出现“绑定到数据”对话框。
- 在“绑定到数据”对话框中,单击“绑定到数据提供器”标签,然后单击“添加数据提供器”。将出现“添加数据提供器”对话框。
- 在“添加数据提供器”对话框中,展开“数据源”> "Travel" >“表”节点,然后选择 PERSON 表。单击“添加”按钮。
图 2:“添加数据提供器”对话框 |
- 在“绑定到数据”对话框中,单击“确定”以创建 personDataProvider 并将其绑定到 personDD 下拉列表。
图 3:“绑定到数据”对话框 |
- 右键单击“下拉列表”组件,然后从弹出式菜单中选择“更改时自动提交”。此设置使得每当从列表中选择新值时都会将页发送到服务器。
- 右键单击“下拉列表”组件,然后从弹出式菜单中选择“配置虚拟表单”。在随即出现的对话框中,请注意
personDD 显示在窗口的左上角中,它表明下拉列表已被选定。有关虚拟表单的详细信息,请参见教程使用虚拟表单。
- 单击“新建”。在“名称”列中输入 "person",然后在“参与”和“提交”列中同时选择“是”。
图 4:配置虚拟表单 |
通过使用虚拟表单,应用程序可避免对表中的数据进行不必要的验证。
- 单击“确定”。
- 单击可视设计器工具栏中的“显示虚拟表单”按钮。通过查看虚拟表单,您将能够看到可视设计器中的组件与您已经配置的任何虚拟表单之间的关系。
图 5:显示虚拟表单 |
配置表
- 右键单击“表”组件,然后从弹出式菜单中选择“绑定到数据”。
- 单击“添加数据提供器”。将打开“添加数据提供器”对话框。
- 在“添加数据提供器”对话框中,展开 "Travel" >“表”节点,然后选择 "TRIP"。单击“添加”。此操作将创建一个名为 tripDataProvider 的数据提供器。此时将会出现一个对话框。
- 该对话框显示您刚创建的 tripDataProvider 中可用的字段,并允许您控制要在表中显示哪些字段。使用 "<" 按钮,从右侧的“选定”列表中移除 TRIP.TRIPID 和 TRIP.PERSONID,如下图所示。这些值仅对编程人员有用,不需要将它们显示给用户。
图 6:将表绑定到数据提供器 |
- 单击“确定”以实现更改并关闭对话框。
- 在可视设计器中,重新选择“表”组件,然后单击鼠标右键,从弹出式菜单中选择“表布局”。
- 单击“选项”标签,将标题改为
Trips Summary。单击“确定”。
提示:也可以通过直接在可视设计器中选择标题并对其进行编辑来更改表组件的标题。
目前在可视设计器中的表组件应如下图所示。请注意,如果未按下图顺序显示各列,则可以通过重新打开“表布局”对话框,单击“列”标签,使用“上移”和“下移”按钮重新对其进行排列。
图 7:Page1 表布局 |
- 在“概要”窗口中,展开 "SessionBean1",右键单击 "tripRowSet",然后从弹出式菜单中选择“编辑 SQL 语句”。这将打开 SQL 查询编辑器。
- 在窗口中心附近的网格区域,在 PERSONID 行中单击鼠标右键,然后选择“添加查询条件”。将打开“添加查询条件”对话框。
- 在对话框中,选择“参数”单选按钮,然后单击“确定”。结果是:字符 "=?" 出现在 PERSONID 的“条件”表单元格中,条件 "WHERE TRAVEL.TRIP.PERSONID=?" 出现在 SQL 窗格中 SQL 语句的结尾。
提示:可以使用一种更快捷的方法,而无需使用“添加查询条件”对话框。只需选择 PERSONID 的“条件”单元格,直接键入 "=?" 即可。然后按 Tab 键实现更改。
- 关闭 SQL 编辑器。
更改列组件
现在,更改表中的页眉文本以使其更具可读性。此外,还要将列内容变为可编辑的字段,准备实现将新行程插入到数据库中的功能。为此,可以通过在表组件中嵌套其他组件,以利用其复合特性。
- 右键单击“表”组件,然后选择“表布局”。将打开“表布局”对话框。
- 单击“列”标签,然后从右侧的“选定”列表中选择 "TRIP.DEPDATE"。在对话框底部的属性区域,进行如下更改:
- 将“页眉文本”字段从 DEPDATE 更改为
Date。
- 使用下拉列表,将“组件类型”从“静态文本”更改为“文本字段”。
- 从“选定”列表中选择 "TRIP.DEPCITY",并进行如下更改:
- 将“页眉文本”字段从 DEPCITY 更改为
From City。
- 使用下拉列表,将“组件类型”从“静态文本”更改为“文本字段”。
- 从“选定”列表中选择 "TRIP.DESTCITY",并进行如下更改:
- 将“页眉文本”字段从 DESTCITY 更改为
To City。
- 使用下拉列表,将“组件类型”从“静态文本”更改为“文本字段”。
- 从“选定”列表中选择 "TRIP.TRIPTYPEID",并进行如下更改:
- 将“页眉文本”字段从 TRIPTYPEID 更改为
Trip Type。
- 使用下拉列表,将“组件类型”从“静态文本”更改为“下拉列表”。
- 单击“确定”以实现更改并关闭窗口。如果执行上述步骤后表列太宽,则可以通过选择每列中的第一个组件再拖动其选择句柄来调整其大小。
- 在可视设计器中,选择“表”中 Trip Type 列最上面的“下拉列表”组件。单击鼠标右键,然后从弹出式菜单中选择“绑定到数据”。将打开“绑定到数据”对话框。
- 在“绑定到数据”对话框中,单击“绑定到数据提供器”标签,然后单击“添加数据提供器”。将出现“添加数据提供器”对话框。
- 在“添加数据提供器”对话框中,展开“数据源”> "Travel" >“表”,然后选择 "TRIPTYPE"。单击“添加”。此操作将创建
triptypeDataProvider 数据提供器。将重新出现“绑定到数据”对话框。
- 在“绑定到数据”对话框中,从下拉列表中选择 "triptypeDataProvider"。
- 在“值字段”中,选择 "TRIPTYPE.TRIPTYPEID";在“显示字段”中,选择 "TRIPTYPE.NAME",如下图所示。单击“确定”以实现更改并关闭窗口。
图 8:“绑定到数据”对话框 |
为表配置虚拟表单
您将为表中的输入组件创建一个虚拟表单。通过虚拟表单可确保在更改 personDD 后提交此页时,不会对输入组件进行验证和转换。用户无论何时从下拉列表中选择新人员,都会在表中显示正确的信息。
- 在可视设计器中,使用 Ctrl 键的同时单击“表”组件中的三个“文本字段”组件和一个“下拉列表”组件。右键单击其中一个选定的组件,然后从弹出式菜单中选择“配置虚拟表单”。
- 在“配置虚拟表单”对话框中,确保窗口左上角列出了三个文本字段和一个下拉列表。如果未按下图所示的方式列出这些内容,请关闭对话框,重新选择它们,然后重试。如果已正确列出这些内容,则单击“新建”。
- 将新虚拟表单的名称更改为
save。将“参与”设置更改为“是”,如下图所示,然后单击“确定”以关闭窗口。
图 9:为表元素配置虚拟表单 |
现在,将 personDD 下拉列表组件与表组件关联,以便在用户从列表中选择某人时,此人的行程会出现在表中。
添加事件代码和初始化代码
为了使用户从下拉列表中的选择与行程表相对应,您需要添加事件处理程序。
- 双击 personDD 下拉列表以创建值更改事件方法,此时,将会打开 Java 编辑器,并且插入点位于该方法内。
- 在值更改事件方法中,找到注释行
// TODO: Replace with your code。使用代码样例 1 中的代码替换此行,以便事件处理程序可以读取其中的代码。
| 代码样例 1:下拉列表事件处理程序 |
public void personDD_processValueChange(ValueChangeEvent vce) {
Object selectedPersonId = personDD.getSelected();
try {
personDataProvider.setCursorRow(
personDataProvider.findFirst("PERSON.PERSONID", selectedPersonId));
getSessionBean1().getTripRowSet().setObject(1, selectedPersonId);
tripDataProvider.refresh();
form1.discardSubmittedValues("save");
}catch (Exception e) {
error("Cannot switch to person " + selectedPersonId);
log("Cannot switch to person " + selectedPersonId, e);
}
}
|
在 try 子句的结尾,语句 form1.discardSubmittedValues("save"); 可确保每当用户从下拉列表中选择新人员时,当前表的行程信息将被替换为与所选人员相关的新信息。回想一下,显示行程信息的用户界面元素都参与了 save 虚拟表单。
请注意,事件处理程序不会抛出异常,而是在服务器日志(server.log 文件)中记录异常。它还会调用错误方法,一旦发生错误事件,放置在页面上的消息组组件就会显示一则消息。
- 在 Java 源代码中滚动到
prerender() 方法(或者,如果愿意,按 Ctrl-F 组合键打开“查找”对话框,键入并搜索 prerender)。按照代码样例 2 中所示编辑该方法。
| 代码样例 2:Prerender 方法 |
public void prerender() {
if ( personDD.getSelected() == null ) {
Object firstPersonId = null;
try {
personDataProvider.cursorFirst();
firstPersonId = personDataProvider.getValue("PERSON.PERSONID");
personDD.setSelected(firstPersonId);
getSessionBean1().getTripRowSet().setObject(
1, firstPersonId);
tripDataProvider.refresh();
}catch (Exception e) {
error("Cannot switch to person " +
firstPersonId);
log("Cannot switch to person " +
firstPersonId, e);
}
}
}
|
- 在 Java 编辑器中单击鼠标右键,然后选择“重新设置代码格式”对代码进行适当的排列。
测试应用程序 - 第 1 部分
在主工具栏上单击“运行主项目”按钮以生成、部署和运行项目。页面将被装入到 Web 浏览器中,并使用名字来填充下拉列表,使用数据填充表。当您从列表中选择其他名字时,与该名字相关联的行程将出现在表中。
图 10:部署的应用程序,测试 1 |
添加插入功能
现在,通过将行集插入到数据库,实现将行程添加到表的功能。首先,为表的文本字段提供消息组件。这些功能可以确保当输入的信息不正确时,用户能够看到错误消息。
- 在可视设计器中查看 Page1。
- 在组件面板的“基本”类别中,将一个“消息”组件拖动到表的前三列中最上面的文本字段上。
- 按 Ctrl-Shift 组合键单击,然后将每个“消息”组件拖动到每个“文本字段”组件上。在执行此操作时,指针将变成链接符号,如下图中的 "From City" 列所示。当消息与文本字段正确关联时,消息文本将更改为显示关联,如下图中的 "Date" 列所示。
图 11:使消息组件与文本字段关联 |
现在,将一个按钮添加到页面中,以便用户可以通过它将新行添加到数据缓冲区中。
- 在组件面板的“基本”类别中,将一个“按钮”组件拖动到 Page1 上,并将它放置在“表”组件的上方(位于第二列的顶部附近),如图 12 所示。
- 按钮文本在可视设计器中处于选定状态。将该文本从
按钮更改为 Add Trip。按 Enter 键或者在按钮组件之外单击,使编辑生效。
- 选择该按钮,然后在“属性”窗口中,将按钮的“常规”类别下的
id 属性更改为 add。
- 在可视设计器中,双击该按钮以打开 Java 编辑器。在 Java 编辑器中,插入点将位于按钮的事件处理程序中。
- 修改按钮的事件代码(
add_action() 方法),使其如下所示:
| 代码样例 3:添加行程操作的代码 |
public String add_action() {
try {
RowKey rk = tripDataProvider.appendRow();
tripDataProvider.setCursorRow(rk);
tripDataProvider.setValue("TRIP.TRIPID", new Integer(0));
tripDataProvider.setValue("TRIP.PERSONID", personDD.getSelected());
tripDataProvider.setValue("TRIP.TRIPTYPEID", new Integer(1));
} catch (Exception ex) {
log("Error Description", ex);
error(ex.getMessage());
}
return null;
}
|
- 如果 Java 编辑器因 IDE 无法解析
RowKey(或其他类)而显示错误,可以使用 Java 编辑器的“修复导入”功能解决该问题。该功能可以自动将所需的包添加到 Page1.java 代码块的 import 语句中。要使用该功能,请在 Java 编辑器中单击鼠标右键,然后从弹出式菜单中选择“修复导入”。为了解析 RowKey 引用,“修复导入”功能会将以下语句添加到 imports 块中: import com.sun.data.provider.RowKey;
测试应用程序 - 第 2 部分
生成、部署和运行项目。页面将被装入到 Web 浏览器中,而且还会出现 "Add Trip" 按钮。每次单击该按钮时,都会有一个新的空行附加到表的底部。您可以编辑行中的信息,但由于尚未提供行集的保存机制,因此当您从下拉列表中选择其他名字时,更改将会丢失。
图 12:部署的应用程序,测试 2 |
修改页以保存行集
现在,将另一个行集添加到项目中。该行集用于计算已使用的最大行程 ID。
- 在编辑器窗口中单击“设计”以返回到可视设计器的 Page1 中。
- 从“服务器”窗口中选择“数据源”> "Travel" >“表”> "TRIP" 表,然后将它拖动到“概要”窗口中的 SessionBean1 节点上。
图 13:将 TRIP 表拖动到 SessionBean1 上 |
此操作将打开“为表 TRIP 添加具有行集的新数据提供器”对话框。
- 单击“创建 SessionBean1/tripRowSet1”单选按钮,将数据提供器的名称更改为
maxTripRowSet,然后单击“确定”。
图 14:将新的数据提供器添加到 SessionBean1 |
此操作将在 SessionBean1 中创建 maxTripDataProvider 和 maxTripRowSet。
- 在“概要”窗口中,双击 "SessionBean1" > "maxTripRowSet" 以打开查询编辑器。在源代码窗格(从上数第三个)中单击。删除在此处找到的现有 SQL 查询,然后输入以下查询:
SELECT max(TRAVEL.TRIP.TRIPID)+1 as MAXTRIPID FROM TRAVEL.TRIP
这将在 "Save" 按钮的操作处理程序中使用 MAXTRIPID 值,接下来将添加 "Save" 按钮。
- 关闭查询编辑器。请注意,此查询不受查询编辑器的图形编辑器支持。如果看到一个提示出现语法错误的警报对话框,则可以通过单击“继续”安全地关闭它。
现在,添加另一个“按钮”组件,该组件会将用户的更改保存到数据库中。
- 将“按钮”组件放在表的第一列的上方。
- 将按钮的文本属性从
按钮更改为 Save Changes。
- 在“属性”窗口中,将“常规”类别中的 "id" 属性更改为
save。
- 右键单击 "Save Changes" 按钮,然后从弹出式菜单中选择“配置虚拟表单”。将打开“配置虚拟表单”对话框。
- 在“配置虚拟表单”对话框中,确保左上角的列表中显示的是
save,以便让此窗口中的更改应用于 "Save Changes" 按钮。然后,选择 save 虚拟表单,将“提交”值更改为“是”,再单击“确定”。
- 在可视设计器中,双击 "Save Changes" 按钮以打开 Java 编辑器。在 Java 编辑器中,插入点将位于按钮的事件处理程序中。
- 修改按钮的事件代码(
save_action() 方法)使其显示如下: 注意: 此代码适用于 Java Studio Creator 2 Update 1,如果您使用的是 Java Studio Creator 2,请将:
int newTripId = ((Integer) maxTrip.getValue("MAXTRIPID")).intValue();
改为int newTripId = ((Long) maxTrip.getValue("MAXTRIPID")).intValue();
| 代码样例 4:保存操作的代码 |
public String save_action() {
try {
// Get the next key, using result of query on MaxTrip data provider
CachedRowSetDataProvider maxTrip = getSessionBean1().getMaxTripDataProvider();
maxTrip.refresh();
maxTrip.cursorFirst();
int newTripId = ((Integer) maxTrip.getValue("MAXTRIPID")).intValue();
// Navigate through rows with data provider
if (tripDataProvider.getRowCount() > 0) {
tripDataProvider.cursorFirst();
do {
if (tripDataProvider.getValue("TRIP.TRIPID").equals(new Integer(0))) {
tripDataProvider.setValue("TRIP.TRIPID", new Integer(newTripId));
newTripId++;
}
} while (tripDataProvider.cursorNext());
};
tripDataProvider.commitChanges();
} catch (Exception ex) {
log("Error Description", ex);
error("Error :"+ex.getMessage());
}
return null;
}
|
测试应用程序 - 第 3 部分
通过单击“运行主项目”按钮以生成、部署和运行项目。应用程序将可以实现下面的功能:
- 可以添加行程并进行保存。该行程将会出现在表中,即使选择其他人然后再返回时,它仍然存在。
- 可以编辑现有的行程信息,并保存所做的更改。
- 如果在 "Date" 字段中输入除日期之外的内容,则应用程序将提供一条错误消息。
- 在保存之前可以多次单击 "Add Trip",这是一种一次添加多行的简单方法。
- 如果在保存之前切换到其他人员,则会丢失所有更新(包括在添加的行中填写的内容)。
- 如果修改某些值,然后单击一个列标题(使之按该列进行排序),则表组件将记住这些暂挂的更改,随后可以保存这些更改。
添加删除功能
现在,将删除功能添加到表中。使用此功能,用户能够通过从数据库中删除某行来删除行程。在本教程中,"Delete" 按钮的操作是即时的,不需要使用 "Save Changes" 按钮从数据库中删除行。实际上,由于 "Delete" 按钮的事件处理程序使用 commitChanges() 方法,因此它也像 "Save Changes" 按钮那样保存所有暂挂更改。
向每行添加 "Delete" 按钮
- 在可视设计器中,右键单击“表”组件,然后从弹出式菜单中选择“表布局”。将打开“表布局”对话框。
- 单击“列”标签,然后单击“新建”,将一个新列添加到表中。
- 当“选定”列表中选定新列的名称时,在“列详细信息”区域中进行如下更改:
- 页眉文本和页脚文本:<删除“页眉文本”字段和“页脚文本”字段中的任何缺省文本,使其保持空白>
- 组件类型:
按钮
- 值表达式:
Delete
- 宽度:<删除任何缺省值,使其保持空白>
- 水平对齐:
居中
- 垂直对齐:
居中
- 单击“确定”。
- 选择“表”组件中最上面的 "Delete" 按钮,并在“属性”窗口中对属性进行如下更改:
添加事件代码
- 双击 "Delete" 列中的第一个按钮,将打开 Java 编辑器,且插入点位于
delete_action() 事件处理程序中。
- 更改
delete_action() 方法,使其如下所示。
| 代码样例 5:删除操作的代码 |
public String delete_action() {
form1.discardSubmittedValues("save");
try {
RowKey rk = tableRowGroup1.getRowKey();
if (rk != null) {
tripDataProvider.removeRow(rk);
tripDataProvider.commitChanges();
tripDataProvider.refresh();}
} catch (Exception ex) {
log("ErrorDescription", ex);
error(ex.getMessage());
}
return null;
}
|
测试应用程序 - 第 4 部分
通过单击“运行主项目”按钮以生成、部署和运行项目。
部署后,您应该能够从表中删除行,从而将其从数据库中删除。删除操作还会将所有暂挂更改提交到数据库中。
图 15:测试应用程序,第 4 部分 |
添加恢复功能
现在,将恢复功能添加到页面中。使用此功能,用户可以放弃其编辑内容,并恢复为以前保存的数据。请注意,恢复功能不能恢复已保存或已删除的行;因为 "Save Changes" 和 "Delete" 按钮会将更改提交到数据库中。
添加 "Revert Changes" 按钮
- 在可视设计器中,将一个“按钮”组件从组件面板拖动到 Page1 上。将新的按钮放在 "Add Trip" 按钮的右侧。
- 将按钮的 text 属性更改为
Revert Changes。
- 在“属性”窗口中,将“按钮”组件的“常规”类别下的
id 属性更改为 revert。
添加事件代码
现在,添加事件代码以实现恢复功能。
- 双击 "Revert Changes" 按钮,将打开 Java 编辑器,插入点会位于
revert_action() 方法中。
- 按照以下代码示例编辑
revert_action() 方法。
| 代码样例 6:恢复操作的代码 |
public String revert_action() {
form1.discardSubmittedValues("save");
try {
tripDataProvider.refresh();
} catch (Exception ex) {
log("Error Description", ex);
error(ex.getMessage());
}
return null;
}
|
配置虚拟表单
如果应用程序使用目前的配置,则会出现一些不理想的行为。例如,如果用户在现有行的第一列中输入了一个无效日期,然后单击 "Add" 按钮,操作将失败。由于日期转换错误会拒绝表单提交,所以不会有任何新行添加到表中。理想的行为是放弃处理表中的输入字段,这样就可以直接添加新行,而无需依赖于现有行的编辑内容。
同样,当用户单击 "Delete" 按钮删除行时,不论对该行或其他现有行执行了哪些编辑内容,都会删除该行。当然,用户还可以通过单击 "Revert" 按钮有意地放弃所有编辑内容,同样在使用虚拟表单的情况下也会忽略这些编辑内容。
为确保用户在单击 "Revert"、"Add" 和 "Delete" 按钮时绕过对页面上的输入字段进行处理(包括验证检查),现在可以让这些按钮提交虚拟表单。在本例中,您可以让所有三个按钮都提交相同的虚拟表单,因为它们都需要提交一个无任何参与组件的虚拟表单。
-
在可视设计器中,选中 "Add"、"Delete" 和 "Revert" 按钮,单击鼠标右键,然后从弹出式菜单中选择“配置虚拟表单”。
-
在“配置虚拟表单”窗口中,
add、delete 和 revert 应该出现在左上角中,以表明选定了这些按钮。
- 在“配置虚拟表单”窗口中,单击“新建”,将新的虚拟表单命名为
add/delete/revert,然后将“提交”设置为“是”。单击“确定”。
测试应用程序 - 第 5 部分
通过单击“运行主项目”按钮以生成、部署和运行项目。
部署后,程序可执行以下功能:
- 从下拉列表中选择一个名字,并显示此人的行程摘要
- 编辑现有的行程信息,并将更改保存到数据库
- 将行添加到表中,填写行程字段,并将更改保存到数据库
- 从表中(以及从数据库中)删除行
- 放弃编辑内容,并恢复到数据库中最近保存的数据
图 16:测试应用程序,第 5 部分 |
小结
在本教程中,您将表组件、文本字段组件和下拉列表组件与数据库中的信息进行了关联。并为组件设置了属性,添加了 prerender 和事件代码,以便从数据库插入、更新和删除数据。同时使用了虚拟表单,应用程序可以通过它只应用一个页面,并且允许提交的数据绕过验证检查。
另请参见:
更多的开发者资源:
此页的最新修改时间:2006 年 4 月 14 日
|