Transaction usage and code examples in [PHP] Yii2

Transaction usage and code examples in Yii2

Preface

In general, we do business logic, will not only be associated with a data table, so we will face transaction problems.

Database Transaction refers to a series of operations performed as a single logical unit of work, either completely or completely not. Transaction processing ensures that data-oriented resources are not permanently updated unless all operations within the transactional unit are successfully completed. By combining a set of related operations into a unit that either succeeds or fails altogether, error recovery can be simplified and the application becomes more reliable. To become a transaction, a logical unit of work must satisfy the so-called ACID (atomicity, consistency, isolation and persistence) attributes. Transaction is a logical unit of work in database operation. Transaction management subsystem in DBMS is responsible for transaction processing.
Get ready

The database engine is innodb
The yii version used in this article is 2.0.5, so long as it is above 2.0, there is no problem.
The running environment is PHP 7.0.0, Mysql 5.6
Transactions in Yii

Handling exceptions

/**
 * Test transaction
 */
public function actionTest(){
    //Create a transaction
    $tr = Yii::$app->db->beginTransaction();
    try {
        for($i=1;$i<=3;$i++){
            $test = new Areas();
            $test->name = 'name'.$i;
            $test->sort=1;
            if($test->save()){
                echo "save $i | ";
            }

        }
        $test = new Areas();
        $test->name = 'ab'.$i;
        $test->sorta=1; //Write fields that do not exist
        if(!$test->save()){
            "save fail"; //Output without writing
        }
        //Submission
        $tr->commit();
    } catch (Exception $e) {
        //RollBACK
        $tr->rollBack();
        echo  "rollback";
    }
}

Operation results
save 1 | save 2 | save 3 | rollback
Note that because the last data was not inserted successfully, the transaction rollback was triggered, so no new data was generated in the data table.
The cause of the transaction rollback is an exception to the code.
Failure to process data
Generally speaking, there is no such obvious exception in our running code, which will be eliminated in the development and testing process.
What really causes data to roll back is a problem in one of our businesses, which results in data not being written in part.

/**
 * Test transaction
 */
public function actionTest(){
    //Create a transaction
    $tr = Yii::$app->db->beginTransaction();
    try {
        for($i=1;$i<=3;$i++){
            $test = new Areas();
            $test->name = 'name'.$i;
            $test->sort=1;
            if($test->save()){
                echo "save $i | ";
            }
        }
        $test = new Areas();
        $test->name = null; //The name of the database design cannot be empty, resulting in write failure artificially.
        $test->sort=1; //Write fields that do not exist
        if(!$test->save()){
           echo  "save fail"; //Output without writing
        }
        //Submission
        $tr->commit();
    } catch (Exception $e) {
        //RollBACK
        $tr->rollBack();
        echo  "rollback";
    }
}

The results are as follows. Three data are inserted into the database.

save 1 | save 2 | save 3 | save fail
That is to say, if a data table is not written to data because of business logic, there is no corresponding rollback.

The improvement scheme is as follows.

/**
 * Test transaction
 */
public function actionTest(){
    //Create a transaction
    $tr = Yii::$app->db->beginTransaction();
    try {
        for($i=1;$i<=3;$i++){
            $test = new Areas();
            $test->name = 'name'.$i;
            $test->sort=1;
            if($test->save()){
                echo "save $i | ";
            }
        }
        $test = new Areas();
        $test->name = null; //The name of the database design cannot be empty, resulting in write failure artificially.
        $test->sort=1; //Write fields that do not exist
        if(!$test->save()){
           throw new \yii\db\Exception(); //Manually throw the exception and capture it from below.
        }
        //Submission
        $tr->commit();
    } catch (Exception $e) {
        //RollBACK
        $tr->rollBack();
        echo  "rollback";
    }
}

As a result, no new data was inserted into the database and transactions were rolled back.

save 1 | save 2 | save 3 | rollback

Decentralized data processing

Due to the complexity of the actual project, our database operations are scattered in different models.

So the actual project code will not look like that.

Simulate requirements

Receiving parameters:

Name
Gender
autograph
Business Processing Processes:
Receiving parameters
The user's uid is obtained from the emitter, which adds a digit to the corresponding data table.
Write the name, gender, signature and the next uid to the user information table
Initialize user balance sheet

