PHP / PDO :: commit не работает в блоке try - PullRequest
1 голос
/ 06 марта 2012

Когда я помещаю метод «PDO :: commit ()» в блок «try», он не работает, и я не получаю «commit», даже если с SQL-кодом все в порядке.

$conn->dbh->beginTransaction();

$stmt = $conn->dbh->prepare('some SQL-code');

$stmt->bindValue(...);

try 
{ 
    $stmt->execute();
    $conn->dbh->commit();
} 
catch (Exception $e) 
{
    $dbh->rollBack();
    echo $e->getMessage();
}

Но если я поставлю его после блока "catch", все будет нормально.

...
try 
{ 
    $stmt->execute();
} 
catch (Exception $e) 
{
    $dbh->rollBack();
    echo $e->getMessage();
}
$conn->dbh->commit();

Это ожидаемое поведение и почему PDO :: commit () не работает внутри блока try?

Просто хочу уточнить это для себя, потому что я потратил целых два часа, чтобы найти решение этой проблемы, и я не уверен, является ли это решение правильным.


Вот код:

Class Connection
{
public $dbh;
private static $instance;

private function __construct()
{
    $config = parse_ini_file('config.ini');
    $dsn = $config['db.dbms'] . ':host=' . $config['db.host'] .
           ';dbname='    . $config['db.dbname'] .
           ';port='      . $config['db.port'] .
           ';connect_timeout=15';
    $this->dbh = new PDO($dsn, $config['db.user'], $config['db.password'], array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
    $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}

public static function getInstance()
{
    if (!isset(self::$instance))
    {
        $object = __CLASS__;
        self::$instance = new $object;
    }
    return self::$instance;
}
}

Class NestedSet
{
public function insertAsLastChildOf ($parentnode)
{
    $fields = '';
    $params = '';

    $conn = Connection::getInstance();

    $conn->dbh->beginTransaction();

    foreach ($this->_modelfields as $field => $type)
    {
        $fields .= '`'.$field.'`, ';
        $params .= ':'.$field.', ';            
    }

    $stmt = $conn->dbh->prepare('UPDATE `' . $this->_tablename . '`
                           SET `rgt` = `rgt` + 2
                           WHERE `rgt` >= :parentnodergt;


                           UPDATE `' . $this->_tablename . '`
                           SET `lft` = `lft` + 2
                           WHERE `lft` > :parentnodergt;


                           INSERT INTO `' . $this->_tablename . '`  
                              (' . $fields . '
                               `lft`,
                               `rgt`,
                               `level`)
                            SELECT  
                                ' . $params . '
                                `rgt` - 2, 
                                `rgt` - 1, 
                                `level` + 1                                    
                            FROM `' . $this->_tablename . '`
                            WHERE `id` = :parentnodeid;');

    foreach ($this->_modelfields as $field => $type)
    {
        $pdoparam = (stripos($type, 'int') === 0) ? PDO::PARAM_INT : PDO::PARAM_STR;
        $stmt->bindValue(':'.$field.'', $this->$field, $pdoparam);
    }

    $stmt->bindValue(':parentnodergt', $parentnode->rgt, PDO::PARAM_INT);
    $stmt->bindValue(':parentnodeid', $parentnode->id, PDO::PARAM_INT);

    try
    {
        $stmt->execute();
        $conn->dbh->commit();
    }
    catch (PDOException $e)
    {
        $conn->dbh->rollBack();
        echo $e->getMessage() . '<br/> file - ' . __FILE__ . '<br/> line - ' . __LINE__ . '<br/>';
    }

    unset($stmt); 
}
}

Я пытался переписать код, чтобы у меня был один запрос на каждое подготовленное утверждение. В этом случае все работает нормально - я выполняю код, если нет ошибок, и получаю исключение и откат в противном случае.

Вот код

    $stmt_1 = $conn->dbh->prepare('UPDATE `' . $this->_tablename . '`
                            SET `rgt` = `rgt` + 2
                            WHERE `rgt` >= :parentnodergt;');
    $stmt_1->bindValue(':parentnodergt', $parentnode->rgt, PDO::PARAM_INT);

    $stmt_2 = $conn->dbh->prepare('UPDATE `' . $this->_tablename . '`
                            SET `lft` = `lft` + 2
                            WHERE `lft` > :parentnodergt;');
    $stmt_2->bindValue(':parentnodergt', $parentnode->rgt, PDO::PARAM_INT);

    $stmt_3 = $conn->dbh->prepare('INSERT INTO `' . $this->_tablename . '`  
                              (' . $fields . '
                               `lft`,
                               `rgt`,
                               `level`)
                            SELECT  
                                ' . $params . '
                                `rgt` - 2, 
                                `rgt` - 1, 
                                `level` + 1                                    
                            FROM `' . $this->_tablename . '`
                            WHERE `id` = :parentnodeid;');

    foreach ($this->_modelfields as $field => $type)
    {
        $pdoparam = (stripos($type, 'int') === 0) ? PDO::PARAM_INT : PDO::PARAM_STR;
        $stmt_3->bindValue(':'.$field.'', $this->$field, $pdoparam);
    }

    $stmt_3->bindValue(':parentnodeid', $parentnode->id, PDO::PARAM_INT);

    try
    {
        $stmt_1->execute();
        $stmt_2->execute();
        $stmt_3->execute();
        $conn->dbh->commit();
    }
    catch (PDOException $e)
    {
        $conn->dbh->rollBack();
        echo $e->getMessage() . '<br/> file - ' . __FILE__ . '<br/> line - ' . __LINE__ . '<br/>';
    }
    unset($stmt);

Означает ли это, что PDO :: prepare () не следует использовать с несколькими запросами?

Я спрашиваю, потому что я ничего не нашел в руководстве по PDO.

1 Ответ

1 голос
/ 06 марта 2012

Что-то не так с вашим запросом. Он не работает в блоке t / c, потому что execute() вызывает ошибку, из-за чего возникает исключение перед вызовом commit(). И единственная причина, по которой он работает, когда вы перемещаете его после блока t / c, заключается в том, что в этот момент это процедурный шаг вперед. Попробуйте записать свой журнал ошибок.

...