文档章节

PHP数据库操作类ADODB 详解(二)

Amamatthew
 Amamatthew
发布于 2014/09/01 16:19
字数 4710
阅读 80
收藏 0



摘要:本文为PHP数据库操作类ADODB 详解<二>

1.GetAll方法

我们可以使用GetAll方法代替Execute()方法,该方法返回的结果为一个二维关联数据,这样可以使用foreach或for循环语句处理,非常方便。另外,GetAll取得的数组与Smarty模板的foreach配合得非常好。

我们一起看下面的脚本例子:

<?php
include_once("libs/adodb/adodb.inc.php");
// 创建一个mysql连接实例对象
$db = NewADOConnection("mysql");
// 打开一个数据库连接
$db->Connect("localhost", "root", "root", "library") or die("Unable to connect");
// 构造并执行一个查询
$query = "SELECT * FROM library";
$result = $db->GetAll($query) or die("Error in query: $query. " . $db->ErrorMsg());
// 清除无用的对象
$db->Close();
// 可以使用print_r打印该数组的内容
// print_r($result); exit(0);
// 遍历记录集,显示列的内容:TITLE 和AUTHOR
foreach ($result as $row){
   echo $row[1] . " - " . $row[2] . "\n";
}
// 取得和显示返回的记录行数
echo "\n[" . sizeof($result) . " 行记录被返回]\n";
?>

GetAll()方法取得记录集后,产生一个二维数组,类似于下面的样子:

Array
(
[0] => Array
(
[0] => 14
[id] => 14
[1] => Mystic River
[title] => Mystic River
[2] => Dennis Lehane
[author] => Dennis Lehane
)
[1] => Array
(
[0] => 15
[id] => 15
[1] => For Kicks
[title] => For Kicks
[2] => Dick Francis
[author] => Dick Francis
)
   //下略
)

我们在数组一章,提到过这类混合数组最适合用foreach来处理。这种方法是对Execute()方法的补充或替代,尤其适合在遍历查询整个表时使用。

另外,ADODB还提供取得一条记录的方法:GetOne()。

2.GetOne()方法

ADODB有个比较直接的方法可以比较方便地检测某条记录是否存在,那就是它的GetOne($sql)方法。

该方法返回查询记录的第1条第1个字段名的值,如果执行过程中出现错误,则返回布尔值false。

我们可以检测这个值是否存在:

<?php
Include_once("libs/adodb/adodb.inc.php");
// 创建一个mysql连接实例对象
$db = NewADOConnection("mysql");
// 打开一个数据库连接
$db->Connect("localhost", “root”, “passwd”, “adodb”) or die("Unable to connect!");
$rs = $db->GetOne("SELECT * FROM library WHERE id='$id'");
if($rs){
echo '记录存在';
}else {
   echo '记录不存在';
}
?>

不过这样有一个问题是,如果数据表中id=$id的记录有多条,不仅仅要知道是否存在有这样一条记录,还要把这条记录提取出来,则可以使用ADODB的GetRow()方法。

3.GetRow()方法

<?php
Include_once("libs/adodb/adodb.inc.php");
// 创建一个mysql连接实例对象
$db = NewADOConnection("mysql");
// 打开一个数据库连接
$db->Connect("localhost", “root”, “passwd”, “adodb”) or die("Unable to connect!");
$rs = $db->GetRow("SELECT * FROM library WHERE id='$id'");
if(is_array($rs)){
   echo '记录存在';
   print_r($rs);
} else {
   echo '记录不存在';
}
?>

需要注意的是,GetOne($sql) 和 GetRow($sql) 都能得到一条特定的记录,或者得到该记录不存在的信息,但是如果符合查询条件的记录存在多条时,则这两个方法只传回第一条记录,其他的都自动抛弃。

如果只要得到查询结果的行数,则可以使用结果集方法中的RecordCount()方法。

4.取得返回的记录行数

ADODB还提供了一批实用功能,如在进行查询时,提供了非常有用的RecordCount() 和FieldCount()方法,分别返回记录的数量和字段的数量,以下是应用这两个方法的例子。

