使用 PDO 的数据库事务

例子 #

数据库事务确保一组数据更改只有在每条语句都成功时才会永久生效。可以捕获事务期间的任何查询或代码故障,然后您可以选择回滚尝试的更改。

PDO 为开始、提交和回滚事务提供了简单的方法。

$pdo = new PDO(
    $dsn, 
    $username, 
    $password, 
    array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
);

try {
    $statement = $pdo->prepare("UPDATE user SET name = :name");

    $pdo->beginTransaction();

    $statement->execute(["name"=>'Bob']);
    $statement->execute(["name"=>'Joe']);

    $pdo->commit();
} 
catch (\Exception $e) {
    if ($pdo->inTransaction()) {
        $pdo->rollback();
        // If we got here our two data updates are not in the database
    }
    throw $e;
}

在事务期间,所做的任何数据更改仅对活动连接可见。 SELECT语句将返回更改后的更改,即使它们尚未提交到数据库。

注意:有关事务支持的详细信息,请参阅数据库供应商文档。有些系统根本不支持事务。有些支持嵌套事务,有些则不支持。

使用带有 PDO 的事务的实际示例

在下一节中演示了一个实际的现实世界示例,其中使用事务确保了数据库的一致性。

想象以下场景,假设您正在为电子商务网站构建购物车,并且您决定将订单保存在两个数据库表中。一个以字段ordersorder_idnameaddress命名。第二个以字段,和命名。第一个表包含订单的元数据,而第二个表包含已订购的实际产品。telephonecreated_atorders_productsorder_idproduct_idquantity

向数据库中插入新订单

要将新订单插入数据库,您需要做两件事。首先,您需要INSERT在表中创建一条新记录,该记录orders将包含订单的元数据name( 、address等)。然后,您需要为订单中包含的每一种产品INSERT在表中记录一条记录。orders_products

您可以通过执行类似于以下的操作来做到这一点:

// Insert the metadata of the order into the database
$preparedStatement = $db->prepare(
    'INSERT INTO `orders` (`name`, `address`, `telephone`, `created_at`)
     VALUES (:name, :address, :telephone, :created_at)'
);

$preparedStatement->execute([
    'name' => $name,
    'address' => $address,
    'telephone' => $telephone,
    'created_at' => time(),
]);

// Get the generated `order_id`
$orderId = $db->lastInsertId();

// Construct the query for inserting the products of the order
$insertProductsQuery = 'INSERT INTO `orders_products` (`order_id`, `product_id`, `quantity`) VALUES';

$count = 0;
foreach ( $products as $productId => $quantity ) {
    $insertProductsQuery .= ' (:order_id' . $count . ', :product_id' . $count . ', :quantity' . $count . ')';
    
    $insertProductsParams['order_id' . $count] = $orderId;
    $insertProductsParams['product_id' . $count] = $productId;
    $insertProductsParams['quantity' . $count] = $quantity;
    
    ++$count;
}

// Insert the products included in the order into the database
$preparedStatement = $db->prepare($insertProductsQuery);
$preparedStatement->execute($insertProductsParams);

这对于将新订单插入数据库非常有用,直到发生意外情况并且由于某种原因第二个INSERT查询失败。如果发生这种情况,您最终将在orders表中获得一个新订单,该订单将没有与之关联的产品。幸运的是,修复非常简单,您所要做的就是以单个数据库事务的形式进行查询。

使用事务将新订单插入数据库

要启动事务,PDO您所要做的就是在beginTransaction对数据库执行任何查询之前调用该方法。然后,您可以通过执行INSERT和/或UPDATE查询对数据进行任何更改。最后,您调用对象的commit方法PDO以使更改永久化。在您调用该commit方法之前,您对数据所做的每一次更改都不是永久性的,只需调用对象的rollback方法即可轻松恢复PDO

在下面的例子中演示了使用事务将新订单插入数据库,同时保证数据的一致性。如果两个查询之一失败,所有更改都将被还原。

// In this example we are using MySQL but this applies to any database that has support for transactions
$db = new PDO('mysql:host=' . $host . ';dbname=' . $dbname . ';charset=utf8', $username, $password);    

// Make sure that PDO will throw an exception in case of error to make error handling easier
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try {
    // From this point and until the transaction is being committed every change to the database can be reverted
    $db->beginTransaction();    
    
    // Insert the metadata of the order into the database
    $preparedStatement = $db->prepare(
        'INSERT INTO `orders` (`order_id`, `name`, `address`, `created_at`)
         VALUES (:name, :address, :telephone, :created_at)'
    );
    
    $preparedStatement->execute([
        'name' => $name,
        'address' => $address,
        'telephone' => $telephone,
        'created_at' => time(),
    ]);
    
    // Get the generated `order_id`
    $orderId = $db->lastInsertId();

    // Construct the query for inserting the products of the order
    $insertProductsQuery = 'INSERT INTO `orders_products` (`order_id`, `product_id`, `quantity`) VALUES';
    
    $count = 0;
    foreach ( $products as $productId => $quantity ) {
        $insertProductsQuery .= ' (:order_id' . $count . ', :product_id' . $count . ', :quantity' . $count . ')';
        
        $insertProductsParams['order_id' . $count] = $orderId;
        $insertProductsParams['product_id' . $count] = $productId;
        $insertProductsParams['quantity' . $count] = $quantity;
        
        ++$count;
    }
    
    // Insert the products included in the order into the database
    $preparedStatement = $db->prepare($insertProductsQuery);
    $preparedStatement->execute($insertProductsParams);
    
    // Make the changes to the database permanent
    $db->commit();
}
catch ( PDOException $e ) { 
    // Failed to insert the order into the database so we rollback any changes
    $db->rollback();
    throw $e;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值