After looking into using try catch blocks for my Pdo statements is it really a benefit? Does this just slow your code down?
I believe that there should be a try catch around the connection command in case the database connection fails. But does there really need to be try catch around each pre prepared statement? These should never change and never really error out.
Any thoughts?
I'm using Php and MySql.
There is no benefit to this:
try {
// exec statement
// exec statement
}
catch (Exception $e) {
// do nothing
}
If you aren't going to do anything with the error and provide a reasonable solution, then you may as well let the exception bubble up to the application's main "something went wrong" error page.
But you may want to do this:
// begin transaction
try {
// exec statement
// exec statement
// commit transaction
}
catch (Exception $e) {
// rollback transaction
// handle error or rethrow $e;
}
And prepared statements can throw exceptions. Perhaps a unique key is violated, or a foreign key constraint is, etc.
But the main point is, you don't use exceptions to hide or silence errors. You use them to catch an error, process it intelligently, and continue on accordingly.
Related
Let's say I have a function that modifies a database. This could be anywhere from one query to multiple queries in a transaction. In any of these modifications we want to make sure all queries are successful. Thankfully for transactions, if one of these fails I have a way of making sure none of the changes are made permanent.
Now let's say I also have a log that needs to be written to to make note of the change (s). How could I make sure that both the modifications are made and the log written?
I could always do a try/catch on either but say for instance my queries are successful but my log is unsuccessful. How could I then go back and undo all of the modifications to the database?
Would something like this be effective and/or advisable?
try {
$db->connect();
$db->mysqli->begin_transaction();
// series of queries
...
// log to file
$db->mysqli->commit();
} catch (exception $e) {
$db->mysqli->rollback();
$db->mysqli->close();
}
Wanted to make note of some behaviors I observed while testing this. If you place the commit() before the log, the database changes will not roll back even if an exception is caught while logging.
The data will be stored in the database once you call commit() or trigger an implicit commit. For example, this will not make any changes to the database because there is no commit:
$mysqli->begin_transaction();
$mysqli->query('INSERT INTO test1(val) VALUES(2)');
// end of script's execution
In your little example, the transaction will be rolled back as long as your logging functionality stops the execution or throws an exception.
$db->connect();
try {
$db->mysqli->begin_transaction();
// series of queries
...
// log to file
throw new \Exception(); // <-- This will prevent commit from executing.
$db->mysqli->commit();
} catch (\Exception $e) {
// An exception? That's ok, we'll try something different instead.
$db->mysqli->rollback();
}
You need to call rollback only if you want to recover from the exception and clear the unsaved buffer. The only time you should ever catch exceptions is if you want to somehow recover from it and do a different thing. Otherwise, your code could be simplified by removing try-catch and rollback altogether.
However, it is a good idea to catch exceptions in transactions and roll it back explicitly as soon as possible, even when you do not want to recover. It can prevent an issue if you catch the exception somewhere else in your code and you try to execute other DB actions. The unsaved data is still in the buffer until you close the session or roll back! This is why you would often see code like this:
$db->connect();
try {
$db->mysqli->begin_transaction();
throw new \Exception(); // <-- Something throws an exception
$db->mysqli->commit();
} catch (\Exception $e) {
// rollback unsaved data, but rethrow the exception as we do not know how to recover from it here
$db->mysqli->rollback();
throw $e;
}
What is the syntax preferred while using PDO transaction and try catch and Why?
$dbh->beginTransaction();
try {
} catch (Exception $e) {
}
OR
try {
$dbh->beginTransaction();
} catch (Exception $e) {
}
The existent answers seem to suggest that since $dbh->beginTransaction() could throw a PDOException it should be in the same try block of the actual transaction code, but this means that the rollBack() code itself will be wrong, because it could invoke a rollBack() without there being a transaction, which could also throw another PDOException.
The right logical ordering of this is that you put the code you want executed in one transaction in one catch block after the transaction has been created. You could also check that the return of beginTransaction() is true before proceeding. You could even check that the database session is in a transaction before calling rollback().
if ($dbh->beginTransaction())
{
try
{
//your db code
$dbh->commit();
}
catch (Exception $ex)
{
if ($dbh->inTransaction())
{
$dbh->rollBack();
}
}
}
Keep in mind that you could still, at least in theory, get an exception from beginTransaction() and rollBack() so I would put this in a separate function and enclose the invocation in another try-catch block.
You could also bubble the exception you get up to catch it and log all Exceptions in one place. But remember that some exceptions could be data integrity errors such as duplicate keys or invalid foreign keys, which would not be a database fault as such, but most probably a bug in your code.
With this approach, the main thing to keep in mind here is that the two try-catch blocks have a slightly different purpose. The inner one is purely to ensure that multiple queries are executed and committed atomically in one transaction and if something happens they are rolled back. The external try-catch would be to detect erroneous situations and log it, or whatever you would want to do if you have a problem with your database.
try {
$dbh->beginTransaction();
} catch (Exception $e) {
}
Simply because an exception could be thrown as you attempt to begin the transaction.
Note that you can place another try catch block inside the initial try.
The second one usually makes most sense. Since you may not always know what will cause the transaction to fail, you would want the rollback (and possibly commit) logic available in the catch, so you'd want to put the beginTransaction() inside of the try.
In addition to the try/catch make sure you set the error mode attribute:
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
And if anything you should catch the PDOExecption just because you should not treat all exception in the same way.
try {
$dbh->beginTransaction();
//do stuff
$dbh->commit();
} catch (PDOException $e) {
$dbh->rollBack();
//...
throw $e;
}
But if you still want to catch other exception add more catch blocks:
try {
....
} catch (PDOException $e) {
//handle pdo exception
}catch (Exception $ex) {
//handle others differently
}
Ok, I understand the "accepted answer" that was given for this question, but it's still not clear to me what kind of code should I put in finally blocks.
If the use of finally is to get the non-catched exceptions thrown and give a general error message for the system not explode for the user, wouldn't appear two error messages for the user if some exception was catched?
[Edit]
Like #MarkBaker said, the "finally" isn't for catch the uncaught exceptions, the generic catch(Exception $e) do that. So... for what it's useful? Or, in other words, what the finally block does that I can't do after the try/catch blocks without finally?
Maybe the following explanation will better help you understand how it works:
try {
function1();//this might throw an exception
function2();//if we want function2 to be executed regardless
//if an exception was thrown from function1() - this
//is not a good place to call it!
} catch (Exception $e) {
echo $e->getMessage();
} finally {
function2();//then the right place to write it will be in a finally clause.
}
When an exception is thrown from function1() - function2() will not be executed - the execution will "jump" to the catch section. If we want function2() to be executed regardless if an error was thrown, for example, if function1() opens a connection to the DB and runs some selects and function2() closes that connection, then we'd better place the call to function2() in the finally block that follows the catch
The 'finally' block should hold code you want executed regardless of the outcome of the try/catch block. For example, if you try to query a database and catch the error, you would still likely want to close the database connection, regardless of whether the database operation succeeded or not. See below:
open_database_conn();
try{
query_database();
return_result();
}
catch(Exception $e){
echo $e->getMessage();
}
finally{
close_database_conn();
}
Should I do
$dbh->beginTransaction();
try{
Or
try{
$dbh->beginTransaction();
It doesn't really matter, it will run the code indifferent of it's position.
But you want to put the rollback() in the catch, and with that setup it's not readable if you put begin outside.
I would vote for inside the try.
It probably doesn't really matter. However, it's better to place the beginTransaction outside the try. When the beginTransaction fails, it should not execute the rollback.
add it inside the try/catch block, so you can catch any PDOException:
try {
$dbh->beginTransaction(); // start transaction
$stmt = $dbh->query($query); // run your query
$dbh->commit(); // commit
} catch(PDOException $ex) { // if exception, catch it
$dbh->rollBack(); // rollback query
echo $ex->getMessage(); // echo exception message
}
In this case it doesn't matter, as beginTransaction will return false on failure. If it threw exceptions, you would want it inside of a nested try block (otherwise you'd execute rollBack() after catching the exception which would fail because no transaction was started).
If you want to catch the possible errors that the beginTranscation method should throw, go for the second one.
I get the following error when I try to perform a rollback.
Rollback failed. There is no active transaction.
I searched for this issue and found a few suggestions that recommend disabling the autocommit setting. But I am unsure how to do this. Is there any other reason for the above error? I am using MYSQL and Zend and my php.ini file loaded the required drivers.
MySQL works in autocommit by default. You can turn it off with:
$connection->setAttribute(Doctrine_Core::ATTR_AUTOCOMMIT, false);
Another idea I have is you didn't start the transaction (which should disable autocommit in Doctrine):
$connection->beginTransaction();
The class UnitOfWork.php has a catch block like:
catch (Exception $e) {
$this->em->close();
$conn->rollback();
throw $e;
}
Of course, if your class is not ready to find an already closed entity manager and therefore connection, you will have this exception. The worst thing about it is that it masks the underlying cause of the exception, since the error was caused before the catch block being executed. To fix it, you can do a simple check in your class' catch block:
catch(Exception $e) {
if($conn->isTransactionActive()) {
[rollback]
[close]
[rethrow] (if necessary)
}
}
Found the problem.....I have called rollback() function 2 times in different places in the code
You can check if the transaction exists with transaction nesting level:
$this->em->getConnection()->getTransactionNestingLevel()
If nesting level exist grather than 0, then you can do a rollback