<?php
include("libs/adodb/adodb.inc.php");
// 创建一个mysql连接实例对象
$db = NewADOConnection("mysql");
// 打开一个数据库连接
$db->Connect("localhost", “root”, “passwd”, “adodb”) or die("Unable to connect!");
// 构造并执行一个查询
$query = "SELECT * FROM library";
$result = $db->Execute($query) or die("Error in query: $query. " . $db->ErrorMsg());
// 取得和显示返回的记录行数
echo $result->RecordCount() . " 行记录被返回\n";
// 取得和显示返回的字段个数
echo $result->FieldCount() . " 个字段被返回\n";
// clea up
$db->Close();
?>

我们可以使用FetchField()方法取得字段的信息,其中含有该字段的详细资料,包括名称和类型等,请看如下的脚本例子。

<?php
include("libs/adodb/adodb.inc.php");
// 创建一个mysql连接实例对象
$db = NewADOConnection("mysql");
// 打开一个数据库连接
$db->Connect("localhost", "root", "passwd", "adodb") or die("Unable to connect!");
// 构造并执行一个查询
$query = "SELECT * FROM library";
$result = $db->Execute($query) or die("Error in query: $query. " . $db->ErrorMsg());
// 取得记录集中字段的结构信息
for($x=0; $x<$result->FieldCount(); $x++){
   print_r($result->FetchField($x));
}
// 清理无用的对象
$db->Close();
?>

下面输出的是有关id字段的结构信息。

stdClass myMagicbject

(

[name] => id

[table] => library

[def] =>

[max_length] => 3

[not_null] => 1

[primary_key] => 1

[multiple_key] => 0

[unique_key] => 0

[numeric] => 1

[blob] => 0

[type] => int

[unsigned] => 1

[zerofill] => 0

[binary] =>

)

 

5.其他相关方法

当执行一个INSERT查询时,如果该表的主键是一个自动增量的字段,则可以使用ADODB的insert_id()方法,来获得最后数据插入时自动产生的增量值。

<?php
include_once(“libs/adodb/adodb.inc.php”);
// 创建一个mysql连接实例对象
$db = NewADOConnection("mysql");
// 打开一个数据库连接
$db->Connect("localhost", “root”, “root”, “adodb”) or die("Unable to connect!");
// 构造并执行INSERT插入操作
$title = $db->qstr("PHP5与MySQL5 Web开发技术详解");
$author = $db->qstr("杜江");
$query = "INSERT INTO library (title, author) VALUES ($title, $author)";
$result = $db->Execute($query) or die("Error in query: $query. " . $db->ErrorMsg());
// 显示插入的记录号
if ($result){
   echo "最后插入的记录ID: " . $db->Insert_ID();
}
// 清理无用的对象
$db->Close();
?>

脚本中的qstr()方法,功能是过滤SQL查询中的非法字符。

执行后,即无论查询(SELECT)、删除(DELETE)或修改(UPDATE)数据,如果想知道是否对表有影响,可以使用affected_rows()方法,它可以告诉我们操作后有多少(记录)行受到了影响。请看下面的脚本例子:

<?php
include_once("libs/adodb/adodb.inc.php");
// 创建一个mysql连接实例对象
$db = NewADOConnection("mysql");
// 打开一个数据库连接
$db->Connect("localhost", “root”, “root”, “adodb”) or die("Unable to connect!");
// 构造并执行一个查询
$query = "DELETE FROM library WHERE author = 'J. Luser'";
$result = $db->Execute($query) or die("Error in query: $query. " . $db->ErrorMsg());
// 取得和显示执行后影响的记录行数
if ($result){
   echo $db->Affected_Rows() . " 行已被删除";
}
// 清理无用的对象
$db->Close();
?>

6.限制查询结果

上面 我们讨论了如何通过使用一个数据库库函数使应用程序更简洁,更易于移植。比如从MS SQL Server转移到MySQL,在MS SQL Server中使用指令“SELECT TOP 15 name FROM employee”取得数据的前15条,可在MySQL中却不支持这种写法,而要写成:SELECT name FROM employee LIMIT 15。

它似乎对我们敲响了警钟,应该停止在查询语句中使用非标准SQL指令,而去认真地学习标准的SQL。

幸运的是,ADODB有一个处理 LIMIT的方法:SelectLimit(),这样我们就根本不用管连接的是MySQL还是MS SQL Server,ADODB会在底层为我们自动转换,请见下面的脚本例子:

