AdoConnection+ADOQuery如何实现同时向数据表添加记录

forDream_ 2010-09-05 12:33:11
数据库Access

因为程序是多线程的、所以向数据库追加新的数据时、可能会出现两条(甚至更多)记录同时向表中添加、这样就会造成其中只有一条记录能够添加成功、那么请问各位、有什么办法可以让他可以同时向其中添加记录?


procedure DbOperateSinger(Singer:SingerInfo);
var
Conn:TADOConnection;
Rs:TADOQuery;
Pic:TMemoryStream;
begin
try
Coinitialize(nil);
Conn:=TADOConnection.Create(nil);
Rs:=TADOQuery.Create(nil);
Pic:=TMemoryStream.Create;

Conn.ConnectionString:=Connstr;
Conn.LoginPrompt:=False;
Conn.Open();
Rs.Connection:=Conn;
rs.SQL.Add('select * from Singer');
Rs.LockType:=ltOptimistic;
Rs.CursorType:=ctDynamic;
Rs.Open;
Rs.Insert;
Rs.FieldByName('名字').AsString:=Singer.sName;
//中间添加其他数据、略
Rs.FieldByName('Blog').AsString:=Singer.Blog;

//获得图像
Pic.Position:=0;//照片字段类型、我设置为OLE对象
TBlobField(Rs.FieldByName('照片')).LoadFromStream(Pic);

Rs.Post;
Rs.Close;
Conn.Close;
finally
Conn.Free;
Rs.Free;
Pic.Free;
CoUninitialize;
end;
end;


这个添加的函数、我写在一个单独的单元里、其他线程里需要用的时候就调用他、
...全文
251 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
无条件为你 2010-09-08
  • 打赏
  • 举报
回复
用临界区即可解决,线程中是基本的用法。
Blessed_Chuan 2010-09-07
  • 打赏
  • 举报
回复
调用的地方呢 也发出来看看
goodhj 2010-09-07
  • 打赏
  • 举报
回复
考虑是不是因为重入引起的?你每个线程里要是单独都有这个DbOperateSinger可能就可以了
lyhoo163 2010-09-06
  • 打赏
  • 举报
回复
关注一下。
wintergoes 2010-09-05
  • 打赏
  • 举报
回复
先用select 判断一下是否存在,如果存在就不添加了
另外判断并添加的过程放到一个临界区里,防止同时执行这些操作
关于临界区


定义:
InsertLock: TRTLCriticalSection;

初始化:
InitializeCriticalSection(InsertLock);

不需要使用(不会再调用那个线程)的时候删除,
DeleteCriticalSection(InsertLock);


使用:
EnterCriticalSection(InsertLock);
try
// 执行判断或者插入的代码
finally
LeaveCriticalSection(InsertLock);
end;


天牌 2010-09-05
  • 打赏
  • 举报
回复
整个函数都是交给线程运行的?
为什么把连接也放进去了呢,有必要每录入一次都要重新连接吗
forDream_ 2010-09-05
  • 打赏
  • 举报
回复
还有我刚才仔细单步看了一下、成功录入的记录并不是第一条、而是最后一条、
大致的结果是第二个打开数据库的连接后、第一个就失败了、第三个打开、第二个就失败了、以此类推、最后就只有最后一条记录录入是成功的
forDream_ 2010-09-05
  • 打赏
  • 举报
回复
很奇怪、我设置了一个全局变量DbOpening、并将操作数据库的函数DbOperateSinger做这样的修改

procedure DbOperateSinger(Singer:SingerInfo);
var
Conn:TADOConnection;
Rs:TADOQuery;
Pic:TMemoryStream;
begin
try
Randomize;
while DbOpening do
Sleep(1000 + Random(2000));{直到其他线程调用的结束、退出循环}

DbOpening:=True;
Coinitialize(nil);
Conn:=TADOConnection.Create(nil);
Rs:=TADOQuery.Create(nil);
Pic:=TMemoryStream.Create;

Conn.ConnectionString:=Connstr;
Conn.LoginPrompt:=False;
Conn.Open();
Rs.Connection:=Conn;
rs.SQL.Add('select * from Singer');
Rs.LockType:=ltOptimistic;
Rs.CursorType:=ctDynamic;
Rs.Open;
Rs.Insert;
Rs.FieldByName('名字').AsString:=Singer.sName;
//中间添加其他数据、略
Rs.FieldByName('Blog').AsString:=Singer.Blog;

//获得图像
Pic.Position:=0;//照片字段类型、我设置为OLE对象
TBlobField(Rs.FieldByName('照片')).LoadFromStream(Pic);

Rs.Post;
Rs.Close;
Conn.Close;
finally
Conn.Free;
Rs.Free;
Pic.Free;
CoUninitialize;
DbOpening:=False;
end;
end;
{可是这样竟然还是只有第一条记录录入成功、那么是否就说明我这个函数有问题呢?}


dinoalex 2010-09-05
  • 打赏
  • 举报
回复
加全局变量,类似于主键,即在线程里面先判断要插入的数据是否跟全局的一样(要全部一样)

如果一样就释放,不一样就插,插完后更新全局变量.

当然前提是这两个线程用的是同一个Mutex或其它
forDream_ 2010-09-05
  • 打赏
  • 举报
回复
To 4F:
比较尴尬的是....Access没有存储过程...只有一个“查询”类似于存储过程而已
dinoalex 2010-09-05
  • 打赏
  • 举报
回复
设置相应的主键,调用存储过程来插入,如果重复或出错,存储过程里会自动ROLLBACK TRANSACTION
forDream_ 2010-09-05
  • 打赏
  • 举报
回复
我记得数据库有个游标锁定的类型、可以设置同时读写数据的、不过在ADO组件里、我只找到了TADOQuery的CursorType、LockType属性、不过设置了好像没用、不知道是否是我方法错误?
forDream_ 2010-09-05
  • 打赏
  • 举报
回复
先谢谢你的关注、


unit Unit2;

interface

uses
Classes, SysUtils, Unit1,
ComCtrls,Windows;

type
TrdGetSinger = class(TThread)
private
{ Private declarations }
public
SingerID:string;
protected
procedure Execute; override;
end;
var
DbAdd:TRTLCriticalSection;
implementation

{ TrdGetSinger }
uses Unit3,Unit4,UnitFunctions;
procedure TrdGetSinger.Execute;
begin
try

{这个线程的FreeOnTerminate=True}
InitializeCriticalSection(DbAdd);

{中间处理数据、略}

EnterCriticalSection(DbAdd);
try
DbOperateSinger(NewThreadGetInfo.sInfo);{这个函数是添加记录的、就是我前面贴的函数}
finally
LeaveCriticalSection(DbAdd);
end;

finally
DeleteCriticalSection(DbAdd);
end;
end;

end.

我这样写了以后、我发现同样只有一条记录能写入、都是执行到添加记录函数的.Insert(.Append)方法就直接跳到finally部分了、是不是是我使用的不正确?

2,498

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 数据库相关
社区管理员
  • 数据库相关社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