PDO 用法学习「建议收藏」

PDO 用法学习「建议收藏」PDO:phpdataobject 数据库访问抽象层 基于驱动: 1、安装扩展php_pdo.dll 2、安装驱动php_pdo_mysql.dlllinux编译时参数:–with-pd

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

 

PDO: php data object
数据库访问抽象层

基于驱动:
1、安装扩展 php_pdo.dll
2、安装驱动 php_pdo_mysql.dll

linux 编译时参数:–with-pdo=mysql=/usr/local/mysql

三个类:
PDO类, 数据库连接有关(连接、执行sql)
PDOStatement 处理结果集
PDOException 异常处理类
一些常量

相比mysqli方法少,常量多

dsn:data source name(数据源) 包括 主机、库名、驱动名
#部分参数可以省略,参数没有顺序
mysql: #最短 会自动调取 php.in 中 mysql.default_host 参数信息

dsn示例:

mysql:host=localhost;port=3307;dbname=testdb
mysql:unix_socket=/tmp/mysql.sock;dbname=testdb

获取pdo对象

 

$dsn = "mysql:host=127.0.0.1;port=3306;dbname=test";
$opts = array(PDO::ATTR_AUTOCOMMIT=>0, PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION, PDO::ATTR_AUTOCOMMIT=>0 #关闭自动提交, PDO::ATTR_TIMEOUT => 3 #设置超时时间);
$pdo = new PDO($dsn, 'root', '', $opts);
ee($pdo->getAttribute(PDO::ATTR_AUTOCOMMIT)); // setAttribute 可以设置属性
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);  #设置获取的方式 

 

执行sql语句: exec() 、query()、 perpare();

1、query用来处理有结果集的,如select, 返回 PDOStatement 对象,失败返回false(当为 PDO::ERRMODE_SILENT,这也是默认的值)
2、exec用来处理有返回影响行数的(int),如  insert(插入的行数)、 delete(删除的行数) 、update(和原数值不等才算), 失败返回false (当为 PDO::ERRMODE_SILENT,这也是默认的值)

3、prepare 执行所有sql,可以完全替代 query,exec的功能

错误报告是针对执行的sql出错时

PDO::ERRMODE_SILENT(0) :默认 不提示任何错误 ,连接时无论如何都会提示,只有在执行后面的方法时才会起作用
PDO::ERRMODE_WARNING(1) : 警告
PDO::ERRMODE_EXCEPTION(2):异常(推荐使用) 用try catch捕获,也可以手动抛出异常 new PDOException($message, $code, $previous)

 

#exec用法
try {
    $sql = "insert into limove(`id`, `name`, `order`) values(null, 'sjk', 1),(null, 'sjk',2)";
    $rows = $pdo->exec($sql);  //影响的条数 2
    $pdo->lastInsertId(); //最后插入的id,有多条时返回的是第一条的id
} catch (Exception $e) {
    ee($pdo->errorInfo());
}

 

#query方法同样也可以执行insert,delete 只是返回的结果集的格式
#同样 lastInsertId 照样也可以使用
$params = array (
                PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'' ,
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        );
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test;port=3306', 'root', '',$params);


$statement = "insert into card (id, json_str, f) values(null, 'ok', 2.3),(null, 'ok', 2.3)";

$flag = $pdo->query($statement);
ee($flag);

ee($pdo->lastInsertId());

PDOStatement Object
(
    [queryString] => insert into card (id, json_str, f) values(null, 'ok', 2.3),(null, 'ok', 2.3)
)

 

#query可以实现所有exec的功能
$params = array (
                PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'' ,
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        );
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test;port=3306', 'root', '',$params);


$statement = "insert into card. (id, json_str, f) values(null, 'ok', 2.3),(null, 'ok', 2.3)";
$statement = "select * from card";
$statement = "delete  from card where id = 625";
$statement = "update card set f=5 where id = 624";

$stmt = $pdo->query($statement); ee($stmt->rowCount());

 

 总结:

  1、query和exec都可以执行所有的sql语句,只是返回值不同而已。

  2、query可以实现所有exec的功能。

  3、当把select语句应用到 exec 时,总是返回 0

 注意:批量插入时,依次插入当遇到错误时后面的插入失败,但是前面的会插入成功。

