PDO交易不起作用

时间:2016-01-08 10:46:46

标签: php mysql pdo transactions

我有一个尝试运行的PDO事务,第一个查询创建一个开关,第二个查询将有关它的信息添加到另一个表。我的问题是,由于某种原因,第一个查询没有正确执行,但事务已提交。 (我使用以下PDO类http://culttt.com/2012/10/01/roll-your-own-pdo-php-class/

try{
    //Insert into required tables
    $db->beginTransaction();
    $db->Query("INSERT INTO firewall (Name)VALUES(:Name)");
    $db->bind(':Name',$Name);
    $db->execute();
    $db->Query("INSERT INTO firewall_switch (Switch_ID, firewall_id,customer_ID)VALUES(:Switch,LAST_INSERT_ID(),:Customer)");
    $db->bind(':Switch',$switch);
    $db->bind(':Customer',$customer);
    $db->execute();
    $db->endTransaction();
}catch(PDOException $e){
    $db->cancelTransaction();
}

以下是从日志中运行SQL的内容:

6 Query       START TRANSACTION
6 Prepare     [6] INSERT INTO firewall (Name)VALUES(?)
6 Prepare     [7] INSERT INTO firewall_switch (Switch_ID, firewall_id,customer_ID)VALUES(?,LAST_INSERT_ID(),?)
6 Execute     [7] INSERT INTO firewall_switch (Switch_ID, firewall_id,customer_ID)VALUES('2',LAST_INSERT_ID(),'164')
6 Query       COMMIT

正如您所看到的那样,第一个查询永远不会执行但第二个查询会执行。此特定事务应该已回滚,因为存在重复的ID,这是不允许的。

如果没有重复项,那么事务似乎按预期完成,但我不确定为什么回滚不起作用...

编辑:

数据库类:     Db级{

    private static $Connection = array();
    public $connection;

    private $dbh;
    private $error;

    private $stmt;

    public static function GetConnection($connection)
    {
        if(!array_key_exists($connection,self::$Connection))
        {
            $className = __CLASS__;
            self::$Connection[$connection] = new $className($connection);
        }
        return self::$Connection[$connection];
    }

    public function __construct($connection){

        global $config;
        //Load Settings


        $this->id = uniqid();
        $this->connection = $connection;

        if(array_key_exists($connection,$config['connections']['database'])){
            $dbConfig = $config['connections']['database'][$connection];

            // Set DSN
            $dsn = 'mysql:host=' . $dbConfig['host'] . ';port='.$dbConfig['port'].';dbname=' . $dbConfig['database'];
        }

        // Set options
        $options = array(
            PDO::ATTR_PERSISTENT    => true,
            PDO::ATTR_ERRMODE       => PDO::ERRMODE_EXCEPTION
        );
        // Create a new PDO instantiate
        try{
            $this->dbh = new PDO($dsn, $dbConfig['user'], $dbConfig['password'], $options);
        }
        // Catch any errors
        catch(PDOException $e){
            $this->error = $e->getMessage();
            error_log($e->getMessage());
        }
    }
    //Create the SQL Query
    public function query($query){
        $this->stmt = $this->dbh->prepare($query);
    }
    //Bind SQL Params
    public function bind($param, $value, $type = null){
        if (is_null($type)) {
          switch (true) {
            case is_int($value):
              $type = PDO::PARAM_INT;
              break;
            case is_bool($value):
              $type = PDO::PARAM_BOOL;
              break;
            case is_null($value):
              $type = PDO::PARAM_NULL;
              break;
            default:
              $type = PDO::PARAM_STR;
          }
        }
        $this->stmt->bindValue($param, $value, $type);
    }
    //Execute the SQL
    public function execute($array = NULL){
        if($array == NULL){
            return $this->stmt->execute();
        }else{
            return $this->stmt->execute($array);
        }

    }
    public function resultset(){
        $this->execute();
        return $this->stmt->fetchAll(PDO::FETCH_ASSOC);
    }
    //Return Single Record
    public function single(){
        $this->execute();
        return $this->stmt->fetch(PDO::FETCH_ASSOC);
    }
    //Count rows in table
    public function rowCount(){
        return $this->stmt->rowCount();
    }
    //Show last ID Inserted into table
    public function lastInsertId(){
        return $this->dbh->lastInsertId();
    }
    //Transactions allows the tracking of multiple record inserts, should one fail all will rollback
    public function beginTransaction(){
        return $this->dbh->beginTransaction();
    }
    public function endTransaction(){
        return $this->dbh->commit();
    }
    public function cancelTransaction(){
        return $this->dbh->rollBack();
    }
    //Debug dumps the info that was contained in a perpared statement
    public function debugDumpParams(){
        return $this->stmt->debugDumpParams();
    }
}
?>

数据库结构:

CREATE TABLE `firewall` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `Name` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `Name_UNIQUE` (`Name`),
  UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=40 DEFAULT CHARSET=latin1 

CREATE TABLE `firewall_switch` (
  `id` int(11) NOT NULL auto_increment,
  `Switch_ID` int(10) unsigned NOT NULL,
  `firewall_id` int(10) unsigned NOT NULL,
  `Customer_ID` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `id_UNIQUE` (`id`),
  KEY `fk_firewall_switch_Switch1_idx` (`Switch_ID`),
  KEY `fk_firewall_switch_firewall1_idx` (`firewall_id`),
) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=latin1 

3 个答案:

答案 0 :(得分:2)

好的所以看来我找到了分辨率,似乎设置这样的错误模式不起作用:

$options = array(
    PDO::ATTR_PERSISTENT    => true,
    PDO::ATTR_ERRMODE       => PDO::ERRMODE_EXCEPTION
);
try{
        $this->dbh = new PDO($dsn, $dbConfig['user'], $dbConfig['password'], $options);
}

我现在将其更改为:

try{
    $this->dbh = new PDO($dsn, $dbConfig['user'], $dbConfig['password'], array(PDO::ATTR_PERSISTENT    => true));
    $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);    
}

答案 1 :(得分:0)

将第一个查询更改为

INSERT INTO firewall (Name) VALUES (:Name)
    ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id);

这样,随后使用LAST_INSERT_ID将会起作用:“姓名”是不是重复。

警告:它会在每次运行时“刻录”一个id,因此ID将比预期消耗得更快。可能idINT UNSIGNED,你不太可能达到40亿。

(我认为这在手册中讨论过,但我找不到。所以我在https://dev.mysql.com/doc/refman/5.6/en/insert-on-duplicate.html添加了评论。)

答案 2 :(得分:-1)

默认情况下,mysql在启用自动提交的情况下运行,因此尽管来自pdo的begintransaction,任何mysql查询都将自动更新。

确保使用mysql命令

设置自动提交
  SET autocommit=0;

此外,如果您使用的mysql后端(myisam)不支持事务,则事务无论如何都无法正常工作。 (innodb工作)

相关问题