Delphi中的SQL编程

2016-02-19 17:49 5 1 收藏

下面是个超简单的Delphi中的SQL编程教程,图老师小编精心挑选推荐,大家行行好,多给几个赞吧,小编吐血跪求~

【 tulaoshi.com - 编程语言 】

  SQL语言作为关系数据库管理系统中的一种通用的结构查询语言,已经被众多的数据库管理系统所采用,如ORACLE、Sybase、Informix等数据库管理系统,它们都支持SQL 语言。Delphi与使用SQL语言的数据库管理系统兼容,在使用Delphi开发数据库应用程序时,我们可以使用SQL语言编程,支持SQL编程是Delphi的一个重要特征,这也是体现Delphi作为一个强大的数据库应用开发工具的一个重要标志。 

  17.1 SQL语言简介 

  17.1.1 SQL的历史 

  在70年代初,E.E.Codd首先提出了关系模型。70年代中期,IBM公司在研制 SYSTEM R关系数据库管理系统中研制了SQL语言,最早的SQL语言(叫SEQUEL2)是在1976 年 11 月的IBM Journal of R&D上公布的。1979年ORACLE公司首先提供商用的SQL,IBM公司在DB2 和SQL/DS数据库系统中也实现了SQL。

  1986年10月,美国ANSI采用SQL作为关系数据库管理系统的标准语言(ANSI X3. 135-1986),后为国际标准化组织(ISO)采纳为国际标准。1989年,美国ANSI采纳在ANSI X3.135-1989报告中定义的关系数据库管理系统的SQL标准语言,称为ANSI SQL 89, 该标准替代ANSI X3.135-1986版本。该标准为下列组织所采纳:

  ● 国际标准化组织(ISO),为ISO 9075-1989报告Database Language SQL With Integrity Enhancement

  ● 美国联邦政府,发布在The Federal Information Processing Standard Publication(FIPS PUB)127

  目前,所有主要的关系数据库管理系统支持某些形式的SQL语言,大部分数据库打算遵守ANSI SQL89标准。 

  17.1.2 SQL的优点

  SQL广泛地被采用正说明了它的优点。它使全部用户,包括应用程序员、DBA管理员和终端用户受益非浅。

  (1) 非过程化语言

  SQL是一个非过程化的语言,因为它一次处理一个记录,对数据提供自动导航。SQL允许用户在高层的数据结构上工作,而不对单个记录进行操作,可操作记录集。所有SQL 语句接受集合作为输入,返回集合作为输出。SQL的集合特性允许一条SQL语句的结果作为另一条SQL语句的输入。

  SQL不要求用户指定对数据的存放方法。这种特性使用户更易集中精力于要得到的结果。所有SQL语句使用查询优化器,它是RDBMS的一部分,由它决定对指定数据存取的最快速度的手段。查询优化器知道存在什么索引,哪儿使用合适,而用户从不需要知道表是否有索引,表有什么类型的索引。

  (2) 统一的语言

  SQL可用于所有用户的DB活动模型,包括系统管理员、数据库管理员、应用程序员、决策支持系统人员及许多其它类型的终端用户。基本的SQL 命令只需很少时间就能学会,最高级的命令在几天内便可掌握。

  SQL为许多任务提供了命令,包括:

  ● 查询数据

  ● 在表中插入、修改和删除记录

  ● 建立、修改和删除数据对象

  ● 控制对数据和数据对象的存取

  ● 保证数据库一致性和完整性

  以前的数据库管理系统为上述各类操作提供单独的语言,而SQL 将全部任务统一在一种语言中。

  (3) 是所有关系数据库的公共语言

  由于所有主要的关系数据库管理系统都支持SQL语言,用户可将使用SQL的技能从一个RDBMS转到另一个。所有用SQL编写的程序都是可以移植的。

  17.2 TQuery部件在SQL编程中的运用

  在Delphi中是通过TQuery部件来实现对SQL语言支持的,也就是说用Delphi 开发数据库应用程序时,使用SQL语言操作数据库中的数据的唯一途径是经过TQuery部件。 TQuery部件在Delphi中使用SQL语言编程时占居着绝对重要的地位。在使用Delphi 开发的数据库应用中,可以使用SQL语言访问下列三个方面的数据库:

  ● Paradox或dBASE数据库中的表

  在访问这些桌面数据库系统中的数据时,只能使用ANSI标准的SQL语言中的部分SQL 语句,它们主要包括:Select、Insert、Update和Delete语句;即本地SQL语句。有关详细情况请参见附录局部SQL语句的使用。

  ● 本地InterBase数据库服务器中的数据库

  在InterBase数据库中支持的SQL语句, 在Delphi中都可以使用。有关InterBase中SQL语句的语法和限制,请参看InterBase的语言参考。

  ● 远程数据库服务器中的数据库

  当然这要求在Delphi中必须安装相应的SQL Link。只要是数据库服务器上的DBMS支持的SQL语句,在Delphi中都可以使用。有关语法及限制请参看相关的数据库管理 系统的文档。

  值得一提的是,Delphi还支持异构查询,即可以同时查询多个数据库服务器中相同的或不同类型的数据库表,例如查询的数据可以是来自ORACLE数据库中的表和Sybase数据库中的表或者其它多个数据库中的表。

  17.2.1 TQuery部件的使用

  TQuery部件是一个数据集部件,它在Delphi部件选择板上的数据访问页(Data Access)上,它与TTable部件具有很多共同的特性,我们在第十五章数据访问部件的应用及编程中较详细地进行了介绍。 TQuery 部件在 SQL 编程中占居了十分重要的地位。 它实现了Delphi对SQL语言的支持,在Delphi开发的数据库应用中,SQL语句是通过TQuery部件传递到要访问的数据库系统的数据库引擎中,由数据库引擎具体执行SQL语句,以实现对数据的操作,而不是传递给Delphi中的BDE,由BDE实施具体的SQL动作。

  我们已经知道了TTable部件在访问数据库时已经具备很强大的功能。TTable部件通过Delphi内置的BDE可以实现对各种数据库系统的访问,然而TQuery部件提供了一些 TTable部件不具备的功能,它们是:

  ●多表联接查询

  ●复杂的嵌套查询(Select中包含着Select子查询)

  ●明确需要按SQL语言进行的操作

  因为TTabel部件不能使用SQL语言,而在TQuery部件可以使用SQL语言,因而TQuery部件也就具备了强大的关系查询能力。当然这也使数据库应用程序本身变得更复杂了。

  在Delphi应用程序中编写和使用的SQL语句有两种:即静态SQL语句、动态SQL 语句。静态SQL语句是在程序设计阶段,将SQL命令文本作为TQuery部件的SQL属性值设置。而动态SQL语句编程是SQL语句中包含一系列的参数,在程序运行过程中各参数值是可变的,即可以动态地给SQL语句中的参数赋值。

  静态方式是把SQL命令文作为TQuery部件的SQL属性值进行设置,这样,当执行应用程序时,Delphi便执行TQuery部件SQL属性中设置的SQL命令。如果是SQL中的查询命令,把TQuery部件通过TDataSource部件与数据控制部件相连,查询的结果将会显示在与 TQuery部件相连接的数据浏览部件中。动态SQL语句是指SQL语句中包含一些参数变量,在程序中可以为这些参数赋值,在程序运行过程中,各个参数值是变化的。TQuery部件的SQL 属性中的SQL语句的编写也有两种方法,一种方法是在程序设置阶段便将相应的SQL语句写入到TQuery的SQL属性中,另一种方法是在Delphi开发的应用程序将SQL语句,包含在Pascal代码单元中。

  在SQL编程中使用TQuery部件的具体方法步骤如下:

  ①为TQuery部件设置DatabaseName属性,它可以是用BDE建立的数据库的别名,或桌面数据库系统中的目录名或数据库服务器中的文件名,如果在应用中使用了TDatabase 部件,那么TQuery部件的DatabaseName属性可以是TDatabase部件中定义的一个数据库别名。详细情况请参看TDatabase部件的使用;

  ②为TQuery部件设置SQL属性,TQuery部件的SQL属性值就是应用程序要执行的SQL 命令文本,设置SQL属性有两种方法:

  ● 在程序设计过程中,我们可以通过对象浏览器(Object Inspector)编辑SQL属性在Object Inspector中选择SQL属性,这样会打开String List Editor窗口,在其中我们便可以编写SQL命令,我还可以打开Visual Query Builder来编写SQL命令 (只有Delphi的客户/服务器版本才具有这一工具)。

  ● 将SQL命令包含在Pascal代码单元中

  在程序运行过程中,首先调用TQuery部件的Close方法关闭当前的TQuery部件,然后调用Clear方法清除SQL属性,并说明新的SQL命令文本,然后调用Add方法,将新的SQL命令文本加入到SQL属性中。

  ③通过调用TQuery部件的Open方法或ExecSQl方法执行 SQL 命令。 Open 方法只执行Select命令,ExecSQL方法还可以执行其它的SQL命令。Open方法和ExecSQL 方法的区别我们在后面的章节里会进一步地加以讨论的。

  如果使用动态SQL语句,首先调用prepare方法,给动态SQL语句中的参数赋值,然后再调用Open方法或ExecSQL方法。调用propare 方法并不是必须的,但是对于要多次执行TQuery部件中SQL属性中的动态SQL语句,调用Prepare可以大大提高TQuery部件执行SQL语句的性能。 

  17.2.2 在TQuery部件中编写简单的SQL查询命令 

  在这一节里我们将学习如何使用TQuery部件编写简单的SQL查询命令,并在Delphi 应用程序中实现SQL查询。

  例如,如果我们想查询出表Customer.DB中客户的编号和公司名称,我们按下列步骤来实现:

  ①在应用窗体中放置一个TQuery部件、一个TDataSource部件一个TDataGrid部件,并将它们连接起来 

  ②设置窗体TQuery 部件Query1的DatabaseName属性值为DBDEMOS

  ③双击Object Inspector窗口中Query1的SQL 属性, Delphi 将显示 String List Editor窗口。

  ④在图17.3中的窗口中输入SQL语句:

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  Select CustNo,Company From Custormer;

  ⑤单击OK按钮,关闭String List Editor窗口。

  ⑥设置Query的Open属性为True。

  17.3 SQL语言编程概述 

  在Delphi应用程序中的SQL命令语句是包含在TQuery部件的SQL属性中,TQuery部件的SQL属性是TString类型的,也就是说SQL属性值是一个字符串列表,这个字符串列表非常类似于一个字符串类型的数组,有关TString类型的信息请参看联机帮助。在前一节里我们介绍了TQuery部件可以执行两种SQL语句:

  ● 静态SQL语句

  ● 动态SQL语句 

  静态SQL语句在程序设计时便已固定下来,它不包含任何参数和变量,例如下面的语句便是一条静态SQL语句: 

  Select * From Cusromer Where CustNo = 1234; 

  而动态SQL语句,也被称作参数化的语句,在其中间包含着表示字段名或表名的参数,例如下面的语句是一条动态SQL语句: 

  Select * From Customer Where CustNo =: Number;

  其中的变量Number便是一个参数变量,它由一个冒号引导,在程序运行过程中,必须要为该参数赋值,该条SQL语句才能正确执行,每次运行应用程序时可以为该参数变量赋予不同的值。

  17.3.1 SQL命令文本的编写

  1. 使用String List Editor编写

  我们要为TQuery部件的SQL属性设置SQL命令文本时,可以在应用窗体中选择TQuery部件且双击Object Inspector窗口中的SQL属性,这样便打开了String List Editor 窗口,在该窗口中我们便可以编写各种SQL命令,如图17.3所示。

  在编写完适当的SQL语句之后,选择 OK 按钮便可以将编辑器中的 SQL 命令文装入到TQuery部件的SQL属性中,选择SAVE按钮可以将编写好的SQL命令保存到一个文件中供以后编程时使用。我们在编写SQL命令文本时还可以选择Load按钮从一个 SQL 命令文件中调入SQL命令。在程序运行过程中,要想设置TQuery部件的SQL属性,必须首先调用Close方法,关闭TQuery部件,然后再调用Clear方法清除SQL属性中现存的SQL命令语句,最后再调用Add方法为SQL属性设置新的SQL命令语句。例如:

  

