Handling Transactions with Connector/Python
Last updated on July 27, 2020
Transactions #
A transaction is a way to execute a set of SQL statements such that either all of the statements execute successfully or none at all. In other words, all the statements in a transaction are executed as a single unit. If any one of the statements in a transaction fails, then the database is rolled back to the point at which transaction began.
Note: In MySQL, only InnoDB table supports transaction.
autocommit mode in MySQL #
By default, whenever you execute a SQL statement, MySQL immediately writes the result of the statement to the database. Another way to think about this is that the MySQL executes each statement as a separate transaction which implicitly starts when you execute the statement and ends as soon as the statement is done executing. This behavior is called the autocommit mode.
To view the current autocommit setting, type the following in the MySQL shell:
1 2 3 4 5 6 7 8 9 | mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
1 row in set (0.01 sec)
mysql>
|
As you can see, the @@autocommit
system variable is set to 1 which means that the autocommit mode is enabled.
If you disable the autocommit mode then the SQL statements that follow will be part of a transaction, and you will have to commit them manually using the COMMIT
statement before the MySQL session ends, otherwise, the changes made by the statements will be rolled back.
To disable the autocommit mode set AUTOCOMMIT
to 0
, as follows:
1 2 | mysql> SET AUTOCOMMIT=0;
Query OK, 0 rows affected (0.00 sec)
|
To verify the changes, let's print the value of @@autocommit
system variable again.
1 2 3 4 5 6 7 8 9 | mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 0 |
+--------------+
1 row in set (0.00 sec)
mysql>
|
The autocommit mode is now disabled. From now on, you will have to explicitly commit the statements before the MySQL session ends. If you fail to do so, changes made by the statements will not be saved into the database.
Let's see this behavior in action:
Right now we have 4080 rows in the city
table.
1 2 3 4 5 6 7 | mysql> select count(*) from city;
+----------+
| count(*) |
+----------+
| 4080 |
+----------+
1 row in set (0.00 sec)
|
Let's now see what would happen if we try to execute a SQL statement which deletes all the rows from the city
table.
1 2 3 4 5 6 7 8 9 10 11 12 13 | mysql>
mysql> delete from city;
Query OK, 4080 rows affected (0.30 sec)
mysql> select count(*) from city;
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.00 sec)
mysql>
|
It looks like all the rows from the city
table is deleted. However, this is not case as we haven't committed the transaction (via the COMMIT
statement) yet.
Quit the current MySQL session by hitting Ctrl+D
or typing exit
.
1 2 3 4 | mysql>
mysql> exit
Bye
$
|
Relaunch the mysql-client and query the city
table again.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $ mysql -u root -p
Enter password:
mysql>
mysql> use world
Database changed
mysql>
mysql>
mysql> select count(*) from city;
+----------+
| count(*) |
+----------+
| 4080 |
+----------+
1 row in set (0.00 sec)
mysql>
|
As expected, no rows are deleted from the city
table. However, if we hadn't disabled the autocommit mode (SET AUTOCOMMIT=0
) before executing the DELETE
statement, all the rows from the city
table would have been deleted.
Also, when we restart a new session the autocommit mode is again turned on. Thus, again individual SQL statements will be executed in a separate transaction.
Now, you know how the autocommit works, let's now shift our attention to how MySQL Connector/Python deals with the transaction.
Performing Transactions with MySQL Connector/Python #
By default, Connector/Python turns the autocommit mode off. This is the reason, why we had to use the commit()
method of the connection object to commit the changes. You can also use the rollback()
method of the connection object to rollback the transaction.
To enable the autocommit mode set autocommit
argument of the connection object to True
.
db = mysql.connector.connect(option_files='my.conf', autocommit=True)
The following listing demonstrates the behavior of Connector/Python when autocommit=True
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | import mysql.connector
from mysql.connector import errors
try:
db = mysql.connector.connect(option_files='my.conf', autocommit=True)
cursor = db.cursor()
sql1 = """
create table employees(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
salary INT NOT NULL
)
"""
cursor.execute(sql1)
sql2 = """
insert into employees(name, salary) value('John', 15000)
"""
cursor.execute(sql2)
sql2 = """
insert into employees(name, salary) value('Bob', 20000)
"""
cursor.execute(sql2)
# db.commit() # we don't need to call commit() because autocommit=True
print('Table created successfully.')
except errors.Error as e:
print(e)
finally:
cursor.close()
db.close()
|
Expected Output:
Table created successfully.
This script creates an employees
table and inserts two rows into it. Since we have set autocommit=True
, we don't need to call the commit()
method manually to commit the changes.
If you look at the employees
table, it should look like this:
1 2 3 4 5 6 7 8 | mysql> select * from employees;
+----+------+--------+
| id | name | salary |
+----+------+--------+
| 1 | John | 15000 |
| 2 | Bob | 20000 |
+----+------+--------+
2 rows in set (0.00 sec)
|
Now let's see what would happen if we try to execute the same script with autocommit=False
.
First off, drop the employees
table using the DROP
statement:
1 2 | mysql> drop table employees;
Query OK, 0 rows affected (0.54 sec)
|
Next, set the autocommit=False
and run the script again.
This time you will get the same output as before. However, if you look at the world
database you will find that the employees
table is still gets created but without any rows.
This happens because DDL statements such as CREATE
, DROP
, TRUNCATE
etc. always perform an implicit commit. This behavior is not just specific to MySQL, in fact, Oracle does the same thing. For this reason, you should not try to include such statements in a transaction.
start_transaction() method #
If autocommit is enabled (i.e autocommit=True
) and you want to execute a set of statements as a single unit, then you must explicitly start the transaction using the start_transaction()
method of the connection object.
Once you have executed all the statements in a transaction you can either commit them by calling the commit()
method or undo them by calling the rollback()
method.
Once you have executed all the statements in a transaction you can commit them using the commit()
method. To undo the changes call the rollback()
method instead of commit()
.
If a transaction is already in progress then invoking start_transaction()
raises ProgrammingError
exception. You can check for an ongoing transaction using the in_transaction
property. The in_transaction
returns True
, if a transaction is active, otherwise False
.
The following listing demonstrates start_transaction()
method in action.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | import mysql.connector
from mysql.connector import errors
try:
db = mysql.connector.connect(option_files='my.conf', autocommit=True)
cursor = db.cursor()
db.start_transaction()
# these two INSERT statements are executed as a single unit
sql1 = """
insert into employees(name, salary) value('Tom', 19000)
"""
sql2 = """
insert into employees(name, salary) value('Leo', 21000)
"""
cursor.execute(sql1)
cursor.execute(sql2)
db.commit() # commit changes
print('Transaction committed.')
except errors.Error as e:
db.rollback() # rollback changes
print("Rolling back ...")
print(e)
finally:
cursor.close()
db.close()
|
Expected Output:
Transaction committed.
This script tries to execute two INSERT
statement in a transaction, which means that either both of the statements execute successfully or none at all.
The start_transaction()
method can also be used when autocommit is disabled to change the default behavior of the transaction. To learn more about options available visit the documentation.
Load Comments