<?php
include_once("libs/adodb/adodb.inc.php");
// 创建一个mysql连接实例对象
$db = NewADOConnection("mysql");
// 打开一个数据库连接
$db->Connect("localhost", "root", "passwd", "adodb") or die("Unable to connect!");
// 构造并执行一个查询
// 我们要取得5行记录,从符合记录的第3行开始取
$query = "SELECT * FROM library";
$result = $db->SelectLimit($query, 5, 3) or die("Error in query: $query. " . $db->ErrorMsg());
// 遍历记录集
while (!$result->EOF) {
   echo $result->fields[1] . " - " . $result->fields[2] . "\n";
$result->MoveNext();
}
// 清理无用的对象
$db->Close();
?>

在这个例子中,selectlimit()方法类似于MySQL的LIMIT语句,可用于控制从某行开始查询,到某行的结果,从而取得我们指定的记录集。

我们可以利用ADODB提供的MetaDatabases()方法取得当前服务器中所有数据库的清单。还有一个方法和它很类似,即使用MetaTables()方法可以取得当前库中所有表的清单。请看下面的例子:

<?php
include(“libs/adodb/adodb.inc.php”);
// 创建一个mysql连接实例对象
$db = NewADOConnection("mysql");
// 打开一个数据库连接
$db->Connect("localhost", "root", "passwd", "adodb") or die("Unable to connect!");
// 取得数据列表
echo "数据库:\n";
foreach($db->MetaDatabases() as $d){
   echo "* $d\n";  
}
// 取得数据表清单
echo "\n当前数据库下的表:\n";
foreach($db->MetaTables() as $table){
   echo "* $table\n";  
}
// 清理无用的对象
$db->Close();
?>

7.快速存取

有时,我们需要对一些不同的值做一些特殊的查询,比如一系列的INSERT(插入)语句。ADODB类提供了两个方法,可以使我们既节约时间又节省系统的开销,请看如下示例:

<?php
include(“libs/adodb/adodb.inc.php”);
// 创建一个mysql连接实例对象
$db = NewADOConnection("mysql");
// 打开一个数据库连接
$db->Connect("localhost", "root", "passwd", "adodb") or die("Unable to connect!");
// 构造准备查询,使用参数绑定
$query = $db->Prepare("INSERT INTO library (title, author) VALUES (?, ?)");
// 从CSV 中取得要插入的标题和作者名称
$data = file("./book_list.csv");
// 遍历该文件,并执行插入操作
foreach ($data as $l){
   $arr = explode(",", $l);
   // 插入值并绑定准备语句
   $result = $db->Execute($query, array($arr[0], $arr[1])) or die("Error in query: $query. " . $db->ErrorMsg());
}
// 清理无用的对象
$db->Close;
?>

prepare()函数,把一个SQL查询作为参数,读取一个查询,但并不立即执行。prepare()返回一个句柄给一个prepare查询,当保存和传递给Execute()方法后,则立即执行该查询。

 

8.处理事务

处理事务是许多应用程序的一个重要的特征(比如,钱从你的账户转出,然后转入到某个人的账户中。只要其中任意一步操作失败,这整个过程都必须被认定为失败。不然,钱被划出,而没有进对方的账户;或者,钱没有划出,但对方账户无端多了一笔钱)。

处理事务可以在代码级上进行机警地管理控制。常数错误检查被用来判断执行COMMIT(事务的所有各项都正确,执行正确,结束事务)还是执行ROLLBACK(事务中有错误,所有改动需要恢复原来状况)。

现在的数据库系统绝大多数都支持事务,如MySQL、Oracle、MS SQL Server等,ADODB提供一个非常好的功能,能够让你更透明地使用这一特性。请看下面的例子:

<?php
include(“libs/adodb/adodb.inc.php”);
// 创建一个mysql连接实例对
$db = NewADOConnection("mysql");
// 打开一个数据库连接
$db->Connect("localhost", “root”, “root”, “adodb”) or die("Unable to connect!");
//关闭auto-commit自动提交事务
// 开始事务处理语句块
$db->BeginTrans();
// 第一次查询
$query = "INSERT INTO library (title, author) VALUES ('测试用书', '佚名')";
$result = $db->Execute($query) or die("Error in query: $query. " . $db->ErrorMsg());
//使用第一次查询返回的ID号
if ($result){
   $id = $db->Insert_ID();
   $query = "INSERT INTO purchase_info (id, price) VALUES ($id, 'RMB 31.9')";
   $result = $db->Execute($query)
or die("Error in query: $query. " . $db->ErrorMsg());
}
// 如果操作成功
if ($result){
   // 事务提交
   $db->CommitTrans();
}// 否则回滚
else{
   $db->RollbackTrans();
}
// 清理无用的对象
$db->Close;
?>

