MYSQL网站制作之在Web使用程序中利用DBI
对于IT经理来说,令他们喜欢的MySQL的简单性还有另一方面。MySQL可以运行的更快速。某些人或许会说MySQL缺少了一些人们想要的功能。web|程序迄今为止,我们编写的DBI剧本用于命令行情况中的命令注释程序,但DBI在其他情况下也是有效的,比方在基于Web的使用程序的开辟中。当编写能从Web扫瞄器挪用的DBI剧本时,就翻开了奇怪而风趣的与数据库交互的功能。比方,假如以表格的情势显现数据,则能够很简单地把每一个列题目转换为能够选择的毗连,以便将该列的数据从头排序。它同意单击一次就能够以分歧的体例检察数据,而又不用键进任何查询。大概能够供应一种用户能够为数据库搜刮而键进的尺度格局,然后,显现含
有搜刮了局的页面。像这类复杂的才能可以出格地改动为会见数据库内容而供应的交互性的程度。除此以外,Web扫瞄器的显现才能比在终端窗口取得的才能要分明地更好一些,以是,输入也常常看起来更大度。
在这部分,我们将创立上面的基于Web的剧本:
samp_db数据库中表的通用扫瞄器。这与我们想对这个数据库完成的任何特定的义务有关,可是它举例申明了多少Web程序计划观点,并供应了一种检察这些表所含有的信息的便利体例。
同意我们检察任何给定的检验或测试分数的分数扫瞄器。它作为回忆评分事务了局的疾速体例是很便利的,而且当我们必要创立测试的品级曲线时,它是有效的,以是我们能够以字母品级来标志试卷。
寻觅分享配合乐趣的汗青联盟成员的剧本。经由过程同意用户输出搜刮短语来完成它,然后在member表的interests域来搜刮短语。我们已编写了一个行命令剧本来做这些,可是,基于Web的版本供应了有引导意义的参考概念,同意对统一义务对照两种办法。
我们将利用CGI.pmPerl模块来编写这些剧本,这个模块是将DBI剧本毗连到Web上最简单的办法(有关取得CGI.pm模块的申明,请参阅附录A)。之以是称为CGI.pm,是由于它有助于编写利用大众网关协定的剧本,这个协定界说了Web服务器怎样与其他程序通讯。CGI.pm处置触及了很多通用外务处置的义务细节,如搜集经由过程Web服务器传送到剧本的作为输入的参数值。CGI.pm也供应了天生HTML输入的便当办法,与编写本人原始的HTML标志比拟,它削减了编写丢脸的HTML的时机。
在本章中,您将学到充足有关CGI.pm的常识来编写本人的Web使用程序,可是,固然不是它所包含的一切功能。要想进修有关这个模块的更多常识,请参阅LincolnStein(JohnWiley1998出书)撰写的《OfficialGuidetoProgrammingwithCGI.pm》,或在以下网址查阅联机文档:
http://stein.cshl.org/www/software/CGI/
设置CGI剧本的Apache
除DBI和CGI.pm以外,编写基于Web的剧本还必要有一个以上的组件:Web服务器。这里的申明合适Apache服务器利用剧本,可是,假如乐意,略微改编一点这些申明,就能够利用分歧的服务器。
一样平常来讲,Apache安装的各个部分位于/usr/local/apache目次。对我们的目标来说,这个目次中最主要的子目次为htdocs(HTML文档树)、cgi-bin(可实行的剧本和Web服务器挪用的程序),和conf(设置文件)。这些目次也大概放在体系中的其他中央。假如是如许,则要对上面的注重事项做得当的调剂。
应当考证cgi-bin目次不在Apache文档树的外部,以便它外部的这些剧本不克不及作为无格局文原本哀求。这是个平安的提防办法。您也不肯意让怀有歹意的客户机程序反省您的剧本,经由过程提取这些剧本的文本并研讨它们来作为平安的冲破口。
要想安装以Apache体例利用的CGI剧本,则将它放在cgi-bin目次下,然后将这个剧本的一切权变动为运转Apache的用户,并将它的形式变动为对该用户为可实行的和只读的形式。比方,假如Apache以称号为www的用户体例运转,则利用上面的命令:
%chownwwwscript_name
%chmod500script_name
大概必要用www或root运转这些命令。假如不同意在cgi-bin目次下安装剧本,则能够哀求体系办理员代表您来如许做。
安装这个剧本以后,经由过程向Web服务器发送得当的URL,能够哀求扫瞄器上的这个剧本。典范的URL是如许的:
http://your.host.name/cgi-bin/script_name
从Web扫瞄器哀求剧本会招致Web服务器实行它。前往剧本的输入,了局作为Web页面呈现在扫瞄器中。
假如为追求更好的功能而利用具有mod_perl的CGI剧本,则能够如许做:
1)确保最少有以下版本的必须软件:Perl5.004、CGI.pm2.36和mod_perl1.07。
2)确保将mod_perl编译为Apache可实行的文件。
3)创建一个存储剧本的目次。我利用了/usr/local/apache/cgi-perl。cgi-bin不该该位于Apache文档树的外部,出于一样的平安缘故原由,cgi-perl目次也不该该在那边。
4)告诉Apache,与位于cgi-perl目次中的剧本mod_perl相干联:
假如正在利用Apache确当前版本,这个版本利用单个的设置文件,则将一切这些唆使放在httpd.conf中。假如Apache的版本利用三个旧文件的办法来设置信息,则将Alias唆使放进srm.conf中,将Location行放进access.conf中。关于cgi-perl目次,不要启用mod_perl、PerlSendHeader或PerlSetupEnv唆使。这些由CGI.pm主动地处置,启用它们大概招致处置抵触。
mod_perl剧本的URL与尺度的CGI剧本的URL相相似。独一的分歧的地方在于指定cgi-perl而不是cgi-bin。
http://your.host.name/cgi-perl/script_name
有关的具体信息,请参阅上面地点的ApacheWeb站点的mod_perl地区:
http://perl.apache.org/
CGI.pm的扼要先容
为了编写利用CGI.pm模块的Perl剧本,将use行放在这个剧本的开首四周,然后创立让您会见CGI.pm办法和变量的CGI工具:
useCGI;
my($cgi)=newCGI;
我们的CGI剧本利用了CGI.pm的功能,它经由过程利用$cgi变量挪用办法来完成。比方,为了天生级别1题目,我们将如许利用h1()办法:
print$cgi->h1("MyHeading");
CGI.pm也撑持同意以函数挪用它的办法的利用作风,而不必前导的‘$cgi->’。在这里,我没有利用这个语法,是由于‘$cgi->’标记更相似于利用DBI的体例,还由于它避免CGI.pm函数名与能够界说的任何函数名发生抵触。
1.反省输出参数,并编写输入
CGI.pm所做的事变之一就是照看一切丑恶的细节,这些细节触及到搜集由Web服务器向剧本供应的输出信息。为了取得那些信息,所需做的就是挪用param()办法。能够以下取得一切可用的参数名:
my(@param)=$cgi->param();
为了检索特定参数的值,只定名感乐趣的参数:
CGI.pm还供应天生传送给客户机扫瞄器的输入办法。思索上面的HTML文档:
这个代码利用$cgi来发生等价的文档:
利用CGI.pm天生输入,而不是编写本人原始的HTML,如许做的一些长处是,能够按逻辑单位思索,而不是按独自的构成标识来思索,并且HTML不太大概含有毛病(我说“不太大概”的缘故原由是CGI.pm不由止做乖僻的事变,如含有一列外部的题目)。除此以外,关于
编写的非标志文本,CGI.pm供应主动的字符本义,如HTML中指定的‘<’和‘>’。
假如乐意,CGI.pm天生输入办法的利用其实不排挤编写本人原始的HTML。能够将这两种办法夹杂起来,组合挪用具有天生笔墨标识的显现语句的CGI.pm办法。
2.本义的HTML和URL文本
假如经CGI.pm办法,如start_html()或h1(),编写非标志的文本,则主动地本义文本中的特定字符。比方,假如利用上面的语句天生题目,则题目文本中的‘&’字符将由CGI.pm转换为‘&’:
print$CGI->start_html(-title=>"A,B&C");
假如不利用CGI.pm天生输入的办法编写非标志的文本,则大概应当先让它经由escapeHTML(),以便确保能够准确地本义任何指定的字符。当机关大概含有特定字符的URL时也是如许,只管在那种情形下应当利用escape()办法来取代它。利用得当的编码办法是很主要的,由于每种办法都将分歧的字符集作为特别的字符来看待,并利用相互分歧的格局来看待特别的字符编码。思索上面冗长的Perl剧本:
假如运转这个剧本,则它天生上面的输入,从这里能够看到HTML文本的编码分歧于URL的编码:
3.编写多目标页面
编写基于Web的剧本来天生HTML,而不是编写静态的HTML文档的次要缘故原由之一是,依据挪用体例,剧本能够发生分歧范例的页面。我们将要编写的一切CGI剧本都有这类特征。每个都像上面如许操纵:
1)当从扫瞄器第一次哀求这个剧本时,它天生一个初始页面,同意选择想要的信息范例。
2)当作了选择今后,从头挪用这个剧本,可是,此次它在第二页检索,并显现哀求的特定信息。
这里的次要成绩是想从第一页的选择中断定第二页的内容,可是,一般Web页面是相互自力的,除非布置某些特定分列的序次。这个秘诀是让剧本天生页面,这个页面给参数设置一个值,告知这个剧本的下一个挪用想要的内容。当第一次挪用这个剧本时,这个参数没有
值;告知这个剧本给出它的初始页面。当指出想看的信息内容时,这个页面再一次挪用这个剧本,可是,将参数设为唆使这个剧本做甚么的一个值。
将申明从页面传送回剧本有分歧的体例。一种体例是供应一种用户填写的表格。当用户提交这张表格时,将它的内容提交给Web服务器。服务器将信息传送给剧本,这个剧本经由过程挪用param()办法,可以找出提交的内容。这就是我们对第三个CGI剧本所做的事变(同意用户输出搜刮汗青联盟目次的关头字)。
对剧本指定申明的别的一种办法是,当哀求剧本时,将信息作为发送到Web服务器的URL的一部分来传送。这就是我们关于samp_db表扫瞄器和分数扫瞄器剧本要做的事变。这类事情体例是剧本天生含有超链接的页面。选择一个毗连,再次挪用这个剧本,可是,此次
指定参数值,这个参数值唆使这个剧本做甚么。实践上,这个剧本以分歧的体例挪用它自己,来供应分歧范例的了局,这取决于用户所选择的毗连。
剧本能够同意经由过程向扫瞄器向它本人的URL传送一个含有超链接的页面来挪用它自己。比方,剧本my_script能够编写含有以下如许毗连的页面:
<AHREF="/cgi-bin/my_script">ClickMe!</A>
当用户敲进文本“ClickMe!”时,用户扫瞄器就哀求将my_script发送回Web服务器。固然,一切这些会招致剧本再次发送出统一个页面,由于它不撑持其他信息。但是,假如将一个参数附加到URL上,则当用户选择这个毗连时,将这个参数送回Web扫瞄器。服务器
挪用这个剧本,这个剧本能够挪用param()来侦测设置的参数,并依据它的值接纳举动。
为了把参数附到URL的开端,加一个“?”字符放到称号/值的后面。为了附上多个参数,用字符“&”分开。比方:
/cgi-bin/my_script?name=value
/cgi-bin/my_script?name=value&name2=value2
为了机关带有附加参数的自援用的URL,CGI剧本应当经由过程挪用script_name()办法取得本人的URL来入手下手,然后像依照以下办法增加参数:
在机关URL以后,经由过程利用CGI.pm的a()办法,能够天生一个包含它的超链接<A>标志:
print$cgi->a({.href=>$url},"ClickMe!);
经由过程反省一个冗长的CGI剧本来检察怎样事情会更简单。第一次挪用时,上面的剧本flip_flop,给出了一个含有单个超链接的称为页面A的页面。选择这个毗连再次挪用这个剧本,可是设置page参数,告知它显现页面B。页面B也包含对剧本的毗连,可是page参数没有值。因而,在页面B当选择这个毗连招致从头显现原始页面。随后的剧本挪用将页面在剧本A和剧本B之间往返切换:
假如另外一个客户机程序呈现并哀求flip_flop,就给出初始页面,由于分歧客户机的扫瞄器其实不相互影响。
实践上,$url的值被后面的样例设置成大度的作风。在把它们放在URL以后以避免包含特别字符时,利用escape()办法对参数名和值举行编码是对照好的。这里有一个较好的办法来用附加的参数值来机关URL:
从Web剧本毗连到MySQL服务器
我们在前一节“运转DBI”中开辟的命令行剧本,为创建到MySQL服务器的毗连共享了一个通用的前文。CGI剧本也共享了一些代码,可是有一些分歧:
这个前文与命令行剧本利用的前文的分歧的地方在于以下几个方面:
第一部分如今含有一条useCGI语句。
不再剖析命令行的参数。
代码仍旧在可选文件中寻觅毗连参数,可是,在用户实行剧本的主目次中不利用.my.cnf文件(也就Web服务器用户的主目次)。Web服务器大概运转会见其他数据库的剧本,没有来由假定一切剧本会利用统一毗连参数。相反,我们寻觅分歧地位寄存的可选文件(/usr/local/apache/conf/samp_db.cnf)。假如想利用分歧的文件,应当修正可选文件的路径名。
经由过程Web服务器挪用的剧本作为Web服务器用户,而不是作为您来运转。这就提出了一些平安成绩,由于在Web服务器接受以后您就不再把持了。应当把可选文件的一切权交给运转Web服务器的用户(多是www大概nobody大概一些相似的用户),并将形式设置为400或600,以便其他用户不克不及读取。不幸的是,能够安装这个Web服务器的剧本来实行的任何人仍旧可以读取这个文件。他们要做的一切事变就是编写一个剧本,显式地翻开可选文件,并在Web页面上显现它的内容。由于他们的剧本作为Web服务器用户来运转,以是它将有充足的权力来读取这个文件。
因为这个缘故原由,创立一个对samp_db数据库具有只读(SELECT)权限的MySQL用户,然后在samp_db.cnf文件中列出这个用户的称号和口令,而不是您本人的称号和口令,这类举动是很审慎的。作为有权修正数据库的表的用户,这类体例不会冒险同意剧本毗连到数据库。第11章“惯例的MySQL办理”,会商了怎样创立具有严厉权限的MySQL用户账户。
另外一种选择,能够在Apache的suEXEC机制下布置实行剧本。这就同意作为特别权限的用户实行剧本,然后编写剧本,从只对谁人用户为只读的可选文件中取得毗连参数。比方,必要编写会见数据库的剧本,就能够如许做。
另有别的一种办法就是编写剧本,从客户机用户哀求用户姓名和口令,并利用这些值创建到MySQL服务器的毗连。这类办法关于为办理目标而创立剧本比关于为一样平常利用供应剧本更合适。不管怎样,应当小心用户名和口令哀求的一些办法遭到一些人的打击,这些人可
能在您和服务器之间的收集上安置窃听器。
由于能够夙昔面的段落中汇集,以是Web剧本的平安性是个辣手的成绩。很分明,应当多读一些有关平安的主题,由于它是一个年夜的主题,以是在这里我不克不及真正做得很周全。检察Apache手册中有关平安性的材料是一种好的办法。您也能够查找WWW平安性的FAQ说
明,比方可使用上面的网址:
http://www.w3.org/Security/Faq/
samp_db数据库扫瞄器
关于第一个基于Web的使用程序,我们将开辟一个复杂的剧本―samp_db―同意检察samp_db数据库中存在的表,并从Web扫瞄器中交互式地反省这些表中的内容。samp_db的事情体例以下:
当初次从扫瞄器中哀求samp_db时,它毗连到MySQL服务器,在samp_db数据库中检索一列表,并向扫瞄器发送一个页面,在这个页面中呈现的每一个表都作为可选择的毗连。中选择这个页面中的一个表名时,榔骶拖Web服务器发送一个哀求,哀求samp_browse显现谁人表的内容。
当挪用samp_browse时,假如它收到从Web服务器发来的一个表名,则它就检索这个表的内容,并将信息显现在Web扫瞄器上。数据每列的题目就是表中列的称号。题目作为毗连呈现;假如选择它们中的一个,则扫瞄器就向Web服务器发送一个哀求,显现一样的表,但按选择的列排序。
注重,这里有个告诫:samp_db表中的这些表绝对较小,因而向扫瞄器发送表的全体内容并非年夜成绩。假如编纂samp_db,显现包括年夜型表的分歧数据库中的表,则应当思索向行检索语句中增添一个LIMIT子句。
在samp_browse剧本的主体中,我们创立了CGI工具,并作废了Web页面的初始部分。然后反省是不是按我们的假定,依据tbl_name参数值显现了一些特定的表:
很简单找出参数的值,由于CGI.pm做了找出Web服务器传送给这个剧本信息的全体事情。我们只需挪用具有我们感乐趣的参数名的param(),在samp_browse的主体中,这个参数为tbl_name。假如它没有界说大概为空,则它就是这个剧本的初始挪用,我们显现这个表列。不然,就显现由tbl_name参数定名的表的内容,由sort_column参数定名的列值排序。显现得当的信息以后,我们挪用end_html()打消停止的HTML标记。
display_table_list()函数天生初始页面。display_table_list()检索这个表列并写出在每一个单位中都含有一个数据库表名的单列的HTML表:
display_table_list()天生的页面含有以下毗连:
当挪用samp_browse时,假如tbl_name参数有值,则这个剧本将这个值传送给display_table(),连同按称号排序后的列名。假如没有定名的列,则我们按第一列排序(我们能够经由过程地位援用列,因此很简单地利用ORDERBY1子句来完成):
表显现了与从头显现该表的毗连相干的列题目的页面;这些毗连包含sort_column参数,它显式地指定排序的列。比方,关于显现event表内容的页面,列题目毗连看起来以下所示:
display_table_list()和display_table()都利用了display_cell(),HTML表中作为单位显现值的有用程序函数。这个函数利用了一个小秘诀,就是将空值转换为不成分的空格(‘ ’),由于在带有边框的表中,空单位不会准确地显现边框。将不成分的空格放进这个单位中办理了这个成绩。display_cell()还具有把持是不是将单位值编码的第三个参数。这是必须的,由于挪用display_cell(),显现了一些已编码的单位值,如含有URL信息的列题目:
假如想编写更通用的剧本,能够将samp_browse变动为扫瞄多个数据库。比方,在剧本入手下手时,能够显现服务器上的一列数据库,而不是一个特定命据库中的一列表。然后,选出一个数据库,取得它的一列表,再从那边持续。
学分保留计划分数扫瞄器
每当我们输出测试的分数时,都必要天生一个有序的分数列表,以便断定品级曲线,并分派字母品级。请注重,关于这个列表我们将做的一切事变就是显现它,以便能断定每一个字母品级停止的地位。然后在前往给先生之前,在测试卷上标出品级分数。我们不在数据库中
持续纪录这个字母品级,由于在品级周期开端的品级取决于数字分数,而不是字母品级。再请注重,严厉地说,在创立检索分数的办法之前,就应当有一个输出分数的办法。我将输出分数的剧本一向保留到下一章。在这时代,在数据库中,我们已从初期的品级周期部分中
失掉了几组分数。即便没无方便的分数输出办法,我们也能够利用具有那些分数的剧本。
我们扫瞄分数的剧本score_browse与samp_browse有些相似,可是但愿检察给定测试或检验的分数这类更特定的方针。初始页面给出一列能够从当选择的大概的品级事务,同意用户选择它们中的任何一个,来检察与事务相干的分数。给定事务的分数依照高分在前的按次按分数排序,因而能够显现出了局,并用它断定品级曲线。
score_browse只必要反省一个参数event_id,检察是不是指定了特定事务。假如不是,则score_browse就显现event表中的行,以便用户能够选择个中的一个。不然,就显现与所选事务相干的分数:
利用哀求表列题目的列名,函数display_events()从event表中抽失信息,并以表格情势显现它。在每行的外部,显现event_id值,作为能够选择的毗连,以触发检索响应事务分数的查询。每一个事务的URL都只是到具有附加参数score_browse的路径,这个参数指定事务号码:
/cgi-bin/score_browse?event_id=number
display_events()函数编写以下:
当用户选择事务时,扫瞄器发送一个具有附加事务ID值的score_browse哀求。score_browse找到event_id参数集,并挪用display_scores()列出一切特定事务的分数。这个页面也显现了文本“ShowEventList”,作为前往初始页面的毗连,以便用户能很简单地前往事务列表页面。这个毗连的URL援用了score_browse剧本,但不指定event_id参数的任何值。display_scores()子程序以下所示:
display_scores()运转的查询与我们之前在第1章的1.4.8节中的“从多个表中检索信息”大节中开辟的申明怎样编写毗连的查询极其相似。在那一章中,我们哀求给定日期的分数,由于日期比事务的ID值更成心义。相反,当我们利用score_browse时,晓得了准确的事务ID。那不是由于我们依照事务ID思索(我们没有),而是由于剧本给了我们一列可从当选择的事务ID。能够看到这类范例的接口削减了懂得特定细节的必要。我们不用懂得事务的ID;只必要辨认出想要的事务。
汗青联盟配合乐趣的搜刮
samp_browse和score_browse剧本经由过程在初始页面给出一列选择而同意用户做出选择,谁人页面中的每一个选择都是用特定参数值再次挪用这个剧本的一个毗连。同意用户做出选择的别的一种办法是,将含有可编纂域的表格放在页面中。当可选局限没有束缚到一些简单断定的值的汇合时,这类办法加倍符合。我们的下一个剧本举例申明了哀求用户输出的这类办法。
在7.3节“运转DBI”中,我们为寻觅共享特定乐趣的汗青同盟成员机关了一个命令行剧本。但是,谁人剧本并非同盟成员已会见的剧本;同盟秘书必需运转这个剧本,然后把了局邮寄给哀求这个列表的成员。最好使这个征采功能更普遍,以便成员能够本人利用。编写Web剧本就是举行这类事的一种办法。
剧本interests把大批表格放到用户可以输出关头字的中央,然后搜刮member表,寻觅有满意前提的成员并显现了局。经由过程将通配符“%”加到关头字的两头,来实行这个搜刮,以便在interests列值的任何中央都能找到。
在每一个页面都显现关头字表格,以便用户能够当即输出新的搜刮,乃至在显现搜刮了局的页面中也能够。除此以外,还在关头字表格中显现后面页面的搜刮字符串,以便假如用户想要运转相似的搜刮,能够编纂这个字符串。如许就不用从头键进很多内容了:
剧本和本人交换信息与samp_browse或score_browse略有分歧。interest参数没有加到URL的开端,而表格中的信息由扫瞄器编码,并作为POST哀求的一部分发送进来。但是,CGI.pm使怎样发送信息成为不相干的;参数值仍旧经由过程挪用param()来取得。
完成搜刮和显现了局的函数以下所示,没有显现格局化项的函数format_html_entry(),由于它与gen_dir剧本中的不异:
MySQL对硬件的较低要求是其最大的优势之一,不过需要注意的是:内存越多越好,因为所有的重要数据存储都在内存中完成。 SQL语言是学习所有数据库产品的基础,无论你是做数据库管理还是做数据库开发都是这样。不过具体学习的侧重点要看你将来做哪一块,如果是做数据库管理(DBA),侧重点应该放在SQLServer的系统管理上. 连做梦都在想页面结构是怎么样的,绝非虚言 其中最有名的应该是row_number了。这个终于解决了用临时表生成序列号的历史,而且SQLServer2005的row_number比Oracle的更先进。因为它把Orderby集成到了一起,不用像Oracle那样还要用子查询进行封装。 一个百万级别的基本信息表A,一个百万级别的详细记录表B,A中有个身份证id,B中也有身份id;先要找出A中在B的详细记录。 这是一个不错的新特性。虽然索引的附加字段没有索引键值效率高,但是相对映射到数据表中效率还是提高了很多。我做过试验,在我的实验环境中会比映射到表中提高30%左右的效率。 groupby子句可以将查询结果分组,并返回行的汇总信息Oracle按照groupby子句中指定的表达式的值分组查询结果。 对于微软系列的东西除了一遍遍尝试还真没有太好的办法 SQLServer的异构移植功能个人感觉最好了。(如果对比过SQLServer的链接服务器和Oracle的透明网关的朋友会发现SQLServer的sp_addlinkedserver(openquery)异构数据库系列比Oracle真是强太多了。) 而写到本地,我又考虑到效率问题.大家来讨论讨论吧,分数不打紧,就给10分,十全十美,没啥对错,各抒己见,但是要有说服力的哦~ 这是一个不错的新特性。虽然索引的附加字段没有索引键值效率高,但是相对映射到数据表中效率还是提高了很多。我做过试验,在我的实验环境中会比映射到表中提高30%左右的效率。 外键的级联更能扩展可能大部分的同行在设计OLTP系统的时候都不愿意建立外键,都是通过程序来控制父子数据的完整性。
页:
[1]