Rollback trigger timing:
Initialize balance sheet without importing uid export without writing data
Actual code
//Controller

/**
 * Test Transaction - Registered Users
 */
public function actionReg()
{
    //Acquisition request
    $request = Yii::$app->request;
    //Setting the return format
    $response = Yii::$app->response;
    $response->format = \yii\web\Response::FORMAT_JSON; //Return to json

    //Test the code to remove the authentication step

    $name = $request->get("name");
    $gender = $request->get("gender");
    $sign = $request->get("sign");

    //Test code, omit parameter checking steps

    $tr = Yii::$app->db->beginTransaction();
    try {
        //Get uid
        $uid = App::getSeNo();
        UserProfile::add($uid, $name, $gender, 1, $sign);
        $user_balance =    UserBalance::initUserBalance($uid);
        $tr->commit(); //Submission of data
    } catch (Exception $e) {
        //RollBACK
        $tr->rollBack();
        return $e->getMessage(); //Returns custom exception information
    }
    return $user_balance;
}

//UserProfile

/**
 * Adding user information
 * @param $user_id
 * @param $nikename
 * @param $gender
 * @param $user_type
 * @param string $intro
 * @return UserProfile
 * @throws \Exception
 */
public static function add($user_id, $nikename, $gender,$user_type,$intro="") {
    $model = new UserProfile();
    $model->gender = $gender;
    $model->nikename = $nikename;
    $model->user_id = $user_id;
    $model->user_type=$user_type;
    $model->intro=$intro;
    $model->update_time = time();
    $insert =$model->insert();
    if(!$insert){
        throw new Exception("No user data was written");
    }
    return $model;
}

//UserBalance

/**
 * Initialize user's withdrawable balance
 * @param $user_id
 */
public static function initUserBalance($user_id){
    $info=self::find()->where(['user_id'=>$user_id])->one();

    if(!$info ){
        $model=new UserBalance();
        $model->user_id = $user_id;
        $model->price= "0";
        $model->update_time=time();
        $insert = $model->insert();
        if(!$insert){
            throw new Exception("User balance not initialized");
        }
        $info=$model;
    }
    return $info->attributes;
}

The normal results are as follows

{"id":124,"user_id":1473179883,"price":"0","update_time":1473179883}
If the user_id of the initialization user balance part is not passed successfully, the result is as follows

"No initialized user balance"
We can locate the error location according to the specific situation and correct it in time.

Transaction

As can be seen from the actual code above, transactions are created, so long as within the scope, even the other models introduced can return the exception NG and complete the rollback operation.

Usually, the whole Yii application uses the same database connection, or singleton.

In yii db Connection, transaction objects are cached:

class Connection extends Component
{
// Save the valid Transaction object of the current connection
private $_transaction;

// If a transaction object is cached and the transaction object is valid, the transaction object is returned.
// Otherwise return null
public function getTransaction()
{
    return $this->_transaction && $this->_transaction->getIsActive() ? $this->_transaction : null;
}

// See how transaction objects are used when transactions are enabled
public function beginTransaction($isolationLevel = null)
{
    $this->open();
    // If the cached transaction object is valid, the transaction object in the cache is used
    // Otherwise, create a new transaction object
    if (($transaction = $this->getTransaction()) === null) {
        $transaction = $this->_transaction = new Transaction(['db' => $this]);
    }
    $transaction->begin($isolationLevel);
    return $transaction;
}

}

Therefore, it can be considered that the entire Yii application uses the same Transaction object, that is to say, Transaction:_level is continuous throughout the life cycle of the application. This is the key and prerequisite for transaction nesting.

summary

Technical level growth comes from trampling pits, trampling pits from business growth.

Have time to talk about career planning and development. )

Reference material

http://www.yiichina.com/question/1576
http://www.digpage.com/transaction.html
http://baike.baidu.com/link?url=1-LvBZQ2hgY9-9HcjQrzf3KCsf-iGzRK3FggAYC5mldYukCQ44B2aNTds7ODDz7bLBNtYfxpjH_tpSsNeoMAsK

Keywords: Database PHP MySQL JSON

Added by infratl on Thu, 20 Jun 2019 04:17:01 +0300