该脚本首先 需要关掉数据库的auto commit功能,通过begintrans()方法来处理,这种方法也标志着一个事务的开始。可以使用CommitTrans()或 RollbackTrans()函数来处理操作,一旦auto commit已经关掉,你就可以任意执行所需要的查询,确认事务的查询执行无误并完毕后,由我们自己决定何时执行commit操作。

每一 次执行Execute()事务块后,它会返回一个布尔值,告诉我们是否成功地执行了查询。可以跟踪到这个值,以及使用的时间,以决定是否要进行整个交易行 为。一旦你相信一切都没问题,则告诉数据库committrans()方法;如果发现有错误发生,则可以进行回滚操作——执行 rollbacktrans()方法。

值得注意的是,注意您的数据库类型是否支持这些事务函数,前面已经说过,MySQL的InnoDB类型表支持事务,但是MyISAM类型并不支持。

9.使用缓存查询

在一个动态页面中,如果其中的一个查询指令很少改变且频繁被执行,我们则可以使用ADODB的缓存功能,可以将查询指令的结果缓存成静态文件,从而提高PHP脚本的性能。

当试图通过缓存来提高你的应用程序的性能之前,建议先去优化查询指令再开始本操作,这样才会起到事半功倍之效果。

ADODB最棒的功能就是提供查询缓存的功能。缓存可以大大改善应用程序的性能,尤其是网站系统,因为大部分用户都是在浏览网站,数据库完成的任务多半是查询(SELECT操作)。为了更好地理解与应用缓存查询的功能,我们来看下面的脚本例子。

<?php

include_once("libs/adodb/adodb.inc.php");

// 创建一个mysql连接实例对象

$db = NewADOConnection("mysql");

// 打开一个数据库连接

$db->Connect("localhost", "root", "root", "adodb") or die("Unable to connect!");

// 构造并执行一个查询

$query = "SELECT * FROM library";

$result = $db->Execute($query) or die("Error in query: $query. " . $db->ErrorMsg());

// 遍历返回的记录集,显示列数据的内容 TITLE 和 AUTHOR

while (!$result->EOF) {

   echo $result->fields[1] . " - " . $result->fields[2] . "\n";

   $result->MoveNext();

}

// 显示取得的记录行数

echo "\n[" . $result->RecordCount() . " 行记录被返回]\n";

// 关闭数据库连接

$db->Close();

?>

这段代码使用ADODB进行一个SELECT操作。比如说,这就是您的网站,平均有每分钟5000次的点击(PV,Page View)量,那么数据库系统每小时至少要被查询3万次以上,可以想象,这对我们的MySQL数据库的负载是相当繁重的。

因此ADODB提供了缓存的功能,可以将经常查询的结果保存起来,进而降低数据库服务器的负荷,同时也向用户提供更快速的内容响应。

下面是修改上面的脚本,改为使用CacheExecute来进行缓存查询的示例:

<?php

include("libs/adodb/adodb.inc.php");

//设置缓存保存的路径,.表示当前目录

$ADODB_CACHE_DIR = '.'; //为了管理方便,实际开发环境请指向到独立的目录中,如/tmp/adodb

// 创建一个mysql连接实例对象

$db = NewADOConnection("mysql");

// 打开一个数据库连接

$db->Connect("localhost", "root", "passwd", "adodb") or die("Unable to connect!");

// 构造并执行一个查询

$query = "SELECT * FROM library";

$result = $db->CacheExecute(300,$query) or die("Error in query: $query. " . $db->ErrorMsg());

// 遍历返回的记录集,显示列数据的内容 TITLE 和 AUTHOR

while (!$result->EOF) {

   echo $result->fields[1] . " - " . $result->fields[2] . "\n";

   $result->MoveNext();

}

// 取得和显示返回的记录行数

