You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何防止单次请求中重复执行CodeIgniter调用的存储过程?

Prevent Multiple Executions of Stored Procedure in Single CodeIgniter Request

Great question! When dealing with invoice number generation (or any state-changing stored procedure), accidental duplicate calls in a single request can lead to invalid or duplicate invoice numbers. Here are a few practical, request-level safeguards you can implement in CodeIgniter:

1. Cache the Result in CodeIgniter Session

Sessions persist for the duration of the request, so you can store the generated invoice number once fetched and reuse it for the rest of the request flow.

Here’s how to implement this in your model or controller:

public function get_invoice_number($state_code) {
    // Check if we already have the invoice number stored in session
    $invoice_no = $this->session->userdata('current_invoice_no');
    
    if (!$invoice_no) {
        // Execute the stored procedure only once per request
        $query = $this->db->query("Declare @CurrentInvoiceNo int Execute spSetInvoiceNoStatewise '$state_code', @CurrentInvoiceNo output select @CurrentInvoiceNo");
        $result = $query->row();
        $invoice_no = $result->CurrentInvoiceNo;
        
        // Save to session for subsequent calls in the same request
        $this->session->set_userdata('current_invoice_no', $invoice_no);
    }
    
    return $invoice_no;
}

Note: If you only need this value for the current request, clear it after processing (e.g., after saving the invoice) with $this->session->unset_userdata('current_invoice_no');.

2. Use a Static Variable in Your Model/Controller

Static variables retain their value for the lifetime of the request, making this a lightweight caching option without relying on sessions.

Example in a model:

class Invoice_model extends CI_Model {
    private static $cached_invoice_no = null;
    
    public function get_invoice_number($state_code) {
        if (self::$cached_invoice_no === null) {
            // Run the stored procedure once per request
            $query = $this->db->query("Declare @CurrentInvoiceNo int Execute spSetInvoiceNoStatewise '$state_code', @CurrentInvoiceNo output select @CurrentInvoiceNo");
            $result = $query->row();
            self::$cached_invoice_no = $result->CurrentInvoiceNo;
        }
        
        return self::$cached_invoice_no;
    }
}

No matter how many times you call get_invoice_number() in the same request, the stored procedure will only execute once.

3. Wrap Logic in a Singleton Service (For Larger Apps)

If you’re building a more complex application, a singleton service ensures only one instance exists per request, guaranteeing the invoice number is generated once.

class InvoiceService {
    private static $instance = null;
    private $invoice_no;
    
    private function __construct() {}
    
    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    public function getInvoiceNumber($state_code, $db) {
        if ($this->invoice_no === null) {
            $query = $db->query("Declare @CurrentInvoiceNo int Execute spSetInvoiceNoStatewise '$state_code', @CurrentInvoiceNo output select @CurrentInvoiceNo");
            $result = $query->row();
            $this->invoice_no = $result->CurrentInvoiceNo;
        }
        return $this->invoice_no;
    }
}

Use it in your controller like this:

$invoice_service = InvoiceService::getInstance();
$invoice_no = $invoice_service->getInvoiceNumber('TN', $this->db);

Bonus: Database-Level Safeguard

Even with application-level checks, add a database lock to prevent race conditions from concurrent requests. Modify your stored procedure to use UPDLOCK when selecting the invoice number:

CREATE Procedure [dbo].[spSetInvoiceNoStatewise] 
    @StateCode nvarchar(20), 
    @InvoiceNo int = 0 output 
AS 
Begin 
    BEGIN TRANSACTION
    -- Lock the row during update to avoid concurrent duplicates
    select @InvoiceNo=invoice_no from statewise_invoice_no with (UPDLOCK) where invoice_state_code = @StateCode;
    
    -- Increment and update the invoice number (adjust this to match your logic)
    update statewise_invoice_no set invoice_no = @InvoiceNo + 1 where invoice_state_code = @StateCode;
    
    COMMIT TRANSACTION
End

内容的提问来源于stack exchange,提问作者Akash lal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:55:07