I create a PDO Database class to manage connections to the db, query's and transactions. I want to execute all the query's in one transaction, so if one throw an error, the others rollback.
Firs i create the update, delete or insert statement to run, and add the query with a function.
public function addQuery($entity)
{
$this->stack[] = $entity;
}
Then i commit all the querys in the array:
public function commit()
{
self::beginTransaction();
try {
foreach ($this->stack as $entity) {
$entity->execute();
}
self::commitTransaction();
return true;
} catch (Exception $e) {
self::rollbackTransaction();
return false;
}
}
This are the functions that i use:
protected function beginTransaction()
{
$this->dbo->beginTransaction();
}
protected function commitTransaction()
{
$this->dbo->commit();
}
protected function rollbackTransaction()
{
$this->dbo->rollBack();
}
I tried making two updates. The firs that will update a row, and the second will affect 0 rows. But my function don't roll back the firs update.
In your code, you catch an error with catch (Exception $e) then execute a rollback.
If you want to consider an SQL statement that affects 0 rows an error then you could do the following:
public function commit()
{
self::beginTransaction();
try {
foreach ($this->stack as $entity) {
$entity->execute();
# If the statement affected 0 rows, then throw an error
# that will trigger a rollback
if ( $entity->rowCount() == 0 ) {
throw new \Exception("0 rows affected.");
}
}
self::commitTransaction();
return true;
} catch (Exception $e) {
self::rollbackTransaction();
return false;
}
}
Cautionary note This could have very unexpected consequences as certain statements (like SELECT) affect 0 rows and then would trigger a ROLLBACK.
Hope this help!
Related
I have 3 mysql queries that I execute from php, 1 is successful and 2 are not. I have however set a commit and rollback. When I do a var_dump on the the commit it returns true, the query that is successful inserts the value in the db instead of rolling back.
//connection is established
function __construct() {
$this->error['State']=0;
$this->error['Msg']="";
try {
$db_con=mysqli_connect($this->server, $this->dbuname, $this->dbpassw, $this->database);
$this->db_connection=$db_con;
mysqli_autocommit($db_con,FALSE);
mysqli_begin_transaction($this->db_connection);
}
catch (Exception $e) {
$this->setError("Error: Please try again");
}
}
//query and commit
public function result($sql) {
try {
$result=mysqli_query($this->db_connection,$sql);
if($result) {
$this->result=$result;
}else {
$this->setError("Error: Please try again");
}
}
catch (Exception $e) {
$this->setError("Error: Please try again");
}
}//end of result function
public function commit() {
$mysqli=$this->db_connection;
$c=mysqli_commit($mysqli);
if (!$c) {
$this->setError("Error: Please try again");
mysqli_rollback($mysqli);
}else{
return 1;
}
}
//query
public function make($i){
$sql="INSERT INTO t1(tcol1, tcol2, tcol3) VALUES ";
...
$this->commit();
$this->close_conn();
Thanks
The issue was that the tables were isam and the database was innoDB. When I changed the tables to innoDB it started working.
If you tell it to commit it will commit. It won't roll back just because there was an error in one of the statements. You have to check for errors each time you perform a query and then rollback if there is an error. Or just check at the end before committing:
public function commit() {
$mysqli=$this->db_connection;
if ($this->error['State'] === 0) {
$this->setError("Error: Please try again");
mysqli_rollback($mysqli);
} else {
$c = mysqli_commit($mysqli);
if ($c) return 1;
}
}
I have 3 mysql queries that I execute from php, 1 is successful and 2 are not. I have however set a commit and rollback. When I do a var_dump on the the commit it returns true, the query that is successful inserts the value in the db instead of rolling back.
//connection is established
function __construct() {
$this->error['State']=0;
$this->error['Msg']="";
try {
$db_con=mysqli_connect($this->server, $this->dbuname, $this->dbpassw, $this->database);
$this->db_connection=$db_con;
mysqli_autocommit($db_con,FALSE);
mysqli_begin_transaction($this->db_connection);
}
catch (Exception $e) {
$this->setError("Error: Please try again");
}
}
//query and commit
public function result($sql) {
try {
$result=mysqli_query($this->db_connection,$sql);
if($result) {
$this->result=$result;
}else {
$this->setError("Error: Please try again");
}
}
catch (Exception $e) {
$this->setError("Error: Please try again");
}
}//end of result function
public function commit() {
$mysqli=$this->db_connection;
$c=mysqli_commit($mysqli);
if (!$c) {
$this->setError("Error: Please try again");
mysqli_rollback($mysqli);
}else{
return 1;
}
}
//query
public function make($i){
$sql="INSERT INTO t1(tcol1, tcol2, tcol3) VALUES ";
...
$this->commit();
$this->close_conn();
Thanks
The issue was that the tables were isam and the database was innoDB. When I changed the tables to innoDB it started working.
If you tell it to commit it will commit. It won't roll back just because there was an error in one of the statements. You have to check for errors each time you perform a query and then rollback if there is an error. Or just check at the end before committing:
public function commit() {
$mysqli=$this->db_connection;
if ($this->error['State'] === 0) {
$this->setError("Error: Please try again");
mysqli_rollback($mysqli);
} else {
$c = mysqli_commit($mysqli);
if ($c) return 1;
}
}
I define a query to delete a table in database. After that i want to show a message that the query was run successfully. How can I check it with an if statement?
$query = DB::table('user_users')->delete();
return view('datenbank');
When you use delete with the query builder it will return the number of affected rows.
Your if statement would just need to look something like:
$query = DB::table('user_users')->delete();
if ($query) {
//query successful
}
If you want to be more explicit you could do if ($query > 0) {}
If anything goes wrong with the query (an error) it will throw an Exception which will mean that no rows have been affected.
Personally, I think the best solution is to use an if statement like this.
your code
$query = DB::table('user_users')->delete();
return view('datenbank');
Soluction
$query = DB::table('user_users')->delete();
// check data deleted or not
if ($query > 0) {
return response()->json('202 Accepted', 202);
} else {
return response()->json('404 Not Found', 404);
}
If the query did not execute successfully, Laravel would normally throw an error. But if you want to be really sure, you could query the table right after truncating it to make sure there is no data left.
DB::table('user_users')->delete();
// Test if no records are left in the table
$success = DB::table('user_users')->count() === 0;
return view('datenbank', compact('success'));
I will recommend to use try catch because laravel query throw exception when some error occur...
$queryStatus;
try {
DB::table('user_')->where('column',$something)->delete();
$queryStatus = "Successful";
} catch(Exception $e) {
$queryStatus = "Not success";
}
return view('datenbank')->with('message', $queryStatus);
you can do try catch with DB Transaction
try {
DB::beginTransaction();
// your code
DB::commit();
}catch (Exception $e) {
DB::rollback();
// other actions
}
I have a collection of items to save to database, but I want the record to be inserted only if not exists.
I think the most effective way would be to filter the collection before saving. Can Doctrine do it automatically?
Or shall I get all id's of all items in the collection, then query the database for items which do not exists on the list of these id's, then in foreach remove all collection items which we do not need, and finally save collection?
Any better approach suggested?
This is the save function from the Doctrine_Collection class
public function save(Doctrine_Connection $conn = null, $processDiff = true)
{
if ($conn == null) {
$conn = $this->_table->getConnection();
}
try {
$conn->beginInternalTransaction();
$conn->transaction->addCollection($this);
if ($processDiff) {
$this->processDiff();
}
foreach ($this->getData() as $key => $record) {
$record->save($conn);
}
$conn->commit();
} catch (Exception $e) {
$conn->rollback();
throw $e;
}
return $this;
}
I'm not sure where you are getting your collection from, or if you are manually building it, but you might want to try extending the Doctrine_Collection class and overloading the save function like this
<?php
class My_Collection extends Doctrine Collection
{
public function save(Doctrine_Connection $conn = null, $processDiff = true, $createOnly = true)
{
if ($conn == null) {
$conn = $this->_table->getConnection();
}
try {
$conn->beginInternalTransaction();
$conn->transaction->addCollection($this);
if ($processDiff) {
$this->processDiff();
}
foreach ($this->getData() as $key => $record) {
if($createOnly)
{
if ($record->exists())
{
$record->save($conn);
}
}else{
$record->save($conn);
}
}
$conn->commit();
} catch (Exception $e) {
$conn->rollback();
throw $e;
}
return $this;
}
}
I don't know anything about Doctrine, but the MySQL REPLACE query does just what you want - updates existing rows & creates new rows if no primary key matches were found.
I am inserting a lot of records using mysqli commit statement, using this code from http://www.php.net/manual/en/mysqli.commit.php#88857. I then check affected rows using:
$mysqli->affected_rows
but even though all records have been inserted, I get zero affected rows.
How can I check when commit failed and retrieve the error?
Thank you
You could do something like this:
mysqli_autocommit($dbconn, FALSE);
$errors = array();
if (!$mysqli->query(/* some SQL query */)) {
$errors[] = $mysqli->error;
}
// ... more queries like the above
if(count($errors) === 0) {
$mysqli->commit()
} else {
$mysqli->rollback();
print_r($errors);
}
When a query goes wrong, it will add the error to the $errors array, so you will know what went wrong. You could also add keys with identifiers for the queries, so you know which query went wrong.
For better handling, you could write a UnitOfWork class for this:
class UnitOfWork
{
protected $_db;
protected $_errors;
protected $_queries;
public function __construct($db) {
$this->_db = $db;
$this->_errors = array();
$this->_queries = array();
}
public function addQuery($id, $sql) {
$this->_queries[$id] = $sql;
return $this;
}
public function getErrors() {
return $this->_errors;
}
public function try() {
$this->_db->autocommit($this->_db, FALSE);
foreach($this->_queries as $id => $query) {
if ($this->_db->query($query) === FALSE) {
$this->_errors[$id] = $this->_db->error;
}
}
$hasErrors = count($this->_errors);
($hasErrors) ? $this->_db->rollback() : $this->_db->commit();
$this->_db->autocommit($this->_db, TRUE);
return !$hasErrors; // return true on success
}
}
and you could use it like
$unit = new UnitOfWork($mysqli);
$unit->addQuery('foo', 'SELECT foo FROM somewhere')
->addQuery('bar', 'SELECT bar FROM somewhereElse')
->addQuery('baz', 'SELECT baz WITH brokenQuery');
if($unit->try() === FALSE) {
print_r($unit->getErrors());
}
mysqli::affected_rows will return the number of rows affected by the last MySQL operation.
If you are doing something like this (pseudo-code) :
$db->query("insert ...");
$db->query("insert ...");
$db->query("insert ...");
$db->commit();
$num = $db->affected_rows();
You will not get the number of inserted rows : the commit instruction is the last executed one, and it doesn't "affect" any row.
If you want to know whether mysqli::commit succedeed or not, you should check it's return value (quoting) :
Returns TRUE on success or FALSE on
failure.
If it returned true, then all your previous inserts, since the beginning of the current transaction, will have been commited.
And if an error occured, you can use mysqli::errno and/or mysqli::error to get informations about it.