echo "\n[" . $result->RecordCount() . " 行记录被返回]\n";

// 关闭数据库连接

$db->Close();

?>

CacheExecute()方法的第一个参数是缓存文件(缓存文件被命名为adodb_*.cache)将被保留的时间,以秒计时;第二个参数是SQL声明。第一个参数是可选择的,若没有限定时间,默认值是3600秒,也就是1个小时。

值得一提的是,使用CacheExcute()方法时,需要将php.ini中的参数magic_quotes_runtime设为0。

也可以根据需要,在程序运行时动态修改它的值:

set_magic_quotes_runtime(0);

注意:将上述代码放到调用数据库的指令之前,我们还可以在任何时候,通过调用CacheFlush()来清除过时的缓存。

10.生成下拉列表菜单

ADODB特意为Web开发任务提供几个通用的方法。其中,最有用的是GetMenu()方法,通过抽取数据库的记录集,自动地生成表单及菜单列表框。

下面介绍的就是从数据库动态构建下拉菜单(Option)的例子。

<html>

<head></head>

<body>

<?php

include_once("libs/adodb/adodb.inc.php");

// 创建一个mysql连接实例对象

$db = NewADOConnection("mysql");

// 打开一个数据库连接

$db->Connect("localhost", "root", "root", "library") or die("Unable to connect!");

// 构造并执行一个查询

$query = "SELECT title, id FROM library";

$result = $db->Execute($query) or die("Error in query: $query. " . $db->ErrorMsg());

//显示HTML下拉列表菜单

echo $result->GetMenu("library", '', false);

// 关闭数据库连接

$db->Close();

?>

</body>

</html>

GetMenu()方法需要传入参数,用来控制列表框的行为。上例中第一个参数是列表框的名字(这个例子为“library”);第二个参数是显示时默认的值,可以为空,从第一个记录开始;第三个参数,指定列表框的项目是否为空;第四个参数,控制是否允许用户多选。

上例的显示结果如下:

<select name="library" >

<option value="15">Mystic River</option>

<option value="16">Where Eagles Dare</option>

<option value="17">XML and PHP</option>

</select>

可以看到,该列表菜单内容是从library表抽取的记录,列表框的名字为“library”,在记录集中,ID是菜单选项的值,名称为菜单框显示的元素。

由此可以看出,GetMenu()方法可以大幅度简化Web开发任务,大大减少代码量。

11.输出到文件

ADODB还允许我们将记录输出为一个不同形式的文件:如逗号分隔符CSV文件,制表符表格,甚至于HTML形式的表格。

这些功能属于ADODB的附属功能,在使用时需要包含相关ADODB类文件,下面是样例的内容。

<?php

include("libs/adodb/adodb.inc.php");

// 包含转换方法的文件

include_once("libs/adodb/toexport.inc.php");

// 创建一个mysql连接实例对象

$db = NewADOConnection("mysql");

// 打开一个数据库连接

$db->Connect("localhost", "root", "passwd", "library") or die("Unable to connect!");

// 构造并执行一个查询

$query = "SELECT title, id FROM library";

$result = $db->Execute($query) or die("Error in query: $query. " . $db->ErrorMsg());

// 返回一个CSV字符串

echo rs2csv($result);

// 关闭数据库的连接

$db->Close();

?>

输出结果如下:

title,id

Mystic River,15

Where Eagles Dare,16

XML and PHP,17

我们也可以去除结果中第一行,即字段的名称,使用脚本格式如下:

// 返回一个 CSV 字符串

echo rs2csv($result, false);

脚本的输出结果将没有字段名称,如下:

Mystic River,15

Where Eagles Dare,16

XML and PHP,17

ADODB还提供生成制表符或分隔符文件功能,使用rs2tab()方法:

<?php

include("libs/adodb/adodb.inc.php");

// 包含转换方法的文件

include("toexport.inc.php");

//创建一个mysql连接实例对象

$db = NewADOConnection("mysql");

// 打开一个数据库连接

$db->Connect("localhost", "root", "root", "library") or die("Unable to connect!");

// 构造并执行一个查询

$query = "SELECT title, id FROM library";

$result = $db->Execute($query) or die("Error in query: $query. " . $db->ErrorMsg());

// 返回一个TAB制表符分隔的字符串

echo rs2tab($result);

