后台代码中使用事务
首先来谈什么是事务
在数据库中简单的解释就是,“单个或者多个sql操作组成的一个逻辑单元”。
事务的四大特性
原子性 (Atomic)
每一个事务都是一个不可分割的工作单位。这些操作要么全部执行,要么全都不执行。在某个操作失败后,回滚到事务执行之前的状态。
回滚:大多数DB的事务造作只是在数据快照中进行,不修改实际数据,如果出错数据进行自然回滚。 支持简单事务的系统(例:NO-SQL)中,不会在快照上更新,而直接操作实际数据。先预演一边所有要执行的操作,如果失败则这些操作不会被执行。
一致性(Consistency)
事务对数据完整性约束的遵循。这些约束可能包括主键约束、外键约束或是一些用户自定义约束。事务执行的前后都是合法的数据状态,不会违背任何的数据完整性。
持久性(Durability)
事务一旦被提交,那么数据一定会被写入到数据库中并持久储存起来。
隔离性(Isolation)
一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
可能会出现的问题
- 问题:
- 脏读:“后一个事务”读到了“前一个事务”未提交的更新结果,如果“前一个事务”提交失败,“后一个事务”读到的就是脏数据。
- 不可重复读:“后一个事务” 在 “前一个事务”提交前读到的结果和提交后读到的结果可能不同而产生不可重复读。通过MVCC(Multi-Version Concurrency Control 多版本并发控制)可以在无锁的情况下,避免不可重复读。
- 幻读:“前一个事务”新增了一条记录,”后一个事务“在“前一个事务”提交前后各执行了一次查询操作,发现后一次比前一次多了一条记录。事务串行化(多个事务的重要操作按时间排序的一个序列),才能避免幻读
- 事务的隔离级别:
- Read Uncommitted:最低的隔离级别,一个事务可以读到另一个事务未提交的结果,所有的并发事务问题都会发生。
- Read Committed:只有在事务提交后,其更新结果才会被其他事务看见。可以解决脏读问题
- Repeated Read:在一个事务中,对于同一份数据,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交 读取结果总是相同的。可以解决脏读、不可重复读
- Serialization:事务串行化执行,隔离级别最高,牺牲了系统的并发性。可以解决并发事务的所有问题。
后端代码中使用事务
使用mysql连接数据为例:
using (MySqlConnection conn = new MySqlConnection(”数据库链接字符串“))
{
if (conn.State == ConnectionState.Closed)
{
conn.Open();
}
using (MySqlTransaction trans = conn.BeginTransaction())
{
try
{
if(ExecuteSql(trans, sqlstr)==1)
{
trans.Commit();
}
else
{
trans.Rollback()
}
}
catch (Exception)
{
trans.Rollback();
throw;
}
}
}
//trans:事务 sqlstr:操作数据库的字符串
public int ExecuteSql(MysqlTransaction trans,string sqlstr)
{
//此处可以进行封装,每次去调用
{
if (trans != null) cmd.Transaction = trans;
cmd.Connection = trans.Connection;
cmd.CommandText = sqlstr;
cmd.BindByName = true;
cmd.CommandType = CommandType.Text;//cmdType;
if (cmdParms != null)
{
//清除很重要
cmd.Parameters.Clear();
foreach (MysqlParameter parm in cmdParms)
{
if (parm.Value == null)
{
parm.Value = DBNull.Value;
}
else if (parm.Value.GetType() == typeof(string))
{
//parm.Size = -1;
parm.Value = StringHelper.Big5ToGB(parm.Value.ToString().Trim());
}
else if (parm.Value.GetType() == typeof(char[]))
{
string charstr = new string((char[])parm.Value);
parm.Value = StringHelper.Big5ToGB(charstr.Trim()).ToCharArray();
}
}
cmd.Parameters.AddRange(cmdParms.ToArray());
}
}
//执行sql操作,并返回结果
int rows = cmd.ExecuteNonQuery();
//清除很重要
cmd.Parameters.Clear();
return rows;
}
这就是一个事务的基本逻辑
所需要的注意的点就是 cmd.Parameters.Clear(); 解除它对其它对象的引用