I wonder whether it matters where to start the transaction.
Example 1:
$transaction = Yii::app()->db->beginTransaction();
try
{
$savedSuccessfully = $object->save();
$transaction->commit();
}
catch (Exception $ex)
{
$transaction->rollBack();
$result = $e->getMessage();
}
Example 2:
$transaction = Yii::app()->db->beginTransaction();
try
{
$object = $model()::model()->findByPk(1); //!!!!!!! - line
// what makes the difference
$savedSuccessfully = $object->save();
$transaction->commit();
}
catch (Exception $ex)
{
$transaction->rollBack();
$result = $e->getMessage();
}
Should transaction be started before selecting data from db or or just before updating/inserting data? Will yii take care of it instead of me?
Thanks
Example 2 would be the solution of choice.
By retrieving the model within the transaction, you make sure that it is consistent throughout your changes.
If you retrieve the model, like in example 1, outside the transaction, other threads/users could change the corresponding database entry before you commit your changes. So you could end up with potentially inconsistent data.
Actually 2nd one is correct , if you are saving data which is more critical like banking transaction or payment system then example 2 is very correct way. for example , you are doing some code like this.
insert into table 1
select from table 1
insert into table 2
update table 2
select from table 1.
so if you start transaction from first , it will rollback all query if any query fails which will be more efficient. for example in online payment system.
Related
Check first all model if it can be saved or not.Then save.
i tried to saved data from query. i have 3 query :
$message_header->save();
$save_receive_info->save_receive_info_id = $message_header->id;
$information_receive->save();
$information_chargeline->save_receive_info_details_id = $information_receive->id;
$maklumat_chargeline->save();
so, based on this query, first it saves $message_header, then if save_receive_info, but if receive_info has an error when saving, what can I do?
I mean, I want to check whether all queries are executable or not then save. I have a problem with this because other queries are stored based on other id
please help. and sorry for my broken english.
You can use try and catch block with DB transactions.
use Illuminate\Support\Facades\DB;
DB::beginTransaction();
try {
// your code logic before save
$message_header->save();
$save_receive_info->save_receive_info_id = $message_header->id;
$information_receive->save();
$information_chargeline->save_receive_info_details_id = $information_receive->id;
$maklumat_chargeline->save();
DB::commit();
// all good
} catch (\Exception $e) {
DB::rollback();
// something went wrong
}
You cannot check whether the queries are executable prior to the execution but You can achieve something similar with DB Transactions.
So, if you can use DB Transactions, it will execute all the queries and if something fails, it will undo all the previous queries that you have done.
use Illuminate\Support\Facades\DB;
try {
DB::beginTransaction();
$message_header->save();
$save_receive_info->save_receive_info_id = $message_header->id;
$information_receive->save();
$information_chargeline->save_receive_info_details_id = $information_receive->id;
$maklumat_chargeline->save();
DB::commit();
} catch (\Throwable $th) {
DB::rollBack();
}
I'm using PHP with PDO and InnoDB tables.
I only want the code to allow one user-submitted operation to complete, the user can either cancel or complete. But in the case that the user posts both operations, I want one of the requests to fail and rollback, which isn't happening right now, both are completing without exception/error. I thought deleting the row after checking it exists would be enough.
$pdo = new PDO();
try {
$pdo->beginTransaction();
$rowCheck = $pdo->query("SELECT * FROM table WHERE id=99")->rowCount();
if ($rowCheck == 0)
throw new RuntimeException("Row isn't there");
$pdo->exec("DELETE FROM table WHERE id = 99");
// either cancel, which does one bunch of queries. if (isset($_POST['cancel'])) ...
// or complete, which does another bunch of queries. if (isset($_POST['complete'])) ...
// do a bunch of queries on other tables here...
$pdo->commit();
} catch (Exception $e) {
$pdo->rollback();
throw $e;
}
How can I make the cancel / complete operations a critical section? The second operation MUST fail.
Another solution just for completeness:
private function getLock() {
$lock = $this->pdo->query("SELECT GET_LOCK('my_lock_name', 5)")->fetchColumn();
if ($lock != "1")
throw new RuntimeException("Lock was not gained: " . $lock);
}
private function releaseLock() {
$releaseLock = $this->pdo->query("SELECT RELEASE_LOCK('my_lock_name')")->fetchColumn();
if ($releaseLock != "1")
throw new RuntimeException("Lock not properly released " . $releaseLock);
}
MySQL GET_LOCK() documentation
The code is fine, with one exception: Add FOR UPDATE to the initial SELECT. That should suffice to block the second button press until the first DELETE has happened, thereby leading to the second one "failing".
https://dev.mysql.com/doc/refman/5.5/en/innodb-locking-reads.html
Note Locking of rows for update using SELECT FOR UPDATE only applies
when autocommit is disabled (either by beginning transaction with
START TRANSACTION or by setting autocommit to 0. If autocommit is
enabled, the rows matching the specification are not locked.
I have this code to insert a new row into MySQL DB using PDO:
$query = INSERT INTO asset_positions (pos_asset_id, pos_latitude, pos_longitude, pos_timestamp) VALUES (:pos_asset_id, :pos_latitude, :pos_longitude, :pos_timestamp)
$statement = $pdo->prepare($query);
$array = [':pos_asset_id' => 1, ':pos_latitude' => -8.5, ':pos_longitude' => 125.5, ':pos_timestamp' => 1398160487];
$statement->execute($array);
echo $pdo->lastInsertId();
The query runs without any error shown. The newly inserted row ID is echoed. However, when i look in the DB, it only insert the latitude, longitude and timestamp. The pos_asset_id field in the newly inserted row is empty.
Could somebody point out where is the problem? There is no error message displayed. I've been trying to solve this for hours without avail.
Ps. This is my first time using PDO, so please bear with me. Thanks.
EDIT
Solved! I didn't notice that there's a FK relation between asset_positions.pos_asset_id and asset.asset_id. Once i remove this relationship constrains, the INSERT works properly now, the pos_asset_id value is inserted to the record.
Anyway, thanks all! :)
try running with error catching, it will give you better understanding of what is happening.
try {
$stmt->execute();
} catch (PDOException $p) {
echo $p->getMessage();
} catch (Exception $e) {
echo $e->getMessage();
}
But your common sense and user3540050 are right... it's a column related issue probably
For relational databases like mysql, transaction handling in PHP is just like.
Begin transaction
...
Insert queries
...
Update queries
...
if error in any query then
Rollback transaction
...
at end, if no error in any query then
Commit transaction
How to handle transactions in neo4jphp?
I have tried same like but there was failure. Even after rollback changes were saved.
I was doing like this.
//$client = Neo4jClient
$transaction = $client->beginTransaction();
...
//Insert queries
...
//Update queries
...
//if error in any query then
$transaction->rollback();
...
// at end, if no error in any query then
$transaction->commit();
Check following code.
//$client = Neo4jClient
$transaction = $client->beginTransaction();
$dataCypherQuery = new Query($client, $dataQuery, $params);
Instead of getting resultset from query, we need to add statement into transaction.
// $dataResult = $dataCypherQuery->getResultSet(); // don't do this for transaction
Important : Pass query object to transaction's add statements method.
$dataResult = $transaction->addStatements($dataCypherQuery);
We can pass true as parameter indicating transaction commit.
//$dataResult = $transaction->addStatements($dataCypherQuery, true);
If there is an error, changes are automatically rolled back.
You can check $dataResult variable for validity, result should be returning something.
if (0 == $dataResult->count()) {
$transaction->rollback();
}
At end, if no error in any query then
$transaction->commit();
For more info see Cypher-Transactions
When I try to run the code below:
$conBud = Propel::getConnection(MyTestBudgetPeer::DATABASE_NAME); // DATABASE_NAME = 'Budget'
$conBud->beginTransaction();
$conIn = Propel::getConnection(MyTestInvoicePeer::DATABASE_NAME); // DATABASE_NAME = 'Invoice'
$conIn->beginTransaction();
$idcl = '1235';
try
{
// Do db udpates related to database Budget (here around 15 tables and 500 data rows are update)
// budExModel is a table, primary id from this table is used to update InvoiceTest Table below
$idtest = $budExModel->save($conBud);
...
// Code to Update one table for database Invoice (only one table)
// Create a Criteria object that will select the correct rows from the database
$selectCriteria = new Criteria();
$selectCriteria->add(InvoiceTestPeer::IDCL, $idcl, Criteria::EQUAL);
$selectCriteria->setDbName(InvoiceTestPeer::DATABASE_NAME);
// Create a Criteria object includes the value you want to set
$updateCriteria = new Criteria();
$updateCriteria->add(InvoiceTestPeer::IDTEST, $idtest);
// Execute the query
BasePeer::doUpdate($selectCriteria, $updateCriteria, $conIn);
$conBud->commit();
$conIn->commit();
} catch (Exception $e)
{
$conBud->rollBack();
$conIn->rollBack();
}
I get error: ["Unable to execute UPDATE statement [UPDATEinvoice_testSETIDTEST=:p1 WHERE invoice_test.IDCL=:p2 ] [wrapped: SQLSTATE[HY000]: General error: 1205 Lock wait timeout exceeded; try restarting transaction]
Lock wait timeout exceeded; try restarting transaction
Error I am getting is for the table/db which has lesser data and only processes for one table.
Is this not allowed for mysql?
I already changed innodb_lock_wait_timeout and tried restarting mysql so they are not an option.
Edit: Here IDTEST I am trying to udpate for table invoice_test is an fk from Table Budget_test from database Budget.
It seems that the reason behind the error was foreign key constraint on idtest.
Here $idtest is primary_key of newly saved row from table bud_ex; This retrieved from last_insert_id, this was the same id that is trying to be used in incoice_test table. Problem here is, I was trying to use $idtest, but the connection/transaction wasn't committed hence when trying to use this id, it threw an fk constraint error which in return lock time out exceeded.
To get this to work I had to run a query to set foreign key checks as false for invoice database.
set foreign_key_checks = 0;
Along with this I made certain few changes to the php code to make the try catch block more concrete.
$con1->beginTransaction();
try
{
// Do stuff
$con2->beginTransaction();
try
{
// Do stuff
$con2->commitTransaction();
}
catch (Exception $e)
{
$con2->rollbackTransaction();
throw $e;
}
try
{
$con1->commitTransaction();
}
catch (Exception $e)
{
// Oops $con2 was already committed, we need to manually revert operations done with $con2
throw $e;
}
}
catch (Exception $e)
{
$con1->rollbackTransaction();
throw $e;
}