2014-09-23 82 views
0

我正在嘗試使用與使用PDO的mysql進行事務處理。我遇到的問題是在我提交之前交易已經中斷。我知道這是因爲我在連接上回顯inTransaction()函數。PDO在提交/回滾之前丟失事務

在我看來,原因是我實例化一個PDODatabase類,然後在實際執行任何查詢之前做一些其他的編碼工作,在這段時間我失去了事務。

實例化我班

$pdo = new PdoDatabase; 
$pdo->beginTransaction(); 
echo "first ".$pdo->transactionStarted()."<br />"; 

的PdoDatabase類

public function __construct(){ 
    $dsn = 'mysql:host='.DB_HOST.';dbname='.DB_NAME; 
    $options = array(PDO::ATTR_PERSISTENT => TRUE, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION); 
    try{ 
     $this->_connection = new PDO($dsn, DB_USER, BD_PASS, $options); 
    } catch (PDOException $e){ 
     $this->_error = $e->getMessage(); 
    } 
} 

public function query($q){ 
    if($this->_error != ''){ 
     echo $this->_error; 
    } else { 
     $this->_stmt = $this->_connection->prepare($q); 
    } 
} 

public function bind($param, $value, $type = null){ 
    //echo "<br>".$value."<br>"; 
    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); 
} 

public function execute($class = null){ 
    $object_array = array(); 

    if($class !== null){ 
     if($this->_stmt->execute()){ 
      $this->_stmt->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, $class, null); 
      while($row = $this->returnRow()){ 
       $object_array[] = $class::instantiate($row); 
      } 
     } 
     return $object_array; 
    } else { 

     return $this->_stmt->execute(); 
    } 
} 

public function transactionStarted(){ 
    return $this->_connection->inTransaction(); 
} 

我如何使用它(部分)

if(isset($_POST['id']) && $_POST['id'] != ''){ 
       echo "id exists ".$pdo->transactionStarted()."<br />"; 
       $bidder->getBidderById($_POST['id']); 
       echo "step 1 ".$pdo->transactionStarted()."<br />"; 
       $bidder = $bidder->getList(0); 
       echo "step 2 ".$pdo->transactionStarted()."<br />"; 
       $old_bidder = clone $bidder; 
       echo "step 3 ".$pdo->transactionStarted()."<br />"; 

       $bidder_phones->getPhones($_POST['id']); 
       echo "step 4 ".$pdo->transactionStarted()."<br />"; 
       $bidder_phones = $bidder_phones->getList(); 
       echo "step 5 ".$pdo->transactionStarted()."<br />"; 
       if($_POST['phone'] == ''){ 
        // check to see if there are any phone numbers in the database already and delete if there is 
        foreach($bidder_phones as $bp){ 
         $q = "delete from bidder_phones where id = :id"; 
         $pdo->query($q); 
         $pdo->bind(":id", $bp->getId()); 
         $pdo->execute(); 
         //$bp->remove(); 
        } 
       } else { 
        echo "phone to check ".$pdo->transactionStarted()."<br />"; 
        $old_phone_numbers = array(); 
        $new_phone_numbers = explode(',', $_POST['phone']); 
        foreach($bidder_phones as $bp){ 
         // remove any unused phone numbers 
         if(!in_array($bp, $new_phone_numbers)){ 
          $q = "delete from bidder_phones where id = :id"; 
          $pdo->query($q); 
          $pdo->bind(":id", $bp->getId()); 
          $pdo->execute(); 
          //$bp->remove(); 
         } 
         // push to an array to test the new numbers 
         array_push($old_phone_numbers, $bp->getPhone()); 
        } 
        foreach($new_phone_numbers as $phone){ 
         // adding new phone numbers 
         if(!in_array($phone, $old_phone_numbers)){ 
          $new_phone = new BidderPhone; 
          $new_phone->setPhone($phone); 
          $new_phone->setBidderId($_POST['id']); 
          $pdo->save('BidderPhones', $new_phone); 
          //$new_phone->save(); 
         } 
        } 
       } 

正如你看到的我是呼應$ pdo- > transactionStarted()幾次。這是嘗試查看我何時失去交易。我期望看到的是我的消息,然後是1,表明交易仍然活躍。這就是我得到:

first 1 
id exists 1 
step 1 
step 2 
step 3 
step 4 
step 5 
phone to check 
in save 
before create 

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42S02]: Base table or view not found: 1146 Table 'clickbid.bidder_phoneses' doesn't exist' in /var/www/classes/PdoDatabase.php:59 Stack trace: #0 /var/www/classes/PdoDatabase.php(59): PDOStatement->execute() #1 /var/www/classes/PdoDatabase.php(158): PdoDatabase->execute() #2 /var/www/classes/PdoDatabase.php(187): PdoDatabase->getFields('BidderPhones') #3 /var/www/classes/PdoDatabase.php(176): PdoDatabase->create('BidderPhones', Object(BidderPhone), false) #4 /var/www/admin/data/post_data.php(284): PdoDatabase->save('BidderPhones', Object(BidderPhone)) #5 {main} thrown in /var/www/classes/PdoDatabase.php on line 59 