预处理语句(prepare)示例,sql只编译一次,执行相同的sql效率会高。单个相比exec,query效率也高。

 

#prepare 在不恰当的位置调用用法可能会出异常

$params = array (
                PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'' ,
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        );
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test;port=3306', 'root', '',$params);
 

// $sql = "insert into card (id, json_str, f) values(null, 'ok', 2.3),(null, 'ok', 2.3)";
// $sql = "select * from card";
$sql = "delete  from card where id = 625";
// $sql = "update card set f=5 where id = 624";
// $sql = "show create table card";
// $sql = "desc card";

// $stmt = $pdo->query($sql);
// ee($stmt->fetchAll());
// ee($stmt);

$stmt = $pdo->prepare($sql);
$stmt->execute();
 ee($stmt->rowCount());
ee($stmt->fetch(pdo::FETCH_ASSOC));


Fatal error:  Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error' in E:\wamp\www\test\song.php:27
Stack trace:
#0 E:\wamp\www\test\song.php(27): PDOStatement->fetch(2)
#1 {main}
  thrown in E:\wamp\www\test\song.php on line 27

 

 

$dsn = "mysql:host=127.0.0.1;port=3306;dbname=test";
$opts = array(PDO::ATTR_AUTOCOMMIT=>0, PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION, PDO::ATTR_AUTOCOMMIT=>0);
try {
    $pdo = new PDO($dsn, 'root', '', $opts);
}catch(PDOException $e){
    echo $e->getMessage();
}


/* pdo中有两种占位符号
 *
*  ? 参数             --- 索引数组, 按索引顺序使用
*  名子参数           ----关联数组, 按名称使用,和顺序无关
*/


//准备好了一条语句,并入到服务器端,也已经编译过来了,就差为它分配数据过来
//同样适用于更新操作
$stmt=$pdo->prepare("insert into limove(`name`, `order`) values(:name,:order)");

//绑定参数,引用方式传递
$stmt->bindParam(":name", $name);
$stmt->bindParam(":order", $order);


// $stmt=$pdo->prepare("insert into limove(`name`, `order`) values(?, ?)"); //所有SQL都可执行

// //绑定参数,引用方式传递
// $stmt->bindParam(1, $name, PDO::PARAM_STR); #起始值为 1 
// $stmt->bindParam(2, $order, PDO::PARAM_INT);

#变量放到 bindParam 前后都可
$name="wwww";
$order = 1;

if($stmt->execute()){
    echo "执行成功";
    echo "最后插入的ID:".$pdo->lastInsertId();
}else{
    echo "执行失败!";
}

 

还支持执行时绑定

    #无序方式
    $stmt=$pdo->prepare("insert into shops(name, price, num, desn) values(:name,:price, :num, :desn)"); 
    $stmt->execute(array(":price"=>77, ":name"=>"kkk3", ":num"=>"453", ":desn"=>"aaaaaa3"));
 $stmt->execute(array(":price"=>77, ":name"=>"kkk3", ":num"=>"453", ":desn"=>"aaaaaa3"));

    #有序方式
    $stmt=$pdo->prepare("insert into shops(name, price, num, desn) values(?, ?, ?, ?)"); //所有SQL都可执行
    $stmt->execute(array("myname1", 11.2, 55, "very good"));
    $stmt->execute(array("myname1", 11.2, 55, "very good"));

 

 

获取结果

//获取结果
$stmt = $pdo->prepare("select * from limove where `order` = :order");
$stmt->execute(array(':order'=>8));

//设置获取的方式
$stmt->setFetchMode(PDO::FETCH_ASSOC);

$data = array();

//方式1
//$data = $stmt->fetchAll();

//方式2
while($row = $stmt->fetch(PDO::FETCH_ASSOC))
{
    $data[] = $row;
}
ee($data);

 

//bindColumn 把取出的值绑定到一个变量上
$stmt->bindColumn('name', $name);
$stmt->bindColumn(2, $sex); # 还可以根据顺序绑定,可以混合使用

 

