事务一般用来管理 insert, update, delete语句。它是数据库并发控制的基本单位,是一个操作序列,要么都执行,要么都不执行。例如银行转账:账户A出账并是账户B入账,要么都成功,要么都失败。
一、事务的基本特性:原子性、一致性、隔离性、持久性。
原子性: 事务包含的所有操作要么全部成功,要么全部失败回滚。因为事务的操作如果成功必须全部应用到数据库,如果失败则不能对数据库有任何影响。
一致性:事务执行前和执行后都必须保持一致性,拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转账几次,事务结束之后两者的钱加起来还是5000,这就是事务的一致性。
隔离性:当多个用户并发访问数据库时,比如操作同一张表,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
持久性 :一个事务一旦被提交了,那么多数据库的改变是永久性的,即使数据库系统出现故障也不会丢失已经提交的操作。
注意:在 MySQL 命令行的默认设置下,事务都是自动提交的,即执行 SQL 语句后就会马上执行 COMMIT 操作,也就是命令行下每输入一行指令执行后结果不可回滚,除非显式的用代码开启了事务机制。
注意:在windows系统中SQL语句不区分大小写
(1)事务的开启
BEGIN;
START TRANSACTION;
(2)事务的提交
COMMIT;
(3)建立事务存档点
SAVEPOINT nameofsavepoint(自定义);
(4)删除事务存档点
RELEASE SAVEPOINT nameifsavepoint(无此存档点会报错);
(5)事务回滚
ROLLBACK; //回滚到begin的位置。
ROLLBACK TO nameofsavepoint; //回滚到指定存档点。
(6)设置事务的隔离级别
SET TRANSACTION;
C++操作Mysql的事务机制实例:
创建数据库名:lcs,表名:test
#include "stdafx.h"
#include <windows.h>//注意头文件顺序
#include "stdio.h"
#include "winsock.h"
#include "mysql.h"
MYSQL mysql, *sock; //声明MySQL的句柄
int main(void)
{
const char* host = "127.0.0.1"; //因为是作为本机测试,所以填写的是本地IP
const char* user = "root"; //这里改为你的用户名,即连接MySQL的用户名
const char* passwd = "123456"; //这里改为你的用户密码
const char* db = "lcs"; //这里改为你要连接的数据库的名字
unsigned int port = 3306; //这是MySQL的服务器的端口,如果你没有修改过的话就是3306。
const char* unix_socket = NULL; //unix_socket这是unix下的,我在Windows下,所以就把它设置为NULL
unsigned long client_flag = 0; //这个参数一般为0
MYSQL_RES* result; //保存结果集
MYSQL_ROW row; //代表的是结果集中的一行
mysql_init(&mysql); //连接之前必须使用这个函数来初始化
if ((sock = mysql_real_connect(&mysql, host, user, passwd, db, port, unix_socket, client_flag)) == NULL) //连接MySQL
{
fprintf(stderr, "连接失败,错误信息:%s\n", mysql_error(&mysql));
exit(1);
}
else
{
fprintf(stderr, "连接MySQL成功!!\n");
}
const char* i_query_1 = "create database if not exists lcs";
if (mysql_query(&mysql, i_query_1) != 0) //不存在,则创建数据库
{
fprintf(stderr, "创建数据库lcs失败!\n");
exit(1);
}
const char* i_query_2 = "select count(*) from information_schema.tables where table_schema = 'lcs' and table_name ='test'";
if (0 != mysql_real_query(&mysql, i_query_2, (unsigned long)strlen(i_query_2)))
{
fprintf(stderr, "查询失败,错误信息:%s\n", mysql_error(&mysql));
exit(1);
}
MYSQL_RES* pRes = mysql_store_result(&mysql);
MYSQL_ROW row1 = mysql_fetch_row(pRes);
if (atoi(row1[0]) != 1)
{
fprintf(stderr, "表不存在,错误信息:%s\n", mysql_error(&mysql));
}
mysql_free_result(pRes);
//const char* i_query_3 = "drop table if exists test";
//if (mysql_query(&mysql, i_query_3) != 0) //删除表
//{
// fprintf(stderr, "删除表失败!\n");
// exit(1);
//}
const char* i_query_4 = "create table if not exists test(id int,value varchar(10),text varchar(50))";
if (mysql_query(&mysql, i_query_4) != 0) //不存在,则建表
{
fprintf(stderr, "建表失败!\n");
exit(1);
}
const char* i_query_5 = "insert into test(id,value,text)values(6,'6','this is 6')";
if (mysql_query(&mysql, i_query_5) != 0)
{
fprintf(stderr, "插入失败!\n");
exit(1);
}
const char* i_query_6 = "select * from test;";
if (mysql_query(&mysql, i_query_6) != 0) //查询
{
fprintf(stderr, "查询失败!\n");
exit(1);
}
else
{
if ((result = mysql_store_result(&mysql)) == NULL) //保存查询的结果
{
fprintf(stderr, "保存结果集失败!\n");
exit(1);
}
else
{
while ((row = mysql_fetch_row(result)) != NULL) //读取结果集中的数据,返回的是下一行。因为保存结果集时,当前的游标在第一行【之前】
{
printf("%s,%s,%s\n", row[0], row[1], row[2]); //打印当前行的第一列的数据
}
}
}
mysql_free_result(result); //释放结果集
char a;
if (mysql_query(&mysql, "begin ;") != 0) //开启事务
{
fprintf(stderr, "事务失败!\n");
exit(1);
}
else
{
const char* i_query_7 = "insert into test(id,value,text)values(5,'5','this is 5');";
if (mysql_query(&mysql, i_query_7) != 0)
{
fprintf(stderr, "插入失败!\n");
exit(1);
}
else
{
printf("输入指令0或1:\n");
a = getchar();
if (a == '0') //如果输入为0,则回滚,为1则提交。
{
if (mysql_query(&mysql, "rollback;") != 0) //事务回滚
{
fprintf(stderr, "回滚失败!\n");
exit(1);
}
if (mysql_query(&mysql, "commit;") != 0) //事务提交
{
fprintf(stderr, "提交失败!\n");
exit(1);
}
}
else
{
if (mysql_query(&mysql, "commit;") != 0) //事务提交
{
fprintf(stderr, "提交失败!\n");
exit(1);
}
}
}
}
mysql_close(sock);//关闭连接
system("pause");
exit(EXIT_SUCCESS);
}