原则:新开发始终使用 DAL2。仅在确认 DAL 没有字段依赖且不涉及 UI 交互时可考虑 DAL1。
三、DAL2 标准工作流
3.1 新建记录(Insert)
#include "bic_dam.h"
function long insert_record()
{
long ret
string dal.name(20)
dal.name = "twhinh215"
| Step 1: 开始新建
ret = dal.new.object(dal.name)
if ret <> 0 then
message("dal.new.object failed: %d", ret)
return(ret)
endif
| Step 2: 设置字段值
dal.set.field("whinh215.item", "ITM000123")
dal.set.field("whinh215.cwar", "WH001")
dal.set.field("whinh215.locn", "A01")
dal.set.field("whinh215.sqty", 100.0)
| Step 3: 保存
ret = dal.save.object(dal.name)
if ret <> 0 then
if ret = DALHOOKERROR then
message("Hook blocked the save")
elif ret = DALNOSETPERM then
message("No table level permission")
elif ret = DALNOOBJPERM then
message("No record level permission")
else
message("DB error: %d", ret)
endif
return(ret)
endif
message("Record inserted")
return(0)
}
3.2 修改记录(Update)
function long update_record()
{
long ret
string dal.name(20)
dal.name = "twhinh215"
| Step 1: 加锁(重要!dal.change.object 不加锁)
twhinh215.item = "ITM000123"
twhinh215.cwar = "WH001"
twhinh215.locn = "A01"
ret = db.bind(twhinh215, db.FIND.BY.KEYS, db.LOCK)
if ret <> 0 then
if ret = db.error.DBRECORDLOCKED then
message("Record is locked by another user")
endif
return(ret)
endif
| Step 2: 开始修改
ret = dal.change.object(dal.name)
if ret <> 0 then
db.release(twhinh215)
return(ret)
endif
| Step 3: 设置要修改的字段
dal.set.field("whinh215.sqty", 200.0)
| Step 4: 保存
ret = dal.save.object(dal.name)
db.release(twhinh215) | 释放锁!
return(ret)
}
3.3 删除记录(Delete)
function long delete_record()
{
long ret
string dal.name(20)
dal.name = "twhinh215"
ret = dal.delete.object(dal.name)
if ret <> 0 then
message("Delete failed: %d", ret)
return(ret)
endif
message("Record deleted")
return(0)
}
3.4 查找记录
function long find_record()
{
long ret
string dal.name(20)
dal.name = "twhinh215"
| 设置主键值
twhinh215.item = "ITM000123"
twhinh215.cwar = "WH001"
twhinh215.locn = "A01"
ret = dal.find.object(dal.name)
if ret = 0 then
| 找到记录,可读取字段值
message("Found: qty = %f", twhinh215.sqty)
else
message("Record not found")
endif
return(ret)
}
3.5 复制记录
function long copy_record()
{
long ret
string dal.name(20)
dal.name = "twhinh215"
| 先查找源记录
twhinh215.item = "ITM000123"
twhinh215.cwar = "WH001"
twhinh215.locn = "A01"
ret = dal.find.object(dal.name)
if ret <> 0 then
return(ret)
endif
| 复制
ret = dal.copy.object(dal.name)
if ret <> 0 then
return(ret)
endif
| 修改复制后的字段
dal.set.field("whinh215.locn", "A02")
dal.set.field("whinh215.sqty", 50.0)
| 保存新记录
ret = dal.save.object(dal.name)
return(ret)
}
dal.validate.field(mode):
if 字段为空 then
if not field.is.never.applicable() and field.is.applicable() then
if mode = DAL_UPDATE and field.is.readonly() then
return(DALHOOKERROR)
if field.is.derived() then
return(DALHOOKERROR)
if field.is.mandatory() then
return(DALHOOKERROR)
endif
else
if 是枚举字段 then
if field.is.never.applicable() then
return(DALHOOKERROR)
if not field.is.applicable() then
return(DALHOOKERROR)
if not field.enum.is.applicable(value) then
return(DALHOOKERROR)
if not field.enum.is.never.applicable(value) then
return(DALHOOKERROR)
else
if not field.is.valid() then
return(DALHOOKERROR)
endif
endif
return(0)
7.5 UI 字段状态自动推导
dal.get.field.state(mode):
if not field.is.applicable() then
return(DISABLED) | 字段禁用
if (mode = DAL_UPDATE and field.is.readonly()) or field.is.derived() then
return(READONLY) | 字段只读
return(ENABLED) | 字段可编辑
⚠️ 注意:官方文档未列出-1 作为 dal.save.object() 的返回值。如果收到 -1,应检查:① dal.change.object() 或 dal.new.object() 的返回值;② DAL 对象名称是否正确;③ DAL 是否已正确初始化。
9.2 db.bind() 错误码
返回值
常量
含义
0
—
成功
db.error.DBRECORDLOCKED
—
记录被其他用户锁定
db.error.DBNOTFOUND
—
记录不存在
db.error.DBERROR
—
其他数据库错误
9.3 标准异常处理模板
| 公共接口调用标准模板
long ret
domain tcmcs.s999m oMsg mb
long oID
ret = SomePublicInterface( ..., oMsg, oID )
if ret = 0 then
Exception.Delete(exception.id)
else
Exception.GetMessage( oID, 1, oMsg )
message( "错误: " & oMsg )
raise error
endif
9.4 异常处理函数
函数
说明
Exception.Delete(id)
删除异常(成功时调用)
Exception.GetMessage(id, seq, msg)
获取异常消息(seq 从 1 开始)
Exception.NumberOfMessages(id)
获取异常消息数量
十、加锁机制详解
10.1 为什么需要手动加锁
dal.change.object()不会自动加锁!多人同时修改同一记录会导致数据覆盖。
10.2 方式一:db.bind() 加锁(推荐)
function long update_with_lock()
{
long ret
string dal.name(20)
dal.name = "twhinh215"
| 加锁
twhinh215.item = "ITM000123"
twhinh215.cwar = "WH001"
twhinh215.locn = "A01"
ret = db.bind(twhinh215, db.FIND.BY.KEYS, db.LOCK)
if ret <> 0 then
if ret = db.error.DBRECORDLOCKED then
message("Record locked by another user")
endif
return(ret)
endif
| 修改
ret = dal.change.object(dal.name)
if ret <> 0 then
db.release(twhinh215)
return(ret)
endif
dal.set.field("whinh215.sqty", 200.0)
| 保存
ret = dal.save.object(dal.name)
db.release(twhinh215) | 始终释放!
return(ret)
}
10.3 方式二:SELECT FOR UPDATE
function long update_with_sql_lock()
{
long ret
select twhinh215.*
from twhinh215 for update
where twhinh215.item = :item
selectdo
twhinh215.sqty = twhinh215.sqty + 10.0
update twhinh215
commit.transaction()
selectempty
message("Record not found")
return(-1)
endselect
return(0)
}
function check_context()
long ctx
ctx = dal.context()
on case ctx
case dal.ctx.ui:
message("Running in UI context")
case dal.ctx.dal:
message("Running in DAL context")
case dal.ctx.integration:
message("Running in Integration context")
endcase
endfunction