#所有的条数,select insert delete update
$stmp->rowCount();

 

 

 

事务举例:

<?php
    try{
        $pdo=new PDO("mysql:host=localhost;dbname=xsphpdb", "root", "123456", array(PDO::ATTR_AUTOCOMMIT=>0)); #一定要关闭自动提交
        $pdo->setAttribute(PDO::ATTR_ERRMODE,  PDO::ERRMODE_EXCEPTION); #开启异常模式
    }catch(PDOException $e){
        echo "数据库连接失败:".$e->getMessage();
        exit;
    }

    //执行SQL语句 exec() query()  prepare()
    //一是有结果集的query(), 执行select语句
    //exec()用来执行有影响行数的,update, delete insert, other
    //exec()返回的是影响的行数
    /*
     *
     * 事务处理
     *
     *   张三从李四那里买了一台 2000 元的电脑
     *
     *     从张三帐号中扣出 2000元
     *
     *     向李四账号中加入 2000元
     *
     *     从商品表中减少一台电脑
     *
     *     MyIsAM  InnoDB
     *
     */

    try{
        $pdo->beginTransaction();
        
        $price=500;

        $sql="update zhanghao set price=price-{$price} where id=1";

        $affected_rows=$pdo->exec($sql);

        if(!$affected_rows)
            throw new PDOException("张三转出失败");

        $sql="update zhanghao set price=price+{$price} where id=3";

        $affected_rows=$pdo->exec($sql);
            
        if(!$affected_rows) #发现问题手动抛出异常
            throw new PDOException("向李四转入失败");

        echo "交易成功!";
        $pdo->commit();
    }catch(PDOException $e){
        echo $e->getMessage();
        $pdo->rollback(); //只要捕获异常则回滚
    }
    
    //不管执行成功还是失败最后都要在关闭自动提交
    $pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, 1);
    
    //设置错误报告模式 ERRMODE_SILENT    ERRMODE_WARNING

 

 

user contribute comments 

/**
To avoid exposing your connection details should you fail to remember to catch any exception thrown by the PDO constructor you can use the following class to implicitly change the exception handler temporarily.
*/

Class SafePDO extends PDO {

    public static function exception_handler($e) {
        // Output the exception details
        die('Uncaught exception:'.$e->getMessage());
    }

    public function __construct($dsn, $username='', $password='', $driver_options=array()) {

        // Temporarily change the PHP exception handler while we . . .
        set_exception_handler(array(__CLASS__, 'exception_handler'));

        // . . . create a PDO object
        parent::__construct($dsn, $username, $password, $driver_options);

        // Change the exception handler back to whatever it was before
        restore_exception_handler();
    }

}

$dsn = 'mysql:host=127.0.0.1;dbname=tesst;port=3306';
$pdo = new SafePDO($dsn);

 

#占位符的无效使用
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE '%?%'");
$stmt->execute(array($_GET['name']));

// 占位符必须被用在整个值的位置
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE ?");
$stmt->execute(array("%$_GET[name]%"));

 

#开启两次失误会报错,下面解决了这个问题
class Database extends \\PDO 
{ 
    protected $transactionCounter = 0; 
    function beginTransaction() 
    { 
        if(!$this->transactionCounter++) 
            return parent::beginTransaction(); 
       return $this->transactionCounter >= 0; 
    } 

    function commit() 
    { 
       if(!--$this->transactionCounter) 
           return parent::commit(); 
       return $this->transactionCounter >= 0; 
    } 

    function rollback() 
    { 
        if($this->transactionCounter >= 0) 
        { 
            $this->transactionCounter = 0; 
            return parent::rollback(); 
        } 
        $this->transactionCounter = 0; 
        return false; 
    } 
//... 
} 

 

#模拟事务的嵌套使用,执行commit时会提交所有的事务
class Database extends PDO
{

    protected $transactionCount = 0;

    public function beginTransaction()
    {
        if (!$this->transactionCounter++) {
            return parent::beginTransaction();
        }
        $this->exec('SAVEPOINT trans'.$this->transactionCounter);
        return $this->transactionCounter >= 0;
    }