// 关闭数据库连接

$db->Close();

?>

显示结果如下:

title   id

Mystic River   15

Where Eagles Dare   16

XML and PHP   17

ADODB还提供生成HTML表格的功能,使用rs2html()方法:

<html>

<head></head>

<body>

<?php

include_once(“libs/adodb/adodb.inc.php”);

// 包含转换方法的文件

include_once("libs/adodb/tohtml.inc.php");

// 创建一个mysql连接实例对象

$db = NewADOConnection("mysql");

// 打开一个数据库连接

$db->Connect("localhost", "root", "passwd", "library") or die("Unable to connect!");

// 构造并执行一个查询

$query = "SELECT title, id FROM library";

$result = $db->Execute($query) or die("Error in query: $query. " . $db->ErrorMsg());

// 返回一个HTML格式的表格

echo rs2html($result);

// 关闭数据库连接

$db->Close();

?>

</body>

</html>

输出结果如下:

本文转载自:http://jianxing0610.blog.163.com/blog/static/504693412010383255374/

Amamatthew
粉丝 67
博文 1050
码字总数 76443
作品 0
厦门
后端工程师
私信 提问
如何整理PHP分页代码,使其实现经典分页样式

请问一下,要如何让下面的代码可以让分页呈现出 首页 上一页 12345 下一页 尾页这种格式的,目前只是呈现出上一页 下一页 implemented Render_PageLinks(). Please note, this class is ent...

Binvor
2012/08/10
1K
1
tikiwiki 安装数据库默认latin1编码,中文在phpmyadmin乱码

修改libadodbdriversadodb-mysql.inc.php 在function connect以及function pconnect的 if ($argDatabasename) return $this->SelectDB($argDatabasename); 前加一行: @mysqlquery("SET NAM......

zhblue
2010/09/15
0
0
snort入侵检测系统做到最后一步,救急

我做snort入侵检测系统做到最后一步,需要配置acid,配置如下, $alert_dbname = "snort"; $alert_host = "localhost"; $alert_port = ""; $alert_user = "snort"; $alert_password = "1234......

adfdf
2009/12/14
1K
8
DaDaBIK 4.3 RC2 发布

DaDaBIK 是一个 PHP 应用,可轻松创建一个高可定制的数据库的Web前端,可对数据进行搜索、增删改操作;仅需要提供一些很简单的配置信息,支持的数据库包括 MySQL、PostgreSQL、Oracle 和 SQ...

红薯
2010/11/29
359
0
golang 操作mssql时取datetime类型数据的问题

开发环境:win7 数据库:sqlserver2008 驱动包:github.com/mattn/go-adodb 碰到的问题:从数据库中取datatime类型数据时,在go中读取的数据和数据库中存储的不一致。请问有其他的朋友碰到过这种...

吹牛老爹
2013/05/28
2.9K
5

没有更多内容

加载失败,请刷新页面

加载更多

Jenkins系列_插件安装及报错处理

进入Jenkins之后我们可以进行插件的安装,插件管理位于以下模块: 发现上面报了一堆错误,是因为插件的依赖没有安装好,那么这一节,就先把这些错误解决掉吧。解决完成后,也就基本会使用插件...

shzwork
今天
2
0
mysql mysql的所有查询语句和聚合函数(整理一下,忘记了可以随时看看)

查询所有字段 select * from 表名; 查询自定字段 select 字段名 from 表名; 查询指定数据 select * from 表名 where 条件; 带关键字IN的查询 select * from 表名 where 条件 [not] in(元素...

edison_kwok
昨天
9
0
多线程同时加载缓存实现

import com.google.common.cache.Cache;import com.google.common.cache.CacheBuilder;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorServi......

暗中观察
昨天
3
0
利用VisualVM 内存查看

准备工作,建几个测试类。等下就是要查看这几个类里面的属性 package visualvm;public class MultiObject { private String str; private int i; MultiObject(String str...

冷基
昨天
2
0
组装一台工作游戏两用机

一、配置清单如下: 分类 项目 价格(元) 主板 华硕(ASUS)TUF Z370-PLUS GAMING II 电竞特工 Z370二代 支持9代CPU 1049 CPU 英特尔(Intel) i7 8700K 酷睿六核 盒装CPU处理器 2640 风扇 九...

mbzhong
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部