I am totally confused about how we should write SQL transactions in PHP.
We have a invoice payment section, so we have to do
Make the DB changes in the invoice tables as per the payment details updateInvoice()
Do data insertions in the Journal as per the payment amount addJournals()
Update/Insert the payment details in the reports for reporting section setUpReport()
so we included all the three actions into a single transaction
try {
$this->conn->beginTransaction();
updateInvoice();
addJournals();
setUpReport();
$this->conn->commit();
} catch (Exception $ex) {
$this->conn->rollback();
}
There are around 8-10 tables involved in this transactions and it seems the transactions are locking all these tables.
Also we have noticed this process is taking too much time and there are occasional deadlocks happening during this process. On doing some research I understood we need to make the above transaction atomic and simple. And most of the suggestion points towards splitting the transaction into multiple transactions.
So I was planning to make separate transaction for each function like
try {
$this->conn->beginTransaction();
updateInvoice();
$this->conn->commit();
} catch (Exception $ex) {
$this->conn->rollback();
}
try {
$this->conn->beginTransaction();
addJournals();
$this->conn->commit();
} catch (Exception $ex) {
$this->conn->rollback();
}
try {
$this->conn->beginTransaction();
setUpReport();
$this->conn->commit();
} catch (Exception $ex) {
$this->conn->rollback();
}
If I restructure the code like this, if an error happens on setUpReport() it will be difficult to revert the actions in the above 2 transactions.
So I am really confused how we need to structure the transaction.
I had the same problem, I changed max_execution_time = 60
If you can't change value, make reconnect
EXAMPLE:
echo "Other queries in your system / framework.....";
$this->sendQuery("SQL: SELECT * from session_tab...");
$this->sendQuery("SQL: SELECT * from privileges... ");
$this->sendQuery("SQL: .....");
$this->sendQuery("SQL: ......");
$this->sendQuery("SQL: SELECT * from table..."); // Waiting long time..
echo "After that you want execute next queries.. " ;
$this->db->disconnect();
$this->db->connect();
try {
$this->conn->beginTransaction();
setUpReport();
$this->conn->commit();
} catch (Exception $ex) {
$this->conn->rollback();
}
Related
I'm looking at executing a query like this:
Yii::$app->db->createCommand()->dropTable($r)
I have been using execute() http://www.yiiframework.com/doc-2.0/yii-db-command.html#dropTable()-detail in order to do so, but I'm unsure from the docs if I should be doing this. I in particular want to return the success or failure of dropping a table in this instance. What is the correct way? I see that execute only seems to return the number of rows affected?
Yes the execute return the number of the row affect. In this case one row is affected . For a better error mnaagement you can also manage the error situation
adding
use yii\base\Exception;
use yii\web\NotFoundHttpException;
and the in you function adding
try {
Yii::$app->db->createCommand()->dropTable($r)->execute();
}
catch (\yii\db\Exception $e) {
// yii db exception
$populateError = $e->getMessage();
}
catch (\Exception $e) {
// not a db exception
$populateError = $e->getMessage();
}
In this way if you have an error (eg: the table to be dropped not exist) you can manage it.
My understanding is the InnoDB is now the default engine for MySQL. With that knowledge, I am beginning to delve into transactions.
Here is what I have so far...
try{
$pdo->beginTransaction();
$stmnt = $pdo->prepare ("delete from playing where uniq = :uniq");
$stmnt->bindParam (':uniq',$uniq);
$stmnt->execute();
$stmnt = $pdo->prepare ("insert into removals (playdate, time, vid) values (:playdate, :time, :vid");
$stmnt->bindParam (":playdate",$playdate);
$stmnt->bindParam (":time", $time);
$stmnt->bindParam (":vid", $vid);
$stmnt->execute();
$pdo->commit();
echo "1"; // success
return;
}
catch (PDOException $e){
$pdo->rollback();
echo $e->getMessage();
}
This is called by jQuery with a result of "1" indicating a success.
If I understand this correctly, if bot statements execute successfully, they will both be "committed" however it either fails, no database activity will take place and an error message will be generated detailing the first statement execution that fails.
My real question is whether the begin transaction and commit should reside within or outside the try...catch block.
Thanks,
-dmd-
For readability and cleanliness, yes it should be inside the try block. But it really does not matter. It just declares what to commit or rollback if you call roll back.
What is better way to begin a transaction?
Inside procedures or PHP functions?
For example I calling MySQL procedure like this:
function sendLeaguesToDb(){
$leagues = "";
try{
$this->PDO->beginTransaction();
$stmt = $this->PDO->prepare("call insupd_Leagues(:id,:name,:country,:sport_id,:his_data,:fixtures,:livescore,
:numofmatches,:latestmatch)");
$leagues=$this->soccer->GetAllLeagues();
foreach($leagues as $key=>$value){
$stmt->bindParam(':id',$value->Id);
$stmt->bindParam(':name',$value->Name);
$stmt->bindParam(':country',$value->Country);
$stmt->bindParam(':sport_id',$value->Sport_Id);
$stmt->bindParam(':his_data',$value->Historical_Data);
$stmt->bindParam(':fixtures',$value->Fixtures);
$stmt->bindParam(':livescore',$value->Livescore);
$stmt->bindParam(':numofmatches',$value->NumberOfMatches);
$stmt->bindParam(':latestmatch',$value->LatestMatch);
$stmt->execute();
$this->PDO->commit();
}
}
catch(XMLSoccerException $e){
echo "XMLSoccerException: ".$e->getMessage();
}
catch(PDOException $e){
echo "PDOException: ".$e->getMessage();
$this->PDO->rollback();
}
}
Is this good way if I want to send/get data fastest possible every minute/hour?
It depends on what you're trying to achieve.
If you want to see all the inserts as an 'atomic operation' you are doing right, as if one call to the SP fails, the rollback will undo all the changes made from the previous calls
If, otherwise, you want to "isolate" every single SP call, assuring that if it succedes the results are stored in the DB, you have to start and end the transaction inside the SP
I think the preferred solution is the first
EDIT: one thing i'm noting now: the commit should be after the for :
try{
$this->PDO->beginTransaction();
$stmt = $this->PDO->prepare("call insupd_Leagues(:id,:name,:country,:sport_id,:his_data,:fixtures,:livescore,
:numofmatches,:latestmatch)");
$leagues=$this->soccer->GetAllLeagues();
foreach($leagues as $key=>$value){
$stmt->bindParam(':id',$value->Id);
$stmt->bindParam(':name',$value->Name);
$stmt->bindParam(':country',$value->Country);
$stmt->bindParam(':sport_id',$value->Sport_Id);
$stmt->bindParam(':his_data',$value->Historical_Data);
$stmt->bindParam(':fixtures',$value->Fixtures);
$stmt->bindParam(':livescore',$value->Livescore);
$stmt->bindParam(':numofmatches',$value->NumberOfMatches);
$stmt->bindParam(':latestmatch',$value->LatestMatch);
$stmt->execute();
}
//move your commit here
$this->PDO->commit();
}
I am making an update for multiple entries with CodeIgniter, my doubt is if I am using the transaction correctly... right now I have this
$this->db->trans_begin();
foreach ($query->result_array() as $b){
try{
$querystring = "somemysql to update... pure mysql";
$query = $this->db->query($querystring);
}catch(Exception $e){
$this->db->trans_rollback();
}
}
$this->db->trans_commit();
At the moment, i havent get any error... and seems to be working fine, what I dont know is this:
If im for the update 4/100 lets say, will this rollback the #4 only? or the #1,#2,#3 as well?
Any idea?
Thanks.
The way you have written this, if there is an exception on 4; 1,2,3 will be rolled back..
BUT it will continue on with 5+, presumably this is not what you want!
I also assume trans_rollback() ends the transaction so the next queries will be run outside of the transaction and won't be rolled back if another exception occurs. You may even get an uncaught exception on trans_commit() or another attempted trans_rollback().
I am guessing what you want is:
$this->db->trans_begin();
try{
foreach ($query->result_array() as $b){
$querystring = "somemysql to update... pure mysql";
$query = $this->db->query($querystring);
}
$this->db->trans_commit();
}
catch(Exception $e){
$this->db->trans_rollback();
}
Sorry for this beginners question and i'm not a PHP developer, but now i'm trying to learn it.
i want to add record in MySQL data base and i'm using transactions lock.
my code is as below.
$SqlQuery="INSERT INTO tab_photo VALUES('$PhotoID','$ProjectId','$Day','$barCode','$photoName','$PhotoXml')";
$waiting = true;
while($waiting) {
try {
// save border data
$stmt = $conn->prepare($SqlQuery);
$conn->beginTransaction();
$stmt->execute();
sleep(1);
$x=$conn->commit();
echo "x value-".$x;
echo "Success";
$waiting = false;
}
catch (PDOException $e){
echo "Failled :".$PhotoID."-".$PhotoID;
if(stripos($e->getMessage(), 'DATABASE IS LOCKED') !== false) {
// This should be specific to SQLite, sleep for 0.25 seconds
// and try again. We do have to commit the open transaction first though
$conn->commit();
usleep(250000);
} else {
$conn->rollBack();
throw $e;
}
}
}
in here as output it gives,
x value-1 Success
but actually this record doesn't add to the database.
My Questions:
Even the commit is successful(output 1) how does it not added to the database?
how can i check whether record is added to database? ( Is there any way to find it without write select statement?
As I understand, you expect that PDOException will be thrown when statement is failed to execute. But as I can see, exception is not thrown by default in such cases.
See how you can change that here
Suppose in your case you should have a code like this:
$conn = new PDO($connection_string);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // this will force PDO to throw exception when SQL statement fails instead of simply setting an error.
Suppose this will work fine for you.
Please note that you should not use
$SqlQuery="INSERT INTO tab_photo VALUES('$PhotoID','$ProjectId','$Day','$barCode','$photoName','$PhotoXml')";
Instead of that, you should use parameters binding:
$SqlQuery="INSERT INTO tab_photo VALUES(:PhotoID,:ProjectId,:Day,:barCode,:photoName,:PhotoXml)";
$stmt = $conn->prepare($SqlQuery);
$conn->beginTransaction();
$stmt->execute(array(':PhotoID' => $PhotoID, ':ProjectId' => $ProjectId, ....));
sleep(1);
See this for more details.