    public function commit()
    {
        if (!--$this->transactionCounter) {
            return parent::commit();
        }
        return $this->transactionCounter >= 0;
    }

    public function rollback()
    {
        if (--$this->transactionCounter) {
            $this->exec('ROLLBACK TO trans'.$this->transactionCounter + 1);
            return true;
        }
        return parent::rollback();
    }
    
}

 

 

/**
 * If you do not fetch all of the data in a result set before issuing your next call to PDO::query(), your call may fail. Call PDOStatement::closeCursor() to release the database resources associated with the PDOStatement object before issuing your next call to PDO::query().
 */
$dsn = 'mysql:host=127.0.0.1;dbname=test;port=3306';
$params = array (
                PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\'' ,
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::MYSQL_ATTR_USE_BUFFERED_QUERY =>0, //@warn
        );
$pdo = new PDO($dsn, 'root', '',$params);


$sql_1 = "select * from card";

$stmt = $pdo->query($sql_1);
$one = $stmt->fetch();
ee($one);

// $stmt->closeCursor();

$sql_2 = "select * from card";

$stmt = $pdo->query($sql_2);
$all = $stmt->fetchAll();
ee($all);

 

/**
获取select的条数,需要公用一个数据库链接才行,会受到limit的影响
MySQL does not seem to return anything in rowCount for a select statement, but you can easily and efficiently get the row count as follows:

*/
class db extends PDO {
  public function last_row_count() {
    return $this->query("SELECT FOUND_ROWS()")->fetchColumn();
  }
}

$myDb = new db('mysql:host=myhost;dbname=mydb', 'login', 'password' );

Then, after running your query:

if ( $myDb->last_row_count() == 0 ) {
  echo "Do something!";
  }

 

更好的获取条数

$db = new PDO(DSN...); 
$db->setAttribute(array(PDO::MYSQL_USE_BUFFERED_QUERY=>TRUE)); 
$rs  = $db->query('SELECT SQL_CALC_FOUND_ROWS * FROM table LIMIT 5,15'); 
$rs1 = $db->query('SELECT FOUND_ROWS()'); 
$rowCount = (int) $rs1->fetchColumn(); 

 

 

/*  使用一个数组的值执行一条含有 IN 子句的预处理语句 */
$params = array(1, 21, 63, 171);
/*  创建一个填充了和params相同数量占位符的字符串 */
$place_holders = implode(',', array_fill(0, count($params), '?'));

/*
    对于 $params 数组中的每个值,要预处理的语句包含足够的未命名占位符 。
    语句被执行时, $params 数组中的值被绑定到预处理语句中的占位符。
    这和使用 PDOStatement::bindParam() 不一样,因为它需要一个引用变量。
    PDOStatement::execute() 仅作为通过值绑定的替代。
*/
$sth = $dbh->prepare("SELECT id, name FROM contacts WHERE id IN ($place_holders)");
$sth->execute($params);

 

/**
$placeHolder
*/
data = ['a'=>'foo','b'=>'bar'];

$keys = array_keys($data);
$fields = '`'.implode('`, `',$keys).'`';

#here is my way 
$placeholder = substr(str_repeat('?,',count($keys),0,-1));

$pdo->prepare("INSERT INTO `baz`($fields) VALUES($placeholder)")->execute(array_values($data));

 

 

1、事务通常是通过把一批更改“积蓄”起来然后使之同时生效而实现的;这样做的好处是可以大大地提供这些更改的效率。

2、当脚本结束或连接即将被关闭时,如果尚有一个未完成的事务,那么 PDO 将自动回滚该事务。这种安全措施有助于在脚本意外终止时避免出现不一致的情况——如果没有显式地提交事务,那么假设是某个地方出错了,所以执行回滚来保证数据安全。

3、预处理语句 的好处
1、查询仅需解析(或预处理)一次,但可以用相同或不同的参数执行多次。 2、提供给预处理语句的参数不需要用引号括起来,驱动程序会自动处理。如果应用程序只使用预处理语句,可以确保不会发生S QL 注入。(然而,如果查询的其他部分是由未转义的输入来构建的,则仍存在 SQL 注入的风险)。