Query1.Close {关闭Query1)Query1.SQL.Clear {清除SQL属性中的SQL命令语句}Query1.SQL.Add('Select * From Country');Query1.SQL.Add('Where Name ="ARGENTINA" ');

  在为TQuery部件设置SQL属性时调用Close方法总是很安全的,如果TQuery部件已经被关闭了,调用Close方法时不会产生任何影响。在应用程序中为SQL属性设置新的SQL 命令语句时,必须要调用Clear方法以清除SQL属性中现存的SQL命令语句,如果不调用Clear方法,便调用Add方法向SQL属性中设置SQL命令语句,那么新设置的SQL命令语句会追加在现存SQL命令语句后面,在程序运行时常常会出现出乎意料的查询结果甚至程序无法运行下去。

  在这里要特别注意的,一般情况下TQuery部件的SQL属性只能包含一条完整的SQL语句,它不允许被设置成多条SQL语句。当然有些数据库服务器也支持在TQuery部件的SQL属性中设置多条SQL语句,只要数据库服务器允许这样,我们在编程时可以为 SQL 属性设置多条SQL语句。

  2. 使用Visual Query Builder编写

  客户/服务器版本的Delphi还包含一个可视化的查询构造器Visual Query Builder ,用这个可视化的工具我们只能编写Select语句。在应用程序窗体中选择TQuery部件后,单击鼠标右键,弹出一个弹出式菜单,从中选择Run Visual Query Builder后便会弹出一对话框提示你选择要访问的数据库,选择想要访问的数据库之后选择OK按钮,紧接着会出现一个弹出式对话框提示你选择要查询的数据库表,一次可以选择多个数据库表,若要选择多个数据库表,每选择一个表之后单击Add按钮,接着选择另一个表,选择完要查询的表之后单击Close按钮,这样,可视化的查询构造器中将会显示出用户选择的数据库表。  

  表17.1 TQuery部件返回查询结果数据的类型

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  RequestLive属性值 CanModify属性值 查询结果的类型

  ────────────────────────────────

  False False 只读数据

  True(SQL语句满足约束条件) True 活动数据

  True(SQL语句不满足约束条件) False 只读数据

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  当TQuery部件返回只读的查询结果数据集,而用户又希望修改这只读的数据集时,一般这样来处理,在应用程序中另外增加一个 TQuery 部件 Query2( 假设获得只读结果的TQuery部件的名字是Query1),在Query2中设置修改语句UpDATE对Query1 中的数据进行修改操作,这样会实现对只读数据的修改。

  17.4 动态SQL语句的编程

  在17.3节中,我们已经介绍了动态SQL语句(又被称为参数化的SQL语句),在其中包含在程序过程中可以变化的参数,在实际的程序设计中使用得更多的是动态SQL语句,因而在这一节里我们重点介绍如何给动态SQL语句的参数赋值,以在应用程序中灵活地使用SQL语句。动态SQL语句的编写、执行等等与17.3节中介绍的SQL语句的编写、执行是一样的。

  动态SQL语句中的参数,我们可以通过两种途径来为它赋值:

  1. 利用参数编辑器(Parameter Editor)来为参数赋值

  具体方法是:选中TQuery部件,单击鼠标右键,然后从中选择Define Parameters 便可以打开参数编辑器。 

  例如,在TQuery部件的SQL属性中我们设置如下的SQL语句: 

  Setect * From Customer Where CustNO=:Number;

  TQuery的DatabaseName属性为DBDEMOS,其中Number为参数变量。我们便可以为参数Number赋值,在Datetype组合框中选择该参数的数据类型为整数Integer,在Value编辑框中可以为参数Number赋一个值,也可以单击Null Value检查框为参数Number赋一个空值Null。给参数赋值之后,单击OK按钮,这样TQuery部件中的SQL 查询便准备好了,而且参数值也被赋给了动态SQL语句中相应的参数,此时当把TQuery 部件的Active属性设置成True时,在与TQuery部件相连的数据浏览部件中会显示出查询结果,通过参数编辑器为参数赋值,这种方式缺乏应有的灵活性,在实际应用中用得较少,在实际应用中程序设计人员希望用更灵活方便的方式为参数赋值,那就是我们接下来要介绍的另一种途径:

  2. 在运行过程中,通过程序为参数赋值

  用这种方式为参数赋值有三种方法:

  ①根据参数在SQL语句中出现的顺序,设置TQuery部件的Params属性值为参数赋值。

  ②直接根据SQL语句中各参数的名字,调用ParamByName方法来为各参数赋值。

  ③将TQuery部件的DataSource属性设置为另一个数据源,这样将另一个数据源中与当前TQuery部件的SQL语句中的参数名相匹配的字段值赋给其对应的参数。

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  这三种方法我们将在下面的三小节中具体地介绍

  17.4.1 使用Params属性为参数赋值

  TQuery部件具有一个Params属性,它们在设计时不可用,在程序运行过程中可用,并且是动态建立的,当为TQuery部件编写动态SQL 语句时, Delphi 会自动地建立一个数组Params,数组Params是以0下标开始的,依次对应动态SQL 语句中的参数, 也就是说动态SQL语句中第一个参数对应Params[0],第二个参数对应params[1],依此类推。

  例如:一个TQuery部件Query1,我们为它编写的动态SQL语句是:

  Insert Into Customer(CustNo,Name,Country)

  Values(:CustNo,:Name, : Country)

  对于上述这条动态SQL语句中的参数,我们可以利用TQuery部件的params 属性为参数赋值:

  Query1.params[0].AsString := "1988";

  Query1.params[1].AsString := "Lichtenstein";

  Query1.params[2].AsString := "USA";

  上述语句将把"1988"赋给参数:Cuse_No,"Lichtenstein"赋给参数:Name,"USA"赋给参数:Country。

  17.4.2 使用ParamByName方法为参数赋值

  ParamByName是一个函数,用动态SQL语句中的参数作为调用ParamByName函数的参数,这样便可以为它们赋值,使用这种赋值方法,必须要知道动态SQL语句参数的名字。

  例如在17.4.1节中的例子中,也可以用下述方法给参数赋值:

  Query1.ParamByName('CustNo').AsString := "1988";

  Query1.ParamByName('Name').AsString := "Lichtenstein";

  Query1.ParamByName('Country').AsString := "USA";

  使用这种方法同样可以为各参数赋值,而且更加直观一些。

  17.4.3 使用Datasource属性为参数赋值

  上述两种方法的共同特点是:我们在为各参数赋值时,我们是知道各参数对应的具体参数值的。而在具体的应用程序中,有些参数值常常是无法确定的,例如参数值来自于另一个查询结果,对于这种情况,Delphi提供了使用Datasource属性为动态SQL 语句中尚存在没有赋值的参数时, Delphi 会自动检查 TQuery 部件的 Datasource 属性, 如果为Datasource属性设置了属性值(该属性的值是另一个TDatasource部件的名字),Delphi 会把没有赋值的参数与TDatasource部件中的各字段比较,Delphi 会将相应的字段值赋给与其相匹配的参数,利用这种方法也能实现所谓的连接查询,我们在学习使用TTable部件时,便会创建主要--明细型数据库应用,用TQuery部件创建的连接查询与主要- -明细型应用是相似的。

  例如:在如图17.7所示的应用中,设置了下列部件:

  ● 一个TTable部件

  名字为Cust,它的DatabaseName属性为DEMOS,TableName属性为Customer。

  ● 一个TDatasource部件

  名字为Custsource,其Dataset属性被设置为Cust。

  ● 一个TQuery部件

  名字为ORDERS,其DatabaseName被设置为DEMOS,SQL属性值为:

  Select Orders.CustNo,Orders.OrderNo,Orders.SaleDate FROM Orders

  WHERE Orders.CustNo =: CustNo

  ORDERS的DataSouce属性被设置为CustSource

  ● 一个TDatasource部件

  名字为OrderSource,其DataSet属性被设置为Orders。

  ● 两个TDBGrid部件

  它们分别连接CustSource和OrderSource。

  TQuery部件Orders中的动态SQL语句中的参数:CustNo在程序设计过程中没有给它赋值,当该应用程序运行时Delphi会自动地到其Datasource属性中说明的数据源CustSource中查找与参数:CustNo匹配的字段,而CustSource中正好有一个名字为 CustNo 的字段与参数:CustNo匹配,这样Customer表中的CustNo字段值被赋给了参数 : CustNo , 而当每移动Customer表中的记录指针,参数:CustNo的值会随之改变,而参数:CustNo的值发生改变时,Orders中的动态SQL语句会根据新的参数值重新查询,从数据库表中获取相应的订单数据,这样也变实现了类似于主要--明细型应用。即连接查询。 

  17.4.4 Prepare方法的使用 

  在使用动态SQL语句编程时,常常用到一个很重要的方法prepare,调用prepare 方法之后,Delphi会将带参数的SQL语句传送给与其对应的数据库引擎,对动态SQL语句进行语法分析和优化。虽然在用动态SQL语句编程时,调用prepare方法并不是必须的,但是这里我们要极力推荐调用prepare方法,因为调用prepare方法后,会大大提高动态SQL 语句的执行性能,特别是当要反复多次执行同一条动态SQL语句时,其优越性会更加明显。如果在应用程序中执行一条SQL语句之前并没有显式地调用prepare方法,每次在执行SQL 语句时,Delphi会隐含地调用propare方法以准备这个查询。

  TQuery部件还有一个prepare属性,这是一个布尔型属性,当其属性值为True时,表明该查询已被准备好了( SQL 语句已被传送到数据库引擎中 ) ,当我们使用参数编辑器Parameters Editor来为动态SQL语句中的参数赋值时,当设置完相应的参数值并退出参数编辑器时,Delphi会隐含地调用prepare方法以准备好查询。

  当SQL语句执行完之后,要想准备下一个查询,首先必须调用close方法,然后才能调用prepare方法准备下一个查询。一般来说,在一个应用程序中应该调用一次prepare方法,常常在窗体的OnCreate事件处理过程中调用prepare方法,然后用上述介绍的方法为参数赋值,最后调用Open方法或ExecSQL方法执行SQL语句,以完成查询。

  当然在调用prepare方法准备好一个查询时,会消耗一些数据库资源,因而每当一个查询执行完毕之后,要养成调用Unprepare方法以撤消查询的好习惯。在运行程序过程中,通过程序改变TQuery部件的SQL属性值时,Delphi会自动地调用Close方法和Unprepare 方法,以撤消查询。

