codeigniter transactions-trans\u status和trans\u complete返回true,但没有提交任何内容

7tofc5zh  于 2021-06-18  发布在  Mysql
关注(0)|答案(6)|浏览(270)

问题:

我在模型中编写了一个函数,用于将订单插入数据库。我使用事务来确保所有提交的内容都被提交,否则它将被回滚。
我的问题是codeigniter没有显示任何数据库错误,但是它正在回滚事务,然后返回 TRUE 为了 trans_status . 但是,只有在订单有折扣的情况下才会发生这种情况。如果订单上没有折扣,一切正常。
我目前正在使用codeigniter3.19、php(7.2)、mysql(5.7)和apache2.4(使用ubuntu 18.04)

功能逻辑的工作原理如下:

将订单数组插入 tbl_orders 保存 order_id ,并浏览每个订单产品(附件) order_id )并将产品插入 tbl_order_products ,
保存 order_product_id 并将其附加到用户出席选项数组中,并将其插入 tbl_order_attendance 获取支付事务数组(附加 order_id )把它插入 tbl_transactions 如果订单上有折扣,它会降低价格 discount_redeem_count (可赎回折扣代码数)乘以1。

实际功能

[功能]:

public function add_order(Order $order, array $order_products, Transaction $transaction = NULL){
  $this->db->trans_start();

  $order->create_order_code();
  $order_array = $order->create_order_array();

  $this->db->insert('tbl_orders', $order_array);
  $order_id = $this->db->insert_id();
  $new_order = new Order($order_id);

  foreach($order_products as $key=>$value){
    $order_products[$key]->set_order($new_order);
    $order_product_array = $order_products[$key]->create_order_product_array();

    $this->db->insert('tbl_order_products', $order_product_array);
    $order_product_id = $this->db->insert_id();

    $product = $order_products[$key]->get_product();

    switch ($product->get_product_class()){
        case 'Iteration':
            $this->db->select('module_id, webcast_capacity, in_person_capacity');
            $this->db->from('tbl_modules');
            $this->db->where('iteration_id', $product->get_product_class_id());
            $results = $this->db->get()->result_array();
            break;
        case 'Module':
            $this->db->select('module_id, webcast_capacity, in_person_capacity');
            $this->db->from('tbl_modules');
            $this->db->where('module_id', $product->get_product_class_id());
            $results = $this->db->get->result_array();
            break;
      }

      if(!empty($results)){
        foreach($results as $result){
        $module_id = $result['module_id'];

        if($result['webcast_capacity'] !== NULL && $result['in_person_capacity'] !== NULL){
          $attendance_method = $order_products[$key]->get_attendance_method();
        }elseif($result['webcast_capacity'] !== NULL && $result['in_person_capacity'] === NULL){
          $attendance_method = 'webcast';
        }elseif($result['webcast_capacity'] === NULL && $result['in_person_capacity'] !== NULL){
          $attendance_method = 'in-person';
        }

        $order_product_attendance_array = array(
          'order_product_id' => $order_product_id,
          'user_id' => $order_products[$key]->get_customer(true),
          'module_id' => $module_id,
          'attendance_method' => $attendance_method,
        );

        $order_product_attendance[] = $order_product_attendance_array;
      }
      $this->db->insert_batch('tbl_order_product_attendance', $order_product_attendance);
    }

    if(!empty($order_products[$key]->get_discount())){
      $discount = $order_products[$key]->get_discount();
    }
  }

  if(!empty($transaction)){
    $transaction->set_order($new_order);
    $transaction_array = $transaction->create_transaction_array();
    $this->db->insert('tbl_transactions', $transaction_array);
    $transaction_id = $this->db->insert_id();
  }

  if(!empty($discount)){
    $this->db->set('discount_redeem_count', 'discount_redeem_count-1', false);
    $this->db->where('discount_id', $discount->get_discount_id());
    $this->db->update('tbl_discounts');
  }

  if($this->db->trans_status() !== false){
    $result['outcome'] = true;
    $result['insert_id'] = $order_id;
    return $result;
  }else{
    $result['outcome'] = false;
    return $result;
  }
}

当此函数以折扣完成时,两者 trans_complete 以及 trans_status 返回 TRUE . 但是,事务从未提交。

我试过的:

我把里面的东西都倒了 $this->db->error() 在每个查询之后,在任何查询中都没有错误。
我用过 this->db->last_query() 要打印出每个查询,然后在线检查语法以查看是否有任何问题,则没有问题。
我还尝试使用codeigniters手动事务,例如:
[示例]

$this->db->trans_begin();
 // all the queries
if($this->db->trans_status() !== false){
    $this->db->trans_commit();
    $result['outcome'] = true;
    $result['insert_id'] = $order_id;
    return $result;
}else{
    $this->db->trans_rollback();
    $result['outcome'] = false;
    return $result;
}

我试过了 echo ing和 var_dump 所有的回报 insert_ids 它们都工作了,我也输出了 affected_rows()UPDATE 查询并显示1行已更新。然而,仍然没有任何承诺:
[值转储]

int(10) // order_id
int(10) // order_product_id
array(3) { 
    ["module_id"]=> string(1) "1" 
    ["webcast_capacity"]=> string(3) "250" 
    ["in_person_capacity"]=> string(3) "250" } // $results array (modules)