,所以我失去了存在的ID之後,這是因爲我在做其他的節目,而不是準備查詢和執行它們的交易?還有什麼我需要知道的,我失蹤了?在過去的兩天中,我一直在努力解決這個問題。問題是我真的需要能夠開始我的交易,並在實際執行一些查詢之前做一些工作。有沒有辦法做到這一點?

在此先感謝您的幫助。

編輯 因此,我對代碼進行了一些重大更改,並且能夠更密切地確定何時丟失活動事務。

我保存方法是這樣的

public function save($table, $object, $old_object = null){ 
    echo "in save ".$this->transactionStarted()."<br />"; 
    if($object->_id != ''){ 
     echo "before update ".$this->transactionStarted()."<br />"; 
     //return $this->update($table, $object, $old_object, $transaction); 
     if($this->update($table, $object, $old_object)){ 
      echo "update true ".$this->transactionStarted()."<br />"; 
      return true; 
     } else { 
      echo "update false ".$this->transactionStarted()."<br />"; 
      return false; 
     } 
    } else { 
     echo "before create ".$this->transactionStarted()."<br />"; 
     //return $this->create($table, $object, $transaction); 
     if($this->create($table, $object)){ 
      echo "create true ".$this->transactionStarted()."<br />"; 
      return true; 
     } else { 
      echo "create false ".$this->transactionStarted()."<br />"; 
      return false; 
     } 
    } 
} 

在這種特定情況下有從物體發出的_id所以這個 - $>更新是我們在這裏調試是更新方法