来源:https://www.tulaoshi.com/n/20160219/1616144.html

延伸阅读
标签: Delphi
  SQL语言作为关系数据库管理系统中的一种通用的结构查询语言,已经被众多的数据库管理系统所采用,如ORACLE、Sybase、Informix等数据库管理系统,它们都支持SQL 语言。Delphi与使用SQL语言的数据库管理系统兼容,在使用Delphi开发数据库应用程序时,我们可以使用SQL语言编程,支持SQL编程是Delphi的一个重要特征,这也是体现Delphi作...
◇[DELPHI]网络邻居复制文件 uses shellapi; copyfile(pchar('newfile.txt'),pchar('//computername/direction/targer.txt'),false); ◇[DELPHI]产生鼠标拖动效果 通过MouseMove事件、DragOver事件、EndDrag事件实现,例如在PANEL上的LABEL: var xpanel,ypanel,xlabel,ylabel:integer; PAN...
标签: Delphi
  Delphi作为一门新起的Windows编程语言,由于其集众多的优秀特性于一身,因而越来越得到广大编程人员和发烧友的青睐。以下十则技巧涉及的面比较广泛,希望能够对Delphi的爱好者有所裨益。 1.类似于vb.中的doevents功能。 大家或许发现,在Delphi中没有类似于vb.中的doevents函数,这样有的时候,我们将无法使Windows响应...
标签: Delphi
  17.5 SQL编程实例 我们在学习了SQL程序的编写方法之后,我们便可以着手创建自己的应用程序了,通过创建应用程序我们对Delphi的强大功能就会有更深刻的印象,同时会进一步全面掌握有关SQL编程的知识,在本节中我们主要介绍两个例子,前一个例子主要是用静态的SQL语句编程,后一个例子是用动态SQL语句编程。 17.5.1 设计简单的S...
标签: Delphi
在Delphi中涉及到系统编程的方面毫无例外都要调用API函数,在ShellAPI.pas单元中有要用到的API函数的原型。 实战演练: 一.新建一个应用程序:File->New Applicaton 在Interface部分定义一个消息常量:const WM_NID=WM_USER+1000; 系统规定从WM_USER开始为用户自定义消息。 二.定义一个全局变量: NotifyIcon:TNotifyIc...

经验教程

863

收藏

86
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部