问题一: 我在三层结构下做主从表,在服务器用的是巢状结构来关联主从结构,客户端用clientdataset的insert和applyupdates来提交,但主表数据可以正常提交写入数据库,但从表毫无动静,也就是说从表能写入clientdateset的缓存,但不能写入数据库,不管是用applyupdata(-1)还是用applyupdate(0)都是这样,请教各位老大这是什么原因?
问题二:有时候用clientdataset的edit方法和delete方法修改数据的时候程序会出错,提示说:不能执行该操作,该记录已被别人修改,(但实际上只有我一个人使用,并没有修改数据),不知这是什么原因?
我只是用clientdataset的insert,edit 再applyupdata的方法对一个数据表table1的abc纪录进行操作,然后由于某种需要,我用另一个clientadaset(专门执行sql语句的公用clientdataset)的commandtext执行一个更新此table1表中abc纪录的操作而已。
问题三:在问题二中,是不是用了clientdataset的insert,edit方法以后,就最好不要再用commandtext方法提交数据阿?我向别人请教,他说最好要么一直用clientdataset的insert,edit方法,要么一直用commandtext提交insert,update语句
问题四:用clientdataset的方法增删改数据时,有时候会提示:Cannot find record,No key specified. 我不知道这是什么原因引起的?有人说要加主键,但我的数据库表已经有主键,有人说是因为我的数据库表中有一个字段是设定自动增一的,又有的人说只需设置datasetprovider的某个属性就行了,莫衷一是,请各位先悟道者给个权威说法 :)
问题二:有时候用clientdataset的edit方法和delete方法修改数据的时候程序会出错,提示说:不能执行该操作,该记录已被别人修改,(但实际上只有我一个人使用,并没有修改数据),不知这是什么原因?
我只是用clientdataset的insert,edit 再applyupdata的方法对一个数据表table1的abc纪录进行操作,然后由于某种需要,我用另一个clientadaset(专门执行sql语句的公用clientdataset)的commandtext执行一个更新此table1表中abc纪录的操作而已。
问题三:在问题二中,是不是用了clientdataset的insert,edit方法以后,就最好不要再用commandtext方法提交数据阿?我向别人请教,他说最好要么一直用clientdataset的insert,edit方法,要么一直用commandtext提交insert,update语句
问题四:用clientdataset的方法增删改数据时,有时候会提示:Cannot find record,No key specified. 我不知道这是什么原因引起的?有人说要加主键,但我的数据库表已经有主键,有人说是因为我的数据库表中有一个字段是设定自动增一的,又有的人说只需设置datasetprovider的某个属性就行了,莫衷一是,请各位先悟道者给个权威说法 :)
解决方案 »
- 请教,webbrowser边框怎么去掉
- 急急急,RAVE导出繁体中文PDF后,数字和标点符号格式不对
- 真的没人能解答这个问题吗
- ado,sql,dbgrid构成表单的显示问题?
- 诱惑测试[你能受的了吗?]
- 强烈鄙视,D版的某些狗屁版主,不吃凉粉你就让凳子
- 用DELPHI的INDY老莫名其妙的出现奇怪错误!!!
- 在XP下用Delphi5调试MTS对象时发生的一个问题(在线 Up有分)
- 尚未解决的SQL问题
- 关于Image控件不能随着相片尺寸变化的问题
- set Obj=Server.CreateObject("ADODB.Connection")
- 并发操作控制:在C/S当中如何避免多个用户同时插入同一条记录?
if (DataSet = qryMaster) then
TableName := 'MasterTableName'
else if (DataSet = qryDetail) then
TableName := 'DetailTableName';
这样就可以了。
消除这个的方法是,给Query中每个Field的ProviderFlags设置属性。只有主键才有pfInWhere,其他的去除就可以了。
3)commandtext纯属多余,可以不用。用上面的方法可以不写一行代码,只要点点鼠标就做到了。
4)取消该Field的pfInUpdate就可以了。
首先,还应该知道Master/Detail到底是什么关系;
其次,要明白什么是巢状数据表是什么意思;
Master/Detail这种主细关系,只要做过数据库程序的人应该都知道,在C/S结构里随时都是,很长见的;
巢状数据表应该如何理解?它就是分布式系统和C/S结构的对于Master/Detail的不同操作机制而引申的;从客户端而言,在C/S结构中,Master/Detail可以用两个数据集组件分别取出表中具有Master/Detail关系的数据,但是在分布多中,对于客户端而言,我们只关心Master的数据集的处理方式,而如何对应Master/Detail则是在应用服务方面进行操作,当我们正确连接了客户端的MasterClinetDataSet(TClientDataSet)时,其所对应的Detail就会随着数据包自动的下载到客户端,这就是巢状数据表的初步解释;而它的正真定义应该是:当数据封包在封装数据时,这个数据封包甚至能够把一个数据集封装起来;
下边用一个例程来说明:
应用服务端:在应用服务器上要实现两个数据组件的M/D(Master/Detail,以后用M/D代替)的关系,可以用TTable or TQuery ,TADOQuery or TADOTable or TADODataSet组件都完全可以;这儿要建立它们的M/D关系,和C/S中的完全一样(中间需要通过一个TDataSource连接);客户端:在客户端只需要用一个TClientDataSet来接收数据,这儿更能体现“巢状“,所谓的接收数据是从应用服务端下载数据,是将M/D数据都下载下来,这后,Delphi可以自动的识别这是一个M/D结构的;此时需要将另一个TClientDataSet组件加进来,而且要设置它的DataSetField为下载数据的TClientDataSet中的Detail数据字段(可以这样理解);但不应该忘记了要执行TClientDataSet.Fetch Params,只有这样才可以实现Detail -> TClientDataSet的DataSetField的设置;设置完成后,就可以正确运行了;
下边我给出设置实例,供参考
应用服务端窗体文件如下:
object DMF_LX: TDMF_LX
OldCreateOrder = False
Left = 192
Top = 107
Height = 409
Width = 570
object ADOConnection1: TADOConnection
Connected = True
ConnectionString =
'Provider=SQLOLEDB.1;Persist Security Info=True;User ID=sa;Initia' +
'l Catalog=pubs;Data Source=(local)'
/////数据连接字符串,有必要看一看;
LoginPrompt = False
Provider = 'SQLOLEDB.1'
Left = 72
Top = 32
end
object MADODataSet: TADODataSet
Connection = ADOConnection1
CursorType = ctStatic
CommandText = 'select * from publishers'
Parameters = <>
Left = 64
Top = 104
object MADODataSetpub_name: TStringField
FieldName = 'pub_name'
Size = 40
end
object MADODataSetcity: TStringField
FieldName = 'city'
end
object MADODataSetstate: TStringField
FieldName = 'state'
FixedChar = True
Size = 2
end
object MADODataSetcountry: TStringField
FieldName = 'country'
Size = 30
end
end
object DADODataSet: TADODataSet
Connection = ADOConnection1
CursorType = ctStatic
CommandText = 'select * from titles'
DataSource = MDDataSource
IndexFieldNames = 'pub_id'/////////这儿建立关系;
MasterFields = 'pub_id' /////////这儿建立关系;
Parameters = <>
Left = 168
Top = 104
end
object GADODataSet: TADODataSet
Connection = ADOConnection1
Parameters = <>
Left = 272
Top = 104
end
object MDDataSource: TDataSource
DataSet = MADODataSet
Left = 264
Top = 32
end
object MDataSetProvider: TDataSetProvider
DataSet = MADODataSet
Constraints = True
Left = 64
Top = 168
end
object DDataSetProvider: TDataSetProvider
DataSet = DADODataSet
Constraints = True
Left = 176
Top = 168
end
object GDataSetProvider: TDataSetProvider
DataSet = GADODataSet
Constraints = True
Options = [poAllowCommandText]
Left = 280
Top = 168
end
end客户端窗体设计:
由于太多,我只取重要的一部分
object Form2: TForm2
Left = 176
Top = 154
BorderIcons = [biSystemMenu, biMinimize]
BorderStyle = bsSingle
Caption = '数据更新'
//////窗体描述;
TextHeight = 13
object DBGrid1: TDBGrid
Left = 0
Top = 25
Width = 440
Height = 120
Align = alTop
DataSource = MDataSource
TabOrder = 0
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -11
TitleFont.Name = 'MS Sans Serif'
TitleFont.Style = []
end
end
object DBGrid2: TDBGrid
Left = 0
Top = 168
Width = 440
Height = 180
Align = alBottom
DataSource = DDataSource
TabOrder = 2
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -11
TitleFont.Name = 'MS Sans Serif'
TitleFont.Style = []
end
object Button1: TButton
Left = 350
Top = 144
Width = 89
Height = 25
Caption = '数据更新'
TabOrder = 4
onClick = Button1Click
end
object DCOMConnection1: TDCOMConnection
ServerGUID = '{49477FD4-DD51-4F8B-ACD4-404A6C07D92F}'
ServerName = 'Server.DMF_LX'
ComputerName = 'Zsh'
Left = 16
Top = 64
end
object MClientDataSet: TClientDataSet
Aggregates = <>
DisableStringTrim = True
Params = <>
ProviderName = 'MDataSetProvider' ////下载数据
RemoteServer = DCOMConnection1
Left = 48
Top = 64
object MClientDataSetpub_id: TStringField
FieldName = 'pub_id'
ProviderFlags = [pfInUpdate, pfInWhere, pfHidden]
Visible = False
FixedChar = True
Size = 4
end
object MClientDataSetpub_name: TStringField
FieldName = 'pub_name'
Size = 40
end
object MClientDataSetcity: TStringField
FieldName = 'city'
end
object MClientDataSetstate: TStringField
FieldName = 'state'
FixedChar = True
Size = 2
end
object MClientDataSetcountry: TStringField
FieldName = 'country'
Size = 30
end
object MClientDataSetDADODataSet: TDataSetField
FieldName = 'DADODataSet'
//只有执行了Fatch Params时,才可以在这儿看到它,另一个数据集也才可以取这个下载的字段;
ProviderFlags = [pfInUpdate, pfInWhere, pfHidden]
Visible = False
end
end
object MDataSource: TDataSource
DataSet = MClientDataSet
Left = 80
Top = 64
end
object DClientDataSet: TClientDataSet
Aggregates = <> ///////计算字段;
DataSetField = MClientDataSetDADODataSet
Params = <>
Left = 56
Top = 216
object DClientDataSettitle: TStringField
FieldName = 'title'
Size = 80
end
object DClientDataSettype: TStringField
FieldName = 'type'
FixedChar = True
Size = 12
end
////这一部分是数据字段定义
object DClientDataSetpubdate: TDateTimeField
FieldName = 'pubdate'
end
end
object DDataSource: TDataSource
DataSet = DClientDataSet
Left = 88
Top = 216
end
object GClientDataSet: TClientDataSet
Aggregates = <>
Params = <>
RemoteServer = DCOMConnection1
Left = 232
Top = 216
end
end当你要更新时,只用执行ApplyUpdates(MasError)就中;
只需要执行Master ClinetDataSet的,不用执行Detial的,原因自己可以分析;
如果不明白,可以给我发短消息;
问题二:可能是有触发器改动了你的数据,也就是说,
现在ClientDataSet中的数据已经不是数据库中的数据,
加两句:
ClientDataSet.Close;
ClientDataSet.Open;
就可以了。问题三:建议你统一使用ApplyUpdate。
问题四:参考二。
我的删除和保存代码://~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//CustomClientDataSet.Delete(远程更新(三层结构))********************************
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function Data_SafeDelete(CustomClientDataSet: TCustomClientDataSet; DBGrid: TDBGrid = nil): Boolean; overload;
begin
if DBGrid<>nil then DBGridDelete(DBGrid);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Result := CustomClientDataSet.ApplyUpdates(0)=0;
if not Result then Abort;
end;//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//CustomClientDataSet.Save(远程更新(三层结构))**********************************
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function Data_SafeSave(CustomClientDataSet: TCustomClientDataSet; WinControl: TWinControl = nil; vRefrseh: Boolean = false): Boolean; overload;
var SavePlace: TBookMark;
begin
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//保存数据库指针记录**********************************************************
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SavePlace := CustomClientDataSet.GetBook;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Result := CustomClientDataSet.ApplyUpdates(0)=0;
if not Result then Abort;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//指定控件聚焦****************************************************************
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if WinControl<>nil then WinControl.SetFocus;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//刷新数据库和返回并释放书签**************************************************
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
with CustomClientDataSet do
try
if vRefrseh then
begin
Close;
Open;
GotoBook(SavePlace);
end;
finally
FreeBook(SavePlace);
end;
end;
问题三:原因如上
问题四:你在数据库中设置的主键仅是数据库的主键,ClientDataset并不知道,而有此操作需要主键,所以你必须在向ClientDataSet提供数据时指定主键,方法就是用ProviderFlags:
我一般是在与DataSetProvider相连的DataSet(如ADOQuery/ADODataSet...)的AfterOpen事件中用:FieldByName( 'ID' ).ProviderFlags := [pfInKey]来指定主键即可。另外,在多层应用中最好不要用Identity类型和Trigger,这种服务端的数据变化会导致多层数据更新的很多麻烦