前言:把日常工作以及學習過程中遇到的問題,常記筆記總結我覺得是非常有必要的,不僅可以方便以後在類似過程中遇到同樣問題方便查找和解決,也便於其他需要幫助的人們可少找彎路!
一.簡介:
1.最近在Linux下使用MySQL數據庫時,爲了支持事務操作需要用到InnoDB引擎,對於表中處理的插入,更新等操作失敗時,回滾前面不應該完成的操作是必須的.
2.一般MySQL默認的數據庫引擎是MyISAM,不支持事務和外鍵,則可使用支持事務和外鍵的InnoDB引擎.關於如何在Linux下設置MySQL默認引擎InnoDB請參照我前面寫的一篇文章小結
http://blog.csdn.net/sunrier/article/details/7475403
3.本筆記着重講解MySQL的autocommit變量,如何在數據庫中設置自動提交,禁止自動提交,如何在對錶操作失敗後回滾,對錶操作成功後提交事務!
二.操作方法
MySQL的autocommit默認是打開的(ON爲打開,OFF爲關閉或者表示成1爲打開,0爲關閉)
打開或關閉即是打開自動提交或者關閉自動提交
1.MySQL命令:
方法1:
1.11)如果是支持事務的引擎,如InnoDB則有系統參數設置是否自動commit,查看參數如下:
mysql> show variables like '%autocommit%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.01 sec)
mysql>
顯示結果爲ON,表示事務自動提交,即不用手工去commit。當然,你可以設置其爲OFF,然後自己手工去commit。
1.12)打開和關閉自動提交功能命令:
關閉自動提交功能命令
mysql> set session autocommit=OFF;
mysql>
或者
mysql> set session autocommit=off;
Query OK, 0 rows affected (0.00 sec)
mysql>
(注:off不區分大小寫)
設置後再次查看:
mysql> show variables like '%autocommit%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | OFF |
+---------------+-------+
1 row in set (0.01 sec)
mysql>
可猜測mysql> set session autocommit=on;應該爲打開自動提交功能,不過猜是猜的,最好還是實踐下這個命令打開自動提交功能命令
mysql> set session autocommit=ON;
mysql>
設置後再次查看:
mysql> show variables like '%autocommit%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.01 sec)
mysql>
注:如果退出MySQL,下次登陸MySQL後autocommit還會自動設置爲默認的變量,這裏即autocommit爲ON,事務自動提交
方法2:
1.21)如果你在剛纔上面方法1.11)中查看參數出現下面的情況:
mysql> show variables like '%autocommit%';
Empty set (0.00 sec)
mysql>
這個情況在我裝的虛擬機上就無法查看,然後在公司的服務器上安裝的MySQL就沒問題(注1.1X爲公司的MySQL)。
現在關於爲什麼會出現兩種不同的結果,筆者還沒解決這個問題,猜測可能是因爲數據庫版本的不同可能查看的命令不一樣,我最終在網上查找到例外的方法在命令下查看和設置autocommit變量
1.22)查看和設置autocommit變量
查看autocommit變量
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
1 row in set (0.00 sec)
mysql>
注:autocommit爲1表示打開自動提交
設置autocommit變量
mysql> set session autocommit=off;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 0 |
+--------------+
1 row in set (0.00 sec)
mysql>
注:autocommit爲0表示關閉自動提交
2.MySQL的配置文件my.cnf:
修改autocommit變量也可以通過修改配置文件my.cnf來關閉autocommit
[mysqld]
init_connect='SET autocommit=0' //在my.cnf文件[mysqld]下面加上這句
3.MySQL數據庫程序中使用C的API函數:
切換autocommit模式,ON/OFF:
my_bool mysql_autocommit(MYSQL *mysql, my_bool mode)
如果模式爲"1",啓用autocommit模式;如果模式爲"0",禁止autocommit模式.
返回值:如果成功返回0;如果出現錯誤返回非0值.
回滾當前事務:
my_bool mysql_rollback(MYSQL *mysql)
該函數的動作取決於completion_type系統變量的值.尤其是如果completion_type的值爲"2",終結事務後,服務器將執行釋放操作,並關閉客戶端連接.客戶端程序應調用mysql_close(),從客戶端一側關閉連接.
返回值:如果成功返回0;如果出現錯誤返回非0值.
提交當前事務:
my_bool mysql_commit(MYSQL *mysql)
該函數的動作受completion_type系統變量的值控制.尤其是如果completion_type的值爲2,終結事務並關閉客戶端連接後,服務器將執行釋放操作.客戶端程序應調用mysql_close(),從客戶端一側關閉連接.
返回值:如果成功返回0;如果出現錯誤返回非0值.
三.實例說明:
1.MySQL下命令實例(注:student爲我的MySQL數據庫test中先前已經創建的一個student表信息):
mysql> select * from student;
+--------+---------+------+
| studno | fname | age |
+--------+---------+------+
| 1 | Jack | 67 |
| 2 | Tim | 22 |
| 3 | Sunrier | 24 |
| 4 | Helen | 26 |
| 5 | Tony | 29 |
| 6 | Cherry | 30 |
+--------+---------+------+
6 rows in set (0.00 sec)
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
1 row in set (0.00 sec)
mysql>
從上面可以看出此時數據庫autocommit爲1也就是自動提交
下面我想插入一個學生信息學號,名字,年齡分別是7,Nicky,22
mysql> insert into student values (7,"Nicky",22);
Query OK, 1 row affected (0.00 sec)
mysql> select * from student;
+--------+---------+------+
| studno | fname | age |
+--------+---------+------+
| 1 | Jack | 67 |
| 2 | Tim | 22 |
| 3 | Sunrier | 24 |
| 4 | Helen | 26 |
| 5 | Tony | 29 |
| 6 | Cherry | 30 |
| 7 | Nicky | 22 |
+--------+---------+------+
7 rows in set (0.00 sec)
mysql>
從MySQL插入命令後,執行查詢結果可以看出,剛纔插入的一條學生記錄(7,"Nicky",22)已經插入到表裏面,也許你爲了保證執行結果確實是這樣,你可能先退出MySQL,再次進入MySQL再查詢下,出於這樣的本能,我也是這樣做的
mysql> exit
Bye
[root@localhost ~]# mysql -uroot -predhat test
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 11 to server version: 5.0.22
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> select * from student;
+--------+---------+------+
| studno | fname | age |
+--------+---------+------+
| 1 | Jack | 67 |
| 2 | Tim | 22 |
| 3 | Sunrier | 24 |
| 4 | Helen | 26 |
| 5 | Tony | 29 |
| 6 | Cherry | 30 |
| 7 | Nicky | 22 |
+--------+---------+------+
7 rows in set (0.00 sec)
mysql>
這樣可以確信剛纔那條學生記錄已經確實插入到數據庫test的student表裏了.
開始主要的環節:
mysql> set session autocommit=off;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 0 |
+--------------+
1 row in set (0.00 sec)
mysql>
從上面可以看出此時數據庫變量autocommit已經被設置爲0也就是禁止自動提交
下面我想插入一個學生信息學號,名字,年齡分別是8,Jerry,21
mysql> insert into student values (8,"Jerry",21);
Query OK, 1 row affected (0.00 sec)
mysql> select * from student;
+--------+---------+------+
| studno | fname | age |
+--------+---------+------+
| 1 | Jack | 67 |
| 2 | Tim | 22 |
| 3 | Sunrier | 24 |
| 4 | Helen | 26 |
| 5 | Tony | 29 |
| 6 | Cherry | 30 |
| 7 | Nicky | 22 |
| 8 | Jerry | 21 |
+--------+---------+------+
8 rows in set (0.00 sec)
mysql>
從MySQL插入命令後,執行查詢結果可以看出,剛纔插入的那條學生記錄(8,"Jerry",21)也插入到表裏面了。
也許你還是爲了保證執行結果之外,你可能先退出MySQL,再次進入MySQL再查詢下
[root@localhost ~]# mysql -uroot -predhat test
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 12 to server version: 5.0.22
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> select * from student;
+--------+---------+------+
| studno | fname | age |
+--------+---------+------+
| 1 | Jack | 67 |
| 2 | Tim | 22 |
| 3 | Sunrier | 24 |
| 4 | Helen | 26 |
| 5 | Tony | 29 |
| 6 | Cherry | 30 |
| 7 | Nicky | 22 |
+--------+---------+------+
7 rows in set (0.00 sec)
mysql>
可以看出剛纔那條學生記錄(8,"Jerry",21)沒有真正插入數據庫test的student表裏,這應該就是沒有自動提交的效果,
說明了剛纔設置變量autocommit爲0時後來產生了作用。那麼現在你也許會想,我現在設置了變量autocommit爲0,不自動提交執行結果了,那我應該怎麼做才能使我執行的SQL語句還一樣對數據庫產生影響呢???基於這樣的思考和探索下,對於我來說,我剛開始也一無所知,好在現在網絡資源強大,我可以利用百度,google等可以利用的資源, 在網上找到了不少相關的知識介紹
發現了下面兩個SQL命令:
mysql> rollback;
mysql> commit;
從字面意思上我們可以猜出了它們的大概意思rollback即回滾,commit即提交.現在回到剛纔沒有插入成功的那條記錄那,爲了我能夠在數據庫中變量autocommit設置爲0的情況下,執行SQL命令也能對數據庫產生影響,那就需要在執行DML的SQL命令後,執行一個commit提交命令
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
1 row in set (0.00 sec)
mysql> set session autocommit=off;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 0 |
+--------------+
1 row in set (0.00 sec)
mysql> select * from student;
+--------+---------+------+
| studno | fname | age |
+--------+---------+------+
| 1 | Jack | 67 |
| 2 | Tim | 22 |
| 3 | Sunrier | 24 |
| 4 | Helen | 26 |
| 5 | Tony | 29 |
| 6 | Cherry | 30 |
| 7 | Nicky | 22 |
+--------+---------+------+
7 rows in set (0.00 sec)
mysql>
上面爲重新設置和查看,可以發現autocommit設置爲0
mysql> insert into student values (8,"Jerry",21);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from student;
+--------+---------+------+
| studno | fname | age |
+--------+---------+------+
| 1 | Jack | 67 |
| 2 | Tim | 22 |
| 3 | Sunrier | 24 |
| 4 | Helen | 26 |
| 5 | Tony | 29 |
| 6 | Cherry | 30 |
| 7 | Nicky | 22 |
| 8 | Jerry | 21 |
+--------+---------+------+
8 rows in set (0.00 sec)
mysql> exit
Bye
[root@localhost ~]# mysql -uroot -predhat test
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 13 to server version: 5.0.22
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> select * from student;
+--------+---------+------+
| studno | fname | age |
+--------+---------+------+
| 1 | Jack | 67 |
| 2 | Tim | 22 |
| 3 | Sunrier | 24 |
| 4 | Helen | 26 |
| 5 | Tony | 29 |
| 6 | Cherry | 30 |
| 7 | Nicky | 22 |
| 8 | Jerry | 21 |
+--------+---------+------+
8 rows in set (0.00 sec)
mysql>
這樣可以確信剛纔那條學生記錄(8,"Jerry",21)已經確實插入到數據庫test的student表裏了.
總結:在數據庫中autocommit設置爲0的情況下,所有的SQL執行命令的結果在遇到commit命令之後才真正提交到數據庫中。也許到這會你發現我剛纔說了兩個SQL命令,到現在只說了commit命令,還有個rollback命令怎麼用呢?
繼續rollback命令
現在我想把數據庫test的student表裏名字爲"Sunrier"這個小朋友的信息刪除了。
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
1 row in set (0.00 sec)
mysql> set session autocommit=off;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 0 |
+--------------+
1 row in set (0.00 sec)
mysql> select * from student;
+--------+---------+------+
| studno | fname | age |
+--------+---------+------+
| 1 | Jack | 67 |
| 2 | Tim | 22 |
| 3 | Sunrier | 24 |
| 4 | Helen | 26 |
| 5 | Tony | 29 |
| 6 | Cherry | 30 |
| 7 | Nicky | 22 |
| 8 | Jerry | 21 |
+--------+---------+------+
8 rows in set (0.00 sec)
mysql> delete from student where fname = "Sunrier";
Query OK, 1 row affected (0.01 sec)
mysql> select * from student;
+--------+--------+------+
| studno | fname | age |
+--------+--------+------+
| 1 | Jack | 67 |
| 2 | Tim | 22 |
| 4 | Helen | 26 |
| 5 | Tony | 29 |
| 6 | Cherry | 30 |
| 7 | Nicky | 22 |
| 8 | Jerry | 21 |
+--------+--------+------+
7 rows in set (0.00 sec)
mysql>
從表裏面可以看到名字爲"Sunrier"的信息刪除了,也許你可能馬上後悔自己的選擇,我找不到任何理由刪除"Sunrier",這個小朋友,馬上我着急了,我該如何找到該小朋友的原始信息呢,於是你眼前一亮,rollback這個命令,回滾,按照日常的用語是不是回過去的意思.於是你查詢了下相關資料,好像可以哎,於是決定自己試試這個SQL命令
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from student;
+--------+---------+------+
| studno | fname | age |
+--------+---------+------+
| 1 | Jack | 67 |
| 2 | Tim | 22 |
| 3 | Sunrier | 23 |
| 4 | Helen | 26 |
| 5 | Tony | 29 |
| 6 | Cherry | 30 |
| 7 | Nicky | 22 |
| 8 | Jerry | 21 |
+--------+---------+------+
8 rows in set (0.00 sec)
mysql>
爲了再次確認,退出MySQL後重新查看
mysql> exit
Bye
[root@localhost ~]# mysql -uroot -predhat test
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 13 to server version: 5.0.22
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> select * from student;
+--------+---------+------+
| studno | fname | age |
+--------+---------+------+
| 1 | Jack | 67 |
| 2 | Tim | 22 |
| 3 | Sunrier | 23 |
| 4 | Helen | 26 |
| 5 | Tony | 29 |
| 6 | Cherry | 30 |
| 7 | Nicky | 22 |
| 8 | Jerry | 21 |
+--------+---------+------+
8 rows in set (0.00 sec)
mysql>
從結果發現,名字爲"Sunrier"小朋友的信息又回來了,真是太高興了.
2.MySQL下使用C的API實例(注:student爲我的MySQL數據庫test中先前已經創建的一個student表信息):
//dbproc.c
/*************************************************************
FileName : dbproc.c
FileFunc : MySQL數據庫事務操作
Version : V0.1
Author : Sunrier
Date : 2012-06-19
Descp : Linux下使用C語言訪問MySQL函數
*************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mysql.h>
static MYSQL s_my_connection;
static int s_iDbconnected = 0;//數據庫連接標誌 連接時爲1,斷開時爲0
int mysql_login(char* pServer,char *pUser,char *pPassword,char *pDataBase)
{
MYSQL *conn_ptr = NULL;
int iRetCode = -1;
unsigned int uiTimeOut = 7;
if( s_iDbconnected )
return 0;
conn_ptr = mysql_init(&s_my_connection);
if( !conn_ptr )
{
fprintf(stderr,"mysql_init failed ! \n");
return EXIT_FAILURE;
}
iRetCode = mysql_options(&s_my_connection,MYSQL_OPT_CONNECT_TIMEOUT,(const char *)&uiTimeOut);
if( iRetCode )
{
fprintf(stderr,"MySQL Connection is timeout! \n");
return EXIT_FAILURE;
}
conn_ptr = NULL;
conn_ptr = mysql_real_connect(&s_my_connection,pServer,pUser,pPassword,pDataBase,0,NULL,0);
if( conn_ptr )
{
printf("MySQL Connection success!\n");
s_iDbconnected = 1;
}
else
{
fprintf(stderr,"MySQL Connection failed!\n");
if( mysql_errno(&s_my_connection) )
{
fprintf(stderr,"Connection error %d: %s!\n",mysql_errno(&s_my_connection),mysql_error(&s_my_connection));
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
void mysql_logout( void )
{
if( s_iDbconnected )
mysql_close(&s_my_connection);//關閉連接
s_iDbconnected = 0 ;
}
//ucMode = 1啓動自動模式,一般爲默認模式
//ucMode = 0禁止autocommit模式
int mysql_commitmode(unsigned char ucMode)
{
int iRetCode = -1;
iRetCode = mysql_autocommit(&s_my_connection,ucMode);
if( iRetCode )
{
fprintf(stderr,"Set autocommit %d-mode error ,sqlcode=[%d] : %s ",ucMode,mysql_errno(&s_my_connection),mysql_error(&s_my_connection));
return EXIT_FAILURE;
}
else
{
return EXIT_SUCCESS;
}
}
int mysql_proc( void )
{
MYSQL *conn_ptr = NULL;
MYSQL_RES *res_ptr = NULL;
MYSQL_ROW sqlrow;
int iTableRow = 0;
int iRetCode = 1;
char szSql[256];
memset(szSql,0,sizeof(szSql));
sprintf(szSql,"insert into student values(9,'Marry',23)");
iRetCode = mysql_query(&s_my_connection,szSql);
if( iRetCode )
{
fprintf(stderr,"insert error ,sqlcode=[%d] : %s ",mysql_errno(&s_my_connection),mysql_error(&s_my_connection));
mysql_rollback(&s_my_connection);//回滾操作
return EXIT_FAILURE;
}
mysql_commit(&s_my_connection);//提交操作(即手動提交)
return EXIT_SUCCESS;
}
//demo.c
/*************************************************************
FileName : demo.c
FileFunc : MySQL數據庫事務的測試
Version : V0.1
Author : Sunrier
Date : 2012-06-19
Descp : Linux下使用C語言訪問MySQL函數
*************************************************************/
#include <stdio.h>
int main(int argc,char *argv[])
{
int iRetCode = -1;
char szServer[20] = "localhost";
char szUser[20] = "Sunrier";
char szPassword[20] = "redhat"
char szDatabase[20] = "test";
iRetCode = mysql_login(szServer,szUser,szPassword,szDatabase);
if( iRetCode )
{
system("service mysqld start");
iRetCode = mysql_login(szServer,szUser,szPassword,szDatabase);
}
mysql_commitmode(0);//禁止autocommit模式
iRetCode = mysql_proc();
if( iRetCode )
{
printf("MySQL proc error !\n");
}
mysql_logout();
return 0;
}
//makefile
#makefile
all:demo
demo:demo.c dbproc.c
@gcc -I/usr/include/mysql $? -L/usr/lib/mysql -lmysqlclient -lz -lm -o demo
clean:
@ls | grep -v ^makefile$$ | grep -v [.]c$$ | grep -v [.]h$$ | grep -v [.]sql$$ | xargs rm -rf
#makefile
[Sunrier@localhost MySql]$ ls
dbproc.c demo.c makefile
[Sunrier@localhost MySql]$ make
[Sunrier@localhost MySql]$ ls
dbproc.c demo demo.c makefile
[Sunrier@localhost MySql]$ ./demo
MySQL Connection success!
[Sunrier@localhost MySql]$
執行前後查看數據庫:
由於程序中設置了禁止autocommit模式,如想插入記錄成功則需要提交函數,撤消操作則要回滾函數.
執行前數據庫:
mysql> select * from student;
+--------+---------+------+
| studno | fname | age |
+--------+---------+------+
| 1 | Jack | 67 |
| 2 | Tim | 22 |
| 3 | Sunrier | 23 |
| 4 | Helen | 26 |
| 5 | Tony | 29 |
| 6 | Cherry | 30 |
| 7 | Nicky | 22 |
| 8 | Jerry | 21 |
+--------+---------+------+
8 rows in set (0.00 sec)
執行後數據庫:
mysql> select * from student;
+--------+---------+------+
| studno | fname | age |
+--------+---------+------+
| 1 | Jack | 67 |
| 2 | Tim | 22 |
| 3 | Sunrier | 23 |
| 4 | Helen | 26 |
| 5 | Tony | 29 |
| 6 | Cherry | 30 |
| 7 | Nicky | 22 |
| 8 | Jerry | 21 |
| 9 | Marry | 23 |
+--------+---------+------+
9 rows in set (0.00 sec)
mysql>
//student.sql
drop database if exists test;
create database test;
use test;
create table student
(
studno integer auto_increment not null primary key,
fname varchar(30),
age integer
);
-- )engine=innodb charset=gb2312;
--insert into student values ('',"Sunrier",22);
--insert into student values ('',"Tom",23);
--insert into student values ('',"Jerry",21);