2016-01-08 6 views
11

Ich habe eine PDO-Transaktion, die ich versuche zu starten, die erste Abfrage erstellt einen Schalter und der zweite fügt Informationen darüber zu einer anderen Tabelle. Mein Problem ist, dass aus irgendeinem Grund die erste Abfrage nicht korrekt ausgeführt wird, aber die Transaktion festgeschrieben ist. (Im den folgenden PDO Klasse http://culttt.com/2012/10/01/roll-your-own-pdo-php-class/ mit)PDO Transaction funktioniert nicht

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(); 
} 

Hier finden Sie, was in SQL aus den Protokollen ausgeführt wird:

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 

, wie Sie die erste Abfrage ausgeführt wird nie sehen können, aber der zweite Fall ist. Diese bestimmte Transaktion sollte zurückgesetzt werden, da eine doppelte ID nicht zulässig ist.

Wenn es keine Duplikate sind dann scheint die Transaktion wie erwartet vollständig, aber ich bin nicht sicher, warum Rollback funktioniert nicht ...

EDIT:

DB Klasse: Klasse 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(); 
    } 
} 
?> 

DB-Struktur:

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 
+0

Können Sie Ihren '$ db' Klassencode schreiben und wo Sie es auch konstruieren. Der Link zeigt nur, wie man einen schreibt, es gibt keine vollständige Klasse. Was ist die Art der Dublettenprüfung? Können Sie auch die Ergebnisse von SHOW CREATE TABLE auf jeder der beiden referenzierten Tabellen anzeigen? – Arth

+0

Haben Sie PDO so eingestellt, dass Ausnahmen ausgelöst werden? Standardmäßig wird der stille Fehlermodus verwendet, bei dem Sie nach jeder Aktion explizit nach Fehlern suchen müssen. Siehe [Fehler und Fehlerbehandlung] (https://secure.php.net/manual/en/pdo.error-handling.php) für weitere Informationen. – eggyal

+0

@Art Meine DB-Klasse sowie die DB-Struktur anhängen –

Antwort

1

Ok So scheint es, dass ich die Lösung gefunden haben, scheint es, dass wie diese den Fehler-Modus Einstellung nicht funktioniert:

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

Ich habe dies nun geändert:

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

So können Sie jetzt sehen, warum die erste Einfügung nie ausgeführt wurde ... aus Interesse, warum war das? – eggyal

+0

meine Theorie ist das $ this-> stmt wird überschrieben – PauAI

-1

von mysql läuft Standard mit autocommit aktiviert, so dass jede mysql-Abfrage trotz der Starttransaktion von PDO autocommiert wird.

Vergewissern Sie sich, die Einstellung der autocommit mit dem MySQL-Befehl

SET autocommit=0; 

auch aus, wenn die MySQL-Backend, das Sie verwenden (MyISAM) doesnt unterstützen Transaktionen die Transaktion ohnehin nicht funktionieren. (Innodb arbeitet)

+0

Beide sind bereits eingestellt. –

0

die erste Abfrage ändern

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

auf diese Weise Ihre spätere Verwendungzu seinfunktioniert, ob `: Name 'ein dup ist oder nicht.

Vorbehalt: Es wird eine ID jedes Mal "brennen", wenn es ausgeführt wird, so dass die IDs schneller verbraucht werden als gewünscht. Wahrscheinlich id ist INT UNSIGNED und Sie sind unwahrscheinlich, 4 Milliarden zu treffen.

(Ich dachte, das im Handbuch besprochen wurde, aber ich nicht so finden kann. So habe ich einen Kommentar zu https://dev.mysql.com/doc/refman/5.6/en/insert-on-duplicate.html.)

Verwandte Themen