array(1) { 
    [0]=> array(4) { 
        ["order_product_id"]=> int(10 
        ["user_id"]=> string(1) "5" 
        ["module_id"]=> string(1) "1" 
        ["attendance_method"]=> string(7) "webcast" } } // order_product_attendance array

int(9) // transaction_id
int(1) // affected rows
string(99) "UPDATE `tbl_discounts` 
            SET discount_redeem_count = discount_redeem_count- 1 
            WHERE `discount_id` = 1" // UPDATE query

-我也试过替换最后一个 UPDATE 一个完全不同的查询,它试图用不同的值更新不同的表。这个查询也不起作用,这让我觉得我的事务达到了某种内存限制。但是,当监控 mysqld 这些过程中,似乎没有一个出现峰值或有困难。
我试过提交一个没有折扣的订单,整个过程都正常!这让我相信我的问题在于我的更新查询[更新后:],但似乎更新查询也在工作。

尝试的建议:

我们试过设置 log_threshold 并查看了codeigniter日志文件,其中没有显示回滚历史。
我们已经检查了mysql查询日志:
[查询日志]

2018-12-03T15:20:09.452725Z         3 Query     UPDATE `tbl_discounts` SET discount_redeem_count = discount_redeem_count-1 WHERE `discount_id` = '1'
2018-12-03T15:20:09.453673Z         3 Quit

它表明 QUIT 命令直接在 UPDATE 查询。这将启动回滚,但是 trans_status 他回来了 TRUE .
我也改变了主意 my.cnf mysql的文件 innodb_buffer_pool_size=256M 以及 innodb_log_file_size=64M . 结果没有变化。
按照@ebcode的建议,我改了 UPDATE 查询以使用 simple_query() 而不是使用codeigniter的查询生成器类中的默认方法:
[简单查询]

if(!empty($discount)){
    $this->db->simple_query('UPDATE `tbl_discounts` SET '.
    'discount_redeem_count = discount_redeem_count-1 WHERE '.
    '`discount_id` = \''.$discount['discount_id'].'\'');
}

然而,这并没有对结果产生任何不同的影响。
如果你有一个想法,我还没有尝试过,或需要更多的信息,请评论,我会尽快回复。

问题:

为什么会这样 trans_status 返回 TRUE 如果没有提交任何事务?
为了给刚刚发现这个问题的用户带来一些清晰的信息,帖子的最新更新将以斜体显示*

izkcnapc

izkcnapc1#

或许可以尝试用调用简单查询来替换更新代码?
更改:

if(!empty($discount)){
    $this->db->set('discount_redeem_count', 'discount_redeem_count-1', false);
    $this->db->where('discount_id', $discount['discount_id']);
    $this->db->update('tbl_discounts');
}

收件人:

if(!empty($discount)){
    $this->db->simple_query('UPDATE `tbl_discounts` SET '.
    'discount_redeem_count = discount_redeem_count-1 WHERE '.
    '`discount_id` = \''.$discount['discount_id'].'\'');
}

我浏览了一下codeigniter的源代码,看起来默认的查询函数做了很多内务处理,可能会把事情搞砸。简单的查询函数有一些文档:

/**
 * Simple Query
 * This is a simplified version of the query() function. Internally
 * we only use it when running transaction commands since they do
 * not require all the features of the main query() function.
 *
 * @param   string  the sql query
 * @return  mixed
 */
2uluyalo

2uluyalo2#

首先,您应该确保在启动事务之前启用事务严格模式。

$this->db->trans_strict(TRUE);
$this->db->trans_start();

其次,请检查$order变量。这是一个数组吗?还是上课?如果这是一个类,那么它可能在这一行失败 $this->db->insert('tbl_orders', $order); 第三,如果$order变量是一个类,那么这一行将成功。如果$order变量是一个数组,那么此行将失败。 $discount = $order->get_discount();

jckbn6z7

jckbn6z73#

我发现了我的问题。我想对所有试图帮助我的人说声谢谢,但这次是我的错。
在前面调用这个函数的controller方法中,我调用了另一个启动事务的函数。那笔交易从未结束,因此一直延续到新的交易中。
因为事务没有提交并且没有错误,所以我找不到任何错误或任何回滚历史记录。然而,一旦我完成了上一笔交易,一切都正常了。
在mysql查询日志、mysql错误日志或codeigniter错误日志中没有任何问题的迹象。我只能通过慢慢阅读整个mysql查询日志来发现这个问题。
对于遇到此问题的任何人:检查您的其他事务并确保它们都已关闭。

lnxxn5zx

lnxxn5zx4#

我在考虑你的数据库领域 discount_redeem_count 姓名。
你确定 discount_redeem_count 不是数字吗?因为这里你试图推一个字符串值。所以数据库字段应该是var或text。
也许有用。
谢谢。

kmynzznz

kmynzznz5#

(这两个建议都试过了,都没有用。)
建议1
也许这才是真正的答案:
事务中必须运行trans\u status()。在您的示例中,trans\u complete()重置状态标志。
(但是,如果您使用的是galera或组复制,这是很悲哀的,因为事务在运行时仍然会失败 COMMIT .)
建议2

These are  `== NULL`:  NULL, '', FALSE, 0
These are `!== NULL`:        '', FALSE, 0

注意你怎么用“三元组” !== 对于某些针对null的测试,请使用“double” == 对其他人来说。
执行此操作以查看实际获得的内容:

var_dump($result['webcast_capacity']);
pdsfdshx

pdsfdshx6#

基于编辑5:

$this->db->set('discount_redeem_count', 'discount_redeem_count-1', false);

应该有效(后面打勾的那个不会。。整个过程 false 第三个参数是这样,ci就不会用backticks转义参数,这将阻止set语句作为字符串传递。
我在我自己的开发代码中做了一些快速测试,更新的版本和那个版本相似,唯一失败的方法就是修改正在更新的表,使字段( discount_redeem_count 在你的情况下)不是数字。例如,如果我的字段是varchar,它就不起作用,但是当我在int字段上尝试它时,它没有问题。
你是肯定的吗 discount_redeem_count 字段是数字?

相关问题