private function update($table, $object, $old){ 
    echo "in update ".$this->transactionStarted()."<br />"; 
    $audit = new PdoDatabase; 
    echo "after new ".$this->transactionStarted()."<br />"; 
    $aq = "insert into audit_trails 
      (table_name, table_id, user_id, field_name, original_value, new_value) values 
      (:table_name, :table_id, :user_id, :field_name, :original_value, :new_value)"; 
    echo "before query ".$this->transactionStarted()."<br />"; 
    $audit->query($aq); 
    echo "after query ".$this->transactionStarted()."<br />"; 
    //$update = new PdoDatabase;  

    $binding = array(); 
    echo "before field_names ".$this->transactionStarted()."<br />"; 
    $field_names = self::getFields($table); 
    echo "after field_names ".$this->transactionStarted()."<br />"; 

    $uc = new UserConfig; 
    $uc->getConfig(); 
    $user = $uc->getList(0); 
    echo "before foreach ".$this->transactionStarted()."<br />"; 
    foreach($field_names as $field){ 
     $thisField = "_".$field['Field']."<br>"; 
     $getField = 'get'.self::to_camel_case($field['Field']); 
     $method = $getField; 
     $class = self::to_camel_case($table); 
     $field_list = ''; 

     if($field['Field'] == 'id'){ 
      $where = 'where id = :id'; 
      $binding[':id'] = ($object->$getField()); 
     } else { 

      if(method_exists($class, $method)){ 
       if($object->$getField() != $old->$getField()){ 
        $field_list .= $field['Field']."= :".$field['Field'].", "; 
        $binding[':'.$field['Field']] = $object->$getField(); 

        $audit->bind(':table_name', $table); 
        $audit->bind(':table_id', $object->getId()); 
        $audit->bind(':user_id', $user->getUserId()); 
        $audit->bind(':field_name', $thisField); 
        $audit->bind(':original_value', $object->$getField()); 
        $audit->bind(':new_value', $old->$getField()); 

        echo "before audit execute ".$this->transactionStarted()."<br />"; 
        $audit->execute(); 
        echo "after audit execute ".$this->transactionStarted()."<br />"; 

       } 
      } 
     } 
    } 
    echo "before binding ".$this->transactionStarted()."<br />"; 
    if(count($binding) > 1){ 
     $q = "update ".self::singularToPlural($table)." set "; 
     foreach($binding as $key => $value){ 
      if($key != ':id'){ 
       $q .= str_replace(':', '', $key)." = ".$key.", "; 
      } 
     } 
     $q = rtrim($q, ", "); 
     $q .= ' '.$where; 

     //$update->query($q); 
     echo "before this query ".$this->transactionStarted()."<br />"; 
     $this->query($q); 
     echo "after this query ".$this->transactionStarted()."<br />"; 
     /*if($transaction && !$this->_stmt->inTransaction()){ 
      $this->_stmt->beginTransaction(); 
     }*/ 

     foreach($binding as $key => $value){ 
      //$update->bind($key, $value); 
      $this->bind($key, $value); 
     } 
     //$update->bind($id); 
     //return $update->execute(); 
     echo "before this execute ".$this->transactionStarted()."<br />"; 
     $stupid = $this->execute(); 
     echo "after this execute ".$this->transactionStarted()."<br />"; 
     return $stupid; 
    } else { 
     echo "before return true ".$this->transactionStarted()."<br />"; 
     return true; 
    } 

} 

,輸出爲

first 1 
in save 1 
before update 1 
in update 1 
after new 1 
before query 1 
after query 1 
before field_names 1 
before execute 1 
after execute 1 
after field_names 1 
before foreach 1 
before audit execute 1 
before execute 1 
after execute 1 
after audit execute 1 
before binding 1 
before this query 1 
after this query 1 
before this execute 1 
before execute 1 
after execute 1 
after this execute 1 
update true 
the save 1 
second 
after save 
before commit 

Fatal error: Uncaught exception 'PDOException' with message 'There is no active transaction' in /var/www/classes/PdoDatabase.php:86 Stack trace: #0 /var/www/classes/PdoDatabase.php(86): PDO->commit() #1 /var/www/admin/data/post_data.php(403): PdoDatabase->commit() #2 {main} thrown in /var/www/classes/PdoDatabase.php on line 86 

從這個我能看到積極的辦理當我們從更新返回到保存方法時離子會丟失,我只是不確定這是爲什麼。

再次感謝。

編輯添加getBidderById功能

public function getBidderById($bidder_id){ 
    $pdo = new PdoDatabase; 

    $q = "select * 
      from bidders Bidder 
      where Bidder.id = :bidder_id 
      limit 1"; 

    $pdo->query($q); 
    $pdo->bind(":bidder_id", $bidder_id); 
    $this->_bidders = $pdo->execute('Bidder'); 

    if(!empty($this->_bidders)){ 
     return true; 
    } else { 
     return false; 
    } 
} 

編輯添加創建方法

private function create($table, $object){ 
    //$insert = new PdoDatabase; 

    $field_names = self::getFields($table); 

    foreach($field_names as $field){ 
     $getField = 'get'.self::to_camel_case($field['Field']); 
     $method = $getField; 
     $class = self::to_camel_case($table); 

     if(method_exists($class, $method)){ 
      if($field['Field'] != 'id'){ 
       $fields = $field['Field'].", "; 
       $binding_fields = ":".$field['Field'].", "; 
       $binding[':'.$field['Field']] = $object->$getField(); 
      } 
     } 
    } 
    $fields = rtrim($fields, ", "); 
    $binding_fields = rtrim($binding_fields, ", "); 

    $iq = "insert into ".self::singularToPlural($table)." 
      (".$fields.") values 
      (".$binding_fields.")"; 
    $this->query($iq); 
    /*if($transaction && !$this->_stmt->inTransaction()){ 
     $this->_stmt->beginTransaction(); 
    }*/ 

    foreach($binding as $key => $value){ 
     $this->bind($key, $value); 
    } 

    if($this->execute()){ 
     $object->setId($this->getConnection()->lastInsertId()); 
     return true; 
    } else { 
     return false; 
    } 

} 
+0