4、在事务中,lastInsertId 应该用在 commit之前,否则会得到 0

5、对于大多数数据库,PDOStatement::rowCount() 不能返回受一条 SELECT 语句影响的行数。替代的方法是,使用 PDO::query() 来发出一条和原打算中的SELECT语句有相同条件表达式的 SELECT COUNT(*) 语句,然后用 PDOStatement::fetchColumn() 来取得返回的行数。这样应用程序才能正确执行。

6、quote 增加引号

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/167196.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 计算机一级本本可以办吗,笔记本电脑卡怎么办,小编教你笔记本电脑太卡怎么解决…[通俗易懂]

    计算机一级本本可以办吗,笔记本电脑卡怎么办,小编教你笔记本电脑太卡怎么解决…[通俗易懂]电脑内的灰尘容易造成部件加快老化,导致硬件的运行迟钝,甚至导致一些因接触不良而无法正常开机。比如风扇,不及时的清理容易造成电脑的温度过高,则就容易电脑开机慢。那笔记本电脑太卡怎么解决?下面,小编给大家带来了笔记本电脑太卡的解决图文。相信很多人都有过以上的经历,其实刚买笔记本的时候,玩游戏开软件都是嗷嗷快,速度快的飞起,然而随着时间的推移,系统垃圾慢慢堆积导致系统运行缓慢。电脑在使用过程中太卡让很多…

  • 用EasySysprep封装Win7系统「建议收藏」

    用EasySysprep封装Win7系统「建议收藏」前面我介绍了使用Dism++封装Windows7系统,不过最后还是没达到我想要的效果。不过经过一番查阅之后,我发现我想要的效果好像没办法简单的使用单一工具达到。我希望做成的效果是类似于Win10,一个ISO镜像内置各种驱动及最新的更新,安装完成之后什么事情都不用做。不过现在看来这个要求比较高,所以得使用一些第三方工具才能比较方便的达成。话说回来Dism++这个工具确实很好用,不过研究下来感觉

  • ArcGis二次开发遇到的问题

    ArcGis二次开发遇到的问题在我们刚开始利用.net对arcgis进行二次开发时,费了牛鼻子劲安装好了arcgisengine,也在vs中建立了新项目,拖进去了工具条控件,主页面控件,想要欣赏一下成果的时候,发现一点击启动就报错了:查阅相关资料后发现,在主程序中加入这样两行代码,给程序进行授权,就可以顺利运行了。在这里强烈推荐牟乃夏老师的一系列书籍,从arcgis基础知识到开发自己的gis软件,非常有用。后面有时间我会…

  • C# ManualResetEvent

    C# ManualResetEvent原文链接http://dotnetpattern.com/threading-manualreseteventManualResetEvent和AutoResetEvent一样,是另外一种.NET线程同步技术。ManualResetEvent被用于在两个或多个线程间进行线程信号发送。多个线程可以通过调用ManualResetEvent对象的WaitOne方法进入等待或阻塞状态。当…

  • pandas中的drop函数_pandas replace函数

    pandas中的drop函数_pandas replace函数dropna()函数的作用是去除读入的数据中(DataFrame)含有NaN的行。如下:>>>df=pd.DataFrame({“name”:[‘Alfred’,’Batman’,’Catwoman’],”toy”:[np.nan,’Batmobile’,’Bullwhip’],”born”:[pd.NaT,pd.Timestamp(“1940-04-25”),

  • 共享打印机错误代码709_连接共享打印机错误0*0000011b

    共享打印机错误代码709_连接共享打印机错误0*0000011b最近发现很多用户连接或安装局域网共享的打印机时出现很多问题,常见的错误代码是0x0000011b和0x00000709或0x000006d9这三个错误。要如何解决呢?下面来讲一下如何解决这两个问题。键盘组合键徽标键Win+R键打开运行,在弹出的运行框中输入【services.msc】确定打开服务窗口,检查这两个服务是否已启动:PrintSpooler和WindowsFirewall一般Win7易出的错误6d9是后面的服务未启动所致。依次查找并卸载KB5005565、KB5005566、KB5005…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号