php - ネスト - this-> db-> trans_begin




CodeIgniterトランザクション-trans_statusとtrans_completeはtrueを返しますが、コミットされるものはありません。 (4)

(これらの提案はどちらも試したことはありませんでした。)

提案1

おそらくこれが本当の答えです。

trans_status()はトランザクション内にいるときに実行されなければなりません。 あなたの例ではtrans_complete()はステータスフラグをリセットします。

(ただし、GaleraまたはGroup Replicationを使用している場合は、 COMMIT実行時にトランザクションが失敗する可能性があるため、これは悲しいことです。)

提案2

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

NULLに対するいくつかのテストでは "triple" !==を使用しますが、それ以外の場合は "double" ==を使用する方法に注意してください。

あなたが実際に何を得ているか見るためにこれをしてください:

var_dump($result['webcast_capacity']);

問題:

データベースに注文を挿入するための関数をモデルに書きました。 トランザクションを使用して、すべてがコミットされるようにします。そうしないとロールバックされます。

私の問題は、CodeIgniterがデータベースエラーを何も表示していないことですが、トランザクションをロールバックしていますが、それからtrans_statusに対してTRUEtrans_statusます。 ただし、これは注文に割引がある場合にのみ発生します。 注文に割引がない場合は、すべてがコミットされ、正しく機能します。

私は現在CodeIgniter 3.19、PHP(7.2)、mySQL(5.7)、そしてApache 2.4を使っています。 (Ubuntu 18.04での作業)

機能ロジックは以下のように機能します。

  • order配列をtbl_orders挿入しtbl_orders
  • order_id保存し、各注文商品をorder_idorder_id )、その商品をtbl_order_products挿入します。
  • order_product_idを保存し、それを一連のユーザー参加オプションにtbl_order_attendanceしてtbl_order_attendance挿入します。
  • 支払いトランザクション配列をorder_idorder_id )、それをtbl_transactions挿入しtbl_transactions
  • 注文discount_redeem_countがある場合、 discount_redeem_count (償還可能な割引コードの数)が1減少しdiscount_redeem_count

実機能

[関数]:

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;
}
  • すべてのreturn insert_idsechoしてvar_dumpてみたところ、すべてうまくいきました。また、 UPDATEクエリのinsert_ids affected_rows()も出力しました。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プロセスを監視するとき、それらのどれもが急上昇したり、困難を抱えているようには見えません。

  • 値引きされていない注文を送信しようとしましたが、プロセス全体が機能します。 これは私の問題は私のUPDATEクエリにあると信じるように私を導きます。 [更新後:]しかし、更新クエリも同様に機能しているようです。

提案された提案:

  • log_thresholdを4に設定し、ロールバックの履歴がないことを示す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_statusTRUE返します。

mySQL用のmy.cnfファイルもinnodb_buffer_pool_size=256Minnodb_log_file_size=64M 。 結果に変化はありませんでした。

  • @ebcodeが推奨しているように、CodeIgniterのQuery Builderクラスのデフォルトメソッドを使う代わりに、 UPDATEクエリを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いないtrans_statusTRUE返すのはなぜですか?

この質問を今すぐ見つけられるようにするために、ユーザーにわかりやすくするために、投稿に対する最新の更新情報を斜体で表示します。


編集5に基づいて:

この

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

false 3番目のパラメータを渡すことのすべての目的は、CIがバッククォートでパラメータをエスケープしないようにすることです。これにより、setステートメントが文字列として渡されるのを防ぐことができます。

私は自分自身の開発コードでそれに似た更新でいくつかの簡単なテストをしました、そして失敗した唯一の方法はフィールド(あなたのケースではdiscount_redeem_count )が数値でないように更新されているテーブルを変更することでした。 たとえば、私のフィールドがVARCHARの場合は機能しませんでしたが、INTフィールドで試しても問題なく機能しました。

discount_redeem_countフィールドが数値であるかどうか


データベースフィールドのdiscount_redeem_count名について考えています。

discount_redeem_countがnumberではありませんか? なぜならここであなたは文字列値をプッシュしようとしています。 そのため、データベースフィールドはvarまたはtextになります。

多分参考になります。

ありがとう。


更新コードを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
 */






codeigniter