發生在$ bidder- Gervs 2014-09-23 17:21:02

+0

$ bidder-> getBidderById($ _ POST ['id'])查詢數據庫中的這個出價者,它填充出價者實例,稍後將用於更新出價者,如果有的話是不同的,因此$ old_bidder = clone $ bidder。這兩個都傳遞給保存方法並相互比較以確定需要更新的內容。 – 2014-09-23 17:37:48

+0

沒有涉及的DDL語句,您能顯示該功能嗎? – Gervs 2014-09-23 17:48:17

回答

0

HMZ,我注意到,你在一個函數中實例化PdoDatabase並使用$這 - > transactionStarted( )。

通常情況下不會成爲問題,但由於您使用的是持久連接,因此您可能會在某個其他類中啓動新的事務,這會導致正在運行的事務的隱式提交。

if($this->create($table, $object)){ 
     echo "create true ".$this->transactionStarted()."<br />"; 
     return true; 
    } 

如果使用此功能的DDL語句(CREATE,ALTER,DROP,TRUNCATE等)犯也將被應用於

+0

我明白你的意思了,但是我並沒有在那個時候失去活躍的交易,我會懷疑這樣做,但是在我回到最初的通話時。 – 2014-09-23 19:25:33

+0

@ Jamie看到我的編輯 – Gervs 2014-09-23 19:51:48

+0

創建不被使用,這只是我的方法名稱。在這個方法中完成的唯一一件事就是插入我將在一秒之內添加到我的帖子上面。 – 2014-09-23 19:54:51

0

在MySQL中有些語句引起的隱式提交。您可能正在進行的任何交易均已落實。舉例來說,所有的DDL語句都是這樣做的。有關更多信息,請參閱https://dev.mysql.com/doc/refman/5.6/en/implicit-commit.html

令人驚訝的是很難知道您的會話中是否有活動事務。在導致隱式提交的語句之間,以及查詢可以是「COMMIT」(不是通過任何commit()函數,而是通過query()函數)的客戶端可能無法確定它們是否仍然存在交易正在進行。

此問題是嘗試創建可重用DBAL的開發人員的一種禍根。

另見我的回答How do detect that transaction has already been started?


回覆評論:

對於MySQL,PDO::inTransaction()可靠,因爲PDO MySQL驅動程序沒有實現的方法mysql_handle_in_transaction()MySQL C API不支持詢問當前會話的事務狀態。

因此PDO類試圖使用,當你調用$dbh->commit()$dbh->rollback()被設置爲1,當你調用$dbh->beginTransaction(),並設置爲0,內部變量做出最好的猜測。

但是,如果DDL語句導致隱含的提交,驅動程序沒有任何通知,該事務結束。所以PDO中的內部變量可能會與現實不同步。如果您撥打$dbh->query('COMMIT'),也可能會發生這種情況,繞過事務控制的PDO功能。

其他PDO驅動程序,例如PostgreSQL驅動程序,確實實現了獲取當前事務狀態的方法。


我不是說這是你的問題的原因在這種情況下。但是試圖檢測客戶端的事務狀態並不是一直能夠正常工作。

我不能說出你的情況發生了什麼。您仍然有幾行調試輸出,但您尚未共享該代碼。

+0

那麼你是說連接的inTransaciton()方法不可靠嗎?因爲我正在使用它,並且它似乎對我有用,所以如果我處於事務中,它將返回1(或true)。從我上面的所有例子中可以看出它正在發生。這就是爲什麼我認爲在我執行查詢語句並返回給調用者之後事務已經中斷,這對我來說沒有任何意義,因爲除了返回true(假設執行成功)並且事務仍處於活動狀態,在返回之前 – 2014-09-23 19:52:05