CodeIgniter交易-trans_status和trans_complete返回true,但未提交任何内容

时间:2018-11-30 21:00:13

标签: php mysql codeigniter

问题:

我已经在模型中编写了一个函数,可以在数据库中插入订单。我正在使用事务来确保所有提交都将被回滚。

我的问题是CodeIgniter没有显示任何数据库错误,但是它回滚了事务,然后为TRUE返回了trans_status。但是,只有在订单有折扣时才会发生这种情况。如果订单没有折扣,那么一切都会提交并正常工作。

我当前正在使用CodeIgniter 3.19,PHP(7.2),mySQL(5.7)和Apache 2.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_completetrans_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;
}
  • 我已经尝试echovar_dump的所有回报insert_ids都工作了,我还输出了affected_rows()的{​​{1}}查询,并显示已更新1行。但是,仍然没有任何承诺:

[转储的值]

UPDATE

-我还尝试用完全不同的查询替换最后一个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进程时,它们似乎都没有峰值或有困难。

  • 我尝试提交没有折扣的订单,整个过程正常!这使我相信我的问题出在我的UPDATE查询上。 [更新后:]但是更新查询似乎也可以正常工作。

建议:

  • 我们尝试将mysqld设置为4,并浏览了CodeIgniter日志文件,该文件没有回滚的历史记录。

  • 我们已经检查了mySQL查询日志:

[查询日志]

log_threshold

它表明在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

我还将mySQL的TRUE文件更改为具有my.cnfinnodb_buffer_pool_size=256M。结果没有改变。

  • 按照@ebcode的建议,我将innodb_log_file_size=64M查询更改为使用UPDATE,而不是使用CodeIgniter的Query Builder Class中的默认方法:

[简单查询]

simple_query()

但是,产生的结果对结果没有任何不同的影响。

如果您认为我还没有尝试过,或者需要我提供更多信息,请发表评论,我会及时答复。

问题:

如果我的交易均未提交,为什么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

为了尝试使用户现在才发现此问题更加清晰,该帖子的最新更新将以斜体显​​示 *

6 个答案:

答案 0 :(得分:4)

我发现了问题。我想对所有尝试提供帮助的人表示感谢,但这是我的错。

在调用此函数的Controller方法中,我调用了另一个启动事务的函数。该交易从未关闭,因此继续进行新交易。

由于该交易未提交且没有错误,因此我找不到任何错误或任何回滚历史记录。但是,一旦我完成上一个交易,一切都会正常。

在mySQL查询日志,mySQL错误日志或CodeIgniter错误日志中没有证据表明存在任何问题。我只能通过慢慢阅读整个mySQL查询日志来找到此问题。

对于遇到此问题的任何人:检查您的其他交易,并确保它们都已关闭。

答案 1 :(得分:0)

也许尝试用对simple_query的调用替换您的更新代码?

更改:

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函数具有以下文档说明:

/**
 * 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
 */

答案 2 :(得分:0)

我正在考虑您的数据库字段discount_redeem_count的名称。

您确定discount_redeem_count不是数字吗?因为在这里您尝试推送字符串值。因此,数据库字段应为var或text。

也许有帮助。

谢谢。

答案 3 :(得分:0)

首先,您应该确保在开始交易之前打开“严格交易”模式。

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

第二,请检查$ order变量。这是一个数组吗?还是一堂课? 如果这是一个类,则可能在此行失败

$this->db->insert('tbl_orders', $order);

第三,如果$ order变量是一个类,则此行将成功。如果$ order变量是数组,则此行将失败。

$discount = $order->get_discount();

答案 4 :(得分:0)

基于 EDIT 5

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

应该可以工作(反引号不会。.传递false第三个参数的重点是,CI不会使用反引号转义您的参数,这将阻止set语句被传递作为字符串。

我在自己的开发代码中进行了一些快速测试,并进行了与之类似的更新,而让它失败的唯一方法是更改​​要更新的表,以使字段(在您的情况下为discount_redeem_count)不是数字。例如,如果我的字段是VARCHAR,则它将无法正常工作,但是当我在INT字段上对其进行尝试时,它将没有任何问题。

您肯定discount_redeem_count字段是数字吗?

答案 5 :(得分:0)

(尝试了这两种建议,都没有用。)

建议1

也许这是真正的答案:

在事务内部时,必须运行

trans_status()。在您的示例中,trans_complete()重置状态标志。

(但是,如果您使用的是Galera或组复制,这很可悲,因为在运行COMMIT时事务仍然可能失败。)

建议2

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

请注意如何在某些针对NULL的测试中使用“三重” !==,而对于其他测试则使用“双” ==

执行此操作以查看您实际得到的内容:

var_dump($result['webcast_capacity']);