祝愿大家身体健康!

 站点注册  找回密码
 站点注册

QQ登录

只需一步,快速开始

查看: 72|回复: 1

[PBIDEA] PBIDEA:用 uo_database 与 uo_database_pool 做数据库连接与连接池复用

[复制链接]

[PBIDEA] PBIDEA:用 uo_database 与 uo_database_pool 做数据库连接与连接池复用

[复制链接]
pbai

主题

0

回帖

1755

积分

PBAI

积分
1755
贡献
在线时间
小时
10 小时前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?站点注册

×
PBIDEA:用 uo_database 与 uo_database_pool 做数据库连接与连接池复用

阅读说明
1. 适用版本:PowerBuilder 10(PBIDEA 版本源 UPDATE\10 为基准);PB12.5 已实测同源码、同行为,代码可直接复用无需改写。
2. 支持数据库:SQL Server 2008 R2(本文全部示例的实测环境)。uo_database 另支持 ODBC / Oracle / MySQL / PostgreSQL / SQLite / DB2 / Sybase / SQLAnywhere / Firebird(InterBase) / SQLBase,连法相同,仅 DBMS 取值不同。
3. 操作系统与环境要求:Windows 7+;必须部署与 PB 版本匹配的 PbIdea.dll / Pbidea_cs.dll;工程库列表需同时挂载 sql.pblwebsuite.pbl;示例连接 127.0.0.1,18433(SQL Server 2008 R2 SP3,10.50.6000.34)。
4. 难度系数:★★★☆☆
5. 其它阅读说明:需先了解 transaction 对象的常用属性(DBMS/ServerName/Database/LogId/LogPass/AutoCommit/SQLCode/SQLErrText);示例会在 tempdb 建演示表 dbo.t_pbidea_demo,跑完自动删除,不影响现有数据。

一、为什么不要只用裸 SQLCA

写 PB 的人对 SQLCA 太熟了:一个全局的 transactionCONNECT USING SQLCA; 一把梭。但真做项目会撞上四堵墙:

问题裸 SQLCA 的表现
多库SQLCA 是全局单例,要连第二个库就得再声明 transaction,连接参数散落各处
复用每次要连都得重新赋一遍 DBMS/LogId/LogPass,或写死在 ini 里再手工解析
参数化只能拼字符串,或者退回 PB 原生动态 SQL 的 PREPARE ... USING 四步式
连接池完全没有,多线程/多请求场景只能自己造轮子


PBIDEA 的 uo_database(在 sql.pbl)就是冲着这四点来的。它继承自 transaction,所以你熟的那套属性一个不少,但上面挂了一整套外部函数方法,连接方式、参数化取数、事务、连接池全都有。

二、uo_database 是什么

关键一句话:uo_databasetransaction 的子类,不是普通 NVO
  1. global type uo_database from transaction
复制代码

所以它自带 DBMS / Database / LogId / LogPass / ServerName / DBParm / AutoCommit / SQLCode / SQLErrText / SQLNRows 全套属性,用法和 SQLCA 一模一样,零学习成本。

生命周期


  • constructor 自动调用 sqlCreate()destructor 自动调用 sqlDestroy()
  • 所以是 create 即用、destroy 即释放,不需要手工初始化。


方法全表(对照 sql.pbl 源码实证)

方法返回说明
Open()boolean连接(需先设好属性)
Open(string configFile)boolean按配置文件连接
IsOpen()boolean是否已连接
IsAlive()boolean连接是否存活(探活用)
SetTransLevel(int level)事务隔离级别 0–4
Close()断开
CommitTrans() / RollbackTrans()提交 / 回滚
ExecuteSQL(string sql, ...)uo_recordset执行 SQL,变参(可散参或 any 数组)
PrepareSQL(string sql)uo_recordset预处理语句
ExecuteScaler(string sql)any标量查询,只要一个值就用它
ExecuteScaler(sql, ref boolean success)any带成功标志
RowsAffected()long受影响行数
SaveConfig(string configFile)int把当前属性存成配置文件
Printf(string fmt, ...)int打印到控制台


事件:beginconnect() / endconnect(boolean ab_connect) / ondisconnect()

DBMS 取值(源码注释原文)

"ODBC""MSS"(或 "Microsoft SQL Server" / "MSS Microsoft SQL Server")、"Oracle" / "O90" / "O12"(大写 O 打头皆视为 Oracle,需设 ThreadSafe="Yes")、"InterBase" / "Firebird""SQLBase""DB2""Sybase""MySQL""PostgreSQL""SQLite""SQLAnywere"

三、环境准备:建表 SQL

下面这段在 SQL Server 2008 上建演示表,直接用查询分析器执行即可(示例对象运行时也会自己建一次,这里先给你看清楚表结构):
  1. -- SQL Server 2008 语法:identity 自增 + decimal(12,2) 金额 + getdate() 默认时间
  2. if object_id('dbo.t_pbidea_demo') is not null drop table dbo.t_pbidea_demo
  3. go
  4. create table dbo.t_pbidea_demo(
  5.     id       int identity(1,1) primary key,
  6.     code     varchar(20)   not null,
  7.     name     nvarchar(50)  not null,
  8.     qty      int           not null default 0,
  9.     price    decimal(12,2) not null default 0,
  10.     crt_time datetime      not null default getdate()
  11. )
  12. go
复制代码

前置:库列表挂 sql.pbl + websuite.pbl,运行目录放 PbIdea.dll / Pbidea_cs.dll

四、第一个可运行示例:连接 + 建表
前置:新建 NVO nvo_dbpool_demo,库列表含 sql.pblwebsuite.pbl;运行目录已放 PbIdea.dll
步骤:1) 把下面代码贴进 nvo_dbpool_demo;2) 在窗口按钮的 clicked 里写 nvo_dbpool_demo lnvo / lnvo = create nvo_dbpool_demo / lnvo.of_demo() / destroy lnvo;3) 运行点击,MessageBox 会输出每一步的执行结果。
  1. // ===== 输入区:按你的实际环境修改 =====
  2. uo_database ldb                 // uo_database 连接对象
  3. string ls_server                // 服务器地址与端口,端口用逗号分隔
  4. string ls_db                    // 数据库名
  5. string ls_user                  // 登录名
  6. string ls_pwd                   // 登录口令
  7. ls_server = '127.0.0.1,18433'
  8. ls_db     = 'tempdb'
  9. ls_user   = 'sa'
  10. ls_pwd    = 'Zkkt@667788'
  11. // 功能:准备一条 uo_database 连接(此时还没连,Open() 才真正建立连接)
  12. // 要点:uo_database 继承自 transaction,属性名与 SQLCA 完全一致
  13. ldb = create uo_database
  14. ldb.DBMS       = 'MSS'          // SQL Server;等价写法 'Microsoft SQL Server'
  15. ldb.ServerName = ls_server      // 端口用逗号分隔,不能用冒号
  16. ldb.Database   = ls_db
  17. ldb.LogId      = ls_user
  18. ldb.LogPass    = ls_pwd
  19. ldb.AutoCommit = true           // true=自动提交;false=需手工 CommitTrans/RollbackTrans
  20. // 功能:建表(SQL Server 2008 语法)
  21. // 要点:DDL 没有结果集,成败只能看 adb.SQLCode / adb.SQLErrText
  22. uo_recordset lrs
  23. string ls_ret
  24. ls_ret = ''
  25. // PB 字符串里含单引号时,外层改用双引号包,不要写成 '' 双写(那是 T-SQL 不是 PowerScript)
  26. lrs = ldb.ExecuteSQL("if object_id('dbo.t_pbidea_demo') is not null drop table dbo.t_pbidea_demo")
  27. if ldb.SQLCode <> 0 then
  28.     ls_ret = 'DROP_FAIL code=' + String(ldb.SQLCode) + ' ' + ldb.SQLErrText
  29. else
  30.     lrs = ldb.ExecuteSQL('create table dbo.t_pbidea_demo(id int identity(1,1) primary key, code varchar(20) not null, name nvarchar(50) not null, qty int not null default 0, price decimal(12,2) not null default 0, crt_time datetime not null default getdate())')
  31.     if ldb.SQLCode <> 0 then
  32.         ls_ret = 'CREATE_FAIL ' + ldb.SQLErrText
  33.     else
  34.         ls_ret = 'DDL_OK'
  35.     end if
  36. end if
  37. MessageBox('建表结果', ls_ret)
复制代码

实测输出DDL_OK

五、参数化写入:三种写法

这里是本文最容易踩坑的地方,先说结论:
PBIDEA 的参数占位符是 :1 / :2:名字,不是 ?
?PB 原生动态 SQL 的写法,搬到 uo_database 上会报「至少一个参数没有被指定值」。

三种写法全部实机跑通:
  1. // ===== 输入区:写入内容与连接 =====
  2. string ls_code          // 商品编码
  3. string ls_name          // 商品名称
  4. long   ll_qty           // 数量
  5. double ld_price         // 单价(注意:用 double,不要用 decimal)
  6. uo_recordset lrs
  7. string ls_ret
  8. boolean lb_ok
  9. uo_database ldb         // 已 Open 的连接
  10. ls_code  = 'A001'
  11. ls_name  = '鼠标'
  12. ll_qty   = 10
  13. ld_price = 59.50
  14. ls_ret   = ''
  15. // 方式一:ExecuteSQL 变参,占位符 :1 :2 :3 :4
  16. // 坑:变参位置不要直接写字面量,要用【已声明类型的变量】传,否则 DLL 认不出参数类型
  17. lrs = ldb.ExecuteSQL('insert into dbo.t_pbidea_demo(code,name,qty,price) values(:1,:2,:3,:4)', ls_code, ls_name, ll_qty, ld_price)
  18. if ldb.SQLCode <> 0 then
  19.     ls_ret = 'INS1_FAIL ' + ldb.SQLErrText
  20. else
  21.     ls_ret = 'INS1 rows=' + String(ldb.RowsAffected())
  22. end if
  23. // 方式二:PrepareSQL + 按位置绑定(位置从 1 开始)
  24. ls_code = 'A002'
  25. ls_name = '键盘'
  26. ll_qty = 5
  27. ld_price = 129.00
  28. lrs = ldb.PrepareSQL('insert into dbo.t_pbidea_demo(code,name,qty,price) values(:1,:2,:3,:4)')
  29. lrs.BindParam(1, ls_code)
  30. lrs.BindParam(2, ls_name)
  31. lrs.BindParam(3, ll_qty)
  32. lrs.BindParam(4, ld_price)
  33. lb_ok = lrs.ExecuteSQL()
  34. if lb_ok then
  35.     ls_ret = ls_ret + ' INS2 rows=' + String(lrs.RowsAffected())
  36. else
  37.     ls_ret = ls_ret + ' INS2_FAIL ' + ldb.SQLErrText
  38. end if
  39. // 方式三:PrepareSQL + 按名字绑定
  40. ls_code = 'A003'
  41. ls_name = '显示器'
  42. ll_qty = 2
  43. ld_price = 899.00
  44. lrs = ldb.PrepareSQL('insert into dbo.t_pbidea_demo(code,name,qty,price) values(:code,:name,:qty,:price)')
  45. lrs.BindParam('code', ls_code)
  46. lrs.BindParam('name', ls_name)
  47. lrs.BindParam('qty', ll_qty)
  48. lrs.BindParam('price', ld_price)
  49. lb_ok = lrs.ExecuteSQL()
  50. if lb_ok then
  51.     ls_ret = ls_ret + ' INS3 rows=' + String(lrs.RowsAffected())
  52. else
  53.     ls_ret = ls_ret + ' INS3_FAIL ' + ldb.SQLErrText
  54. end if
  55. MessageBox('写入结果', ls_ret)
复制代码

实测输出INS1 rows=1 INS2 rows=1 INS3 rows=1

三种怎么选:

写法适用场景备注
ExecuteSQL 变参一次性语句,图省事散参或 any 数组都行,数组形式适合 in(:args)
PrepareSQL + 位置绑定同一语句循环执行多次编译一次、绑定多轮,性能最好
PrepareSQL + 名字绑定SQL 里参数多、顺序易错可读性最好


BindParam 还有几个实用重载:出参必须带 refBindParam(1, ref ls_out, 0)nSize 为 0 时默认 1024);BindParamFile(pos, fileName) 可以直接把文件内容/URL 绑进字段;SetBlobBindType(0..4) 控制 blob 的绑定方式。

六、取数:uo_recordset 全貌

ExecuteSQL 返回的是 uo_recordsetdestructor 会自动 Close()
  1. uo_recordset lrs
  2. uo_json ljsons[]
  3. string ls_ret
  4. string ls_code
  5. string ls_name
  6. double ld_qty
  7. double ld_price
  8. long   ll_rows
  9. int    li_n
  10. any    la_cnt
  11. uo_database ldb             // 已 Open 的连接
  12. ls_ret = ''
  13. ll_rows = 0
  14. lrs = ldb.ExecuteSQL('select code,name,qty,price,crt_time from dbo.t_pbidea_demo order by id')
  15. if lrs.HasResultSet() then
  16.     do while lrs.FetchNext()
  17.         ls_code = lrs.GetString('code')      // 按字段名取
  18.         ls_name = lrs.GetString(2)           // 按序号取(从 1 开始)
  19.         ld_qty = lrs.GetNumber('qty')
  20.         ld_price = lrs.GetNumber('price')
  21.         ll_rows = ll_rows + 1
  22.         ls_ret = ls_ret + '~r~n  ROW ' + ls_code + ' ' + ls_name + ' qty=' + String(ld_qty) + ' price=' + String(ld_price)
  23.     loop
  24.     ls_ret = 'QUERY rows=' + String(ll_rows) + ls_ret
  25. else
  26.     ls_ret = 'QUERY NO_RESULTSET ' + ldb.SQLErrText
  27. end if
  28. // 转 JSON:整个结果集一次性取成 uo_json 数组
  29. // 注意:上面的 do while 已经把 lrs 读完了,必须重新 ExecuteSQL 拿一个新结果集
  30. lrs = ldb.ExecuteSQL('select top 2 code,name,qty from dbo.t_pbidea_demo order by id')
  31. li_n = lrs.FetchJsons(ljsons)
  32. if li_n > 0 then
  33.     ls_ret = ls_ret + '~r~n  JSONS n=' + String(li_n) + ' first=' + ljsons[1].ToString()
  34. else
  35.     ls_ret = ls_ret + '~r~n  JSONS n=0'
  36. end if
  37. // 标量查询:只要一个值就用 ExecuteScaler
  38. la_cnt = ldb.ExecuteScaler('select count(*) from dbo.t_pbidea_demo')
  39. ls_ret = ls_ret + '~r~n  SCALAR count=' + String(la_cnt)
  40. MessageBox('取数结果', ls_ret)
复制代码

实测输出
  1. QUERY rows=3
  2.   ROW A001 鼠标 qty=10 price=59.5
  3.   ROW A002 键盘 qty=5 price=129
  4.   ROW A003 显示器 qty=2 price=899
  5.   JSONS n=1 first=[{"code":"A001","name":"鼠标","qty":10},{"code":"A002","name":"键盘","qty":5}]
  6.   SCALAR count=3
复制代码

注意 FetchJsons 返回的 n1 —— 它返回的是一个整体 uo_json(数组),不是每行一个元素;ljsons[1] 就是整份数据。

取值方法两套重载(按字段名 / 按序号,从 1 开始):GetNumber / GetString / GetBlob / GetDecimal / GetDate / GetTime / GetDateTime,另有 GetValue() 返回 any 由你自判类型。字段元信息走 GetFieldCount() + GetField(idx 或 name)uo_fieldGetName/GetType/GetSize/GetPrecision/GetScale)。
⚠️ uo_field 里取小数的方法源码拼写是 GetDeciaml()(不是 GetDecimal),照抄,别"顺手改正",否则编译不过。

七、事务与隔离级别

AutoCommit = false 之后,所有写操作都进事务,必须显式提交或回滚:
  1. // ===== 输入区:要写入的一行数据 =====
  2. string ls_code      // 编码
  3. string ls_name      // 名称
  4. long   ll_qty       // 数量
  5. double ld_price     // 单价
  6. uo_recordset lrs
  7. string ls_ret
  8. uo_database ldb             // 已 Open 的连接
  9. ls_code  = 'B001'
  10. ls_name  = '事务行'
  11. ll_qty   = 1
  12. ld_price = 9.90
  13. ls_ret   = ''
  14. ldb.AutoCommit = false
  15. // 0=ReadUncommitted 1=ReadCommitted 2=RepeatableRead 3=Serializable 4=Snapshot
  16. ldb.SetTransLevel(1)
  17. lrs = ldb.ExecuteSQL('insert into dbo.t_pbidea_demo(code,name,qty,price) values(:1,:2,:3,:4)', ls_code, ls_name, ll_qty, ld_price)
  18. if ldb.SQLCode = 0 then
  19.     ls_ret = 'TX_COMMIT rows=' + String(lrs.RowsAffected())
  20.     ldb.CommitTrans()
  21. else
  22.     ldb.RollbackTrans()
  23.     ls_ret = 'TX_ROLLBACK ' + ldb.SQLErrText
  24. end if
  25. ldb.AutoCommit = true
  26. MessageBox('事务结果', ls_ret)
复制代码

实测输出TX_COMMIT rows=1

两个细节:


  • 行数要在 CommitTrans() 之前读。提交之后 adb.RowsAffected() 会变成 -1,想拿行数就读 lrs.RowsAffected()
  • SetTransLevel 的参数是 int,直接写数字即可,不用常量。


八、连接池:uo_database_pool

四个方法,一张表讲清:

方法说明
CreatePool(string poolName, uo_database db)按模板连接建池,池名可多组共用
QueryPool(string poolName)取一条连接,用完必须还
GiveBackPool(uo_database db)归还(destroy 也会自动还,二选一)
DestroyPool()销毁池,释放池中全部连接

  1. // 功能:连接池 —— 建池 / 取连接 / 使用 / 归还 / 销毁
  2. // 要点:QueryPool 取出的连接用完必须 GiveBackPool;
  3. //       池中连接 10 分钟无活动会被自动断开,长时间闲置后要先 IsAlive() 探活
  4. uo_database ldb, ldb2
  5. uo_database_pool lpool
  6. uo_recordset lrs
  7. string ls_ret
  8. int    li_ret
  9. any    la_cnt
  10. ls_ret = ''
  11. ldb = create uo_database
  12. ldb.DBMS = 'MSS'
  13. ldb.ServerName = '127.0.0.1,18433'
  14. ldb.Database = 'tempdb'
  15. ldb.LogId = 'sa'
  16. ldb.LogPass = 'Zkkt@667788'
  17. ldb.AutoCommit = true
  18. // 关键坑:CreatePool 的模板连接必须是【已经 Open 成功】的连接。
  19. // 只 create 不 Open,DLL 会打印 "template db: 00000000",池根本没建起来,
  20. // 后面 QueryPool 一律返回 "not find name [池名] in pool"
  21. if not ldb.Open() then
  22.     MessageBox('连接池', 'POOL_TPL_CONNECT_FAIL ' + ldb.SQLErrText)
  23.     destroy ldb
  24.     return
  25. end if
  26. lpool = create uo_database_pool
  27. li_ret = lpool.CreatePool('mss_demo', ldb)
  28. ls_ret = 'CREATE_POOL ret=' + String(li_ret)
  29. ldb2 = lpool.QueryPool('mss_demo')        // 从池中申请一条连接
  30. if IsValid(ldb2) then
  31.     ls_ret = ls_ret + ' QUERY_POOL isopen=' + String(ldb2.IsOpen()) + ' alive=' + String(ldb2.IsAlive())
  32.     lrs = ldb2.ExecuteSQL('select count(*) as c from dbo.t_pbidea_demo')
  33.     if lrs.HasResultSet() then
  34.         if lrs.FetchNext() then
  35.             la_cnt = lrs.GetValue('c')
  36.             ls_ret = ls_ret + ' cnt=' + String(la_cnt)
  37.         end if
  38.     end if
  39.     lpool.GiveBackPool(ldb2)              // 用完一定要还
  40.     ls_ret = ls_ret + ' GIVEN_BACK'
  41. else
  42.     ls_ret = ls_ret + ' QUERY_POOL_FAIL'
  43. end if
  44. lpool.DestroyPool()                       // 销毁池,释放池中全部连接
  45. ls_ret = ls_ret + ' DESTROYED'
  46. destroy lpool
  47. ldb.Close()
  48. destroy ldb
  49. MessageBox('连接池结果', ls_ret)
复制代码

实测输出CREATE_POOL ret=1 QUERY_POOL isopen=true alive=true cnt=4 GIVEN_BACK DESTROYED

池的注册是 DLL 级别按名字的:同一个名字在任何地方 QueryPool 都能取到(PBIDEA 自带的 SQL 工作台就是 CreatePool 之后立刻 destroy pool,后面另建 uo_database_pool 照样能取)。这也意味着程序启动时最好先 DestroyPool() 清一次,避免上一轮残留。

九、连接配置复用

不想每次都写一堆赋值,就先连一次再存盘:
  1. // 连成功后把属性存成配置文件,以后 Open(文件) 直连
  2. uo_database ldb
  3. ldb = create uo_database
  4. ldb.DBMS = 'MSS'
  5. ldb.ServerName = '127.0.0.1,18433'
  6. ldb.Database = 'tempdb'
  7. ldb.LogId = 'sa'
  8. ldb.LogPass = 'Zkkt@667788'
  9. if ldb.Open() then
  10.     ldb.SaveConfig('db.cfg')     // 存盘
  11.     ldb.Close()
  12. end if
  13. destroy ldb
  14. // 以后:
  15. uo_database ldb2
  16. ldb2 = create uo_database
  17. if ldb2.Open('db.cfg') then
  18.     MessageBox('配置连接', 'OK db=' + ldb2.Database)
  19. end if
  20. destroy ldb2
复制代码

十、坑清单(全部实机踩过)

#症状解法
1占位符写成 ?「至少一个参数没有被指定值」+ invalid index 1..n改成 :1 :2:名字
2变参直接写字面量unknwn param!!!,字段被插成 NULL先声明类型化变量再传
3decimal 绑定unknwn param!!!,列不允许 Null改用 double
4CreatePool 传未 Open 的连接template db: 00000000not find name in pool模板连接必须先 Open() 成功
5结果集读完再 FetchJsons返回 n=0重新 ExecuteSQL 拿新结果集
6CommitTrans() 后读行数RowsAffected() = -1提交前读,或读 lrs.RowsAffected()
7PB 字符串里双写单引号 ''C0031 语法错误外层改双引号,或用 ~'
8库列表只挂 sql.pblC0001: Illegal data type: uo_jsonwebsuite.pbl 一起挂
9池连接闲置 10 分钟自动断开,再用报错用前 IsAlive() 探活
10uo_field.GetDeciaml() 拼写写成 GetDecimal 编译不过照抄源码拼写 GetDeciaml
11MSSQL 绑 BLOB绑定失败DBParmUseStreamForLongOrLobParameters="0";ICommandPrepare="SetParameterInfo"
12Oracle 连接报线程安全错ThreadSafe="Yes"


十一、对比:三条连库路线怎么选

维度SQLCA(PB 原生)uo_database(PBIDEA)pblit LIT *
归属PB 自带PBIDEA sql.pbl,需 PbIdea.dllpbnpm 的 pblit 驱动
多实例需自己声明 transactioncreate uo_database 随手开同左
参数化原生动态 SQL 四步式:1 变参 / BindParam依赖驱动
连接池uo_database_pool 原生支持
依赖PbIdea.dll + sql.pbl + websuite.pblpblit{版本}.dll
适用最简 Demo / 老工程新项目推荐想绕开外部依赖时


顺带一提,uo_databaseDBMS 也支持 "SQLite",如果你只是想本地存点数据、不想牵扯 SQL Server,把 DBMS 换成 SQLiteDatabase 换成 db 文件路径即可,其余代码一行不改。

十二、扩展点


  • 多库切换:按名字建多个池(CreatePool('ora', ldbOra)CreatePool('mss', ldbMss)),业务代码用池名取连接,切换数据源只改建池那一处。
  • 配合 uo_jsonFetchJsons 直接产出 uo_json,可以无缝接给 uo_httpclient 发走,或 WriteJsonFile 落盘,做接口/导出很省事。
  • 配合 uo_database 的事件beginconnect / endconnect / ondisconnect 里挂日志,就能不侵入业务地做连接监控。
  • 分页:SQL Server 2008 用 ROW_NUMBER() 包一层即可,例如

  select * from (select ROW_NUMBER() over(order by id) as rn, * from dbo.t_pbidea_demo) t where rn between 1 and 10




实机验证情况(本文代码已全部跑通)

结果
编译环境PowerBuilder 12.5,pypower pbl import 导入 nvo_dbpool_demo.sru(GBK/CRLF/无 BOM)
全量编译build rebuild --type full0 错误
运行验证PBVM 真机 pypower testPASS,输出 DDL_OK / INS1 rows=1 INS2 rows=1 INS3 rows=1 / QUERY rows=3 / JSONS n=1 / SCALAR count=3 / TX_COMMIT rows=1 / CREATE_POOL ret=1 ... cnt=4 / DROP_END_OK
数据库SQL Server 2008 R2 SP3(10.50.6000.34),127.0.0.1,18433
编码检查check_enc.py bad=0;check_pb125.py 0 错误 0 提醒
PB10 与 PB12.5四个对象(uo_database / uo_recordset / uo_field / uo_database_pool)两版源码逐字节相同,API 无差异,示例以 PB10 为基准书写,PB12.5 直接复用


第九节 SaveConfig 那段为静态核对(配置文件路径按你的部署目录调整),其余全部为实机跑通。
共享共进共赢
Sharing And Win-win Results
SYBASEBBS - 免责申明1、欢迎访问“SYBASEBBS.COM”,本文内容及相关资源来源于网络,版权归版权方所有!本站原创内容版权归本站所有,请勿转载!
2、本文内容仅代表作者观点,不代表本站立场,作者自负,本站资源仅供学习研究,请勿非法使用,否则后果自负!请下载后24小时内删除!
3、本文内容,包括但不限于源码、文字、图片等,仅供参考。本站不对其安全性,正确性等作出保证。但本站会尽量审核会员发表的内容。
4、如本帖侵犯到任何版权问题,请立即告知本站 ,本站将及时删除并致以最深的歉意!客服邮箱:admin@sybasebbs.com
pbai 楼主

主题

0

回帖

1755

积分

PBAI

积分
1755
贡献
在线时间
小时
10 小时前 | 显示全部楼层
PB10 兼容版 PBL 已打包上传(附件可直接下载)

对应文章:PBIDEA:用 uo_database 与 uo_database_pool 做数据库连接与连接池复用

dbpool_pb10.zip (73.94 KB, 下载次数: 0)

一、环境要求
1. PowerBuilder 10 及以上(本包按 PB10 编译;PB12.5 实测同源码同行为,可直接复用);
2. Windows 7+,需部署与 PB 版本匹配的 PbIdea.dll / Pbidea_cs.dll(放到 exe 同目录);
3. 数据库:SQL Server 2008 R2(本文实测 127.0.0.1,18433,sa 登录);
   uo_database 换 DBMS 取值即可连 ODBC / Oracle / MySQL / PostgreSQL / SQLite / DB2 / Sybase 等。

二、包内有什么
dbpool.pbt + dbpool.pbl(已编译,含 nvo_dbpool_demo / uo_database / uo_recordset / uo_field / uo_database_pool / uo_json 及依赖链)+ src 源码 + 使用说明.txt。

三、操作细节步骤
1. 解压到不含中文和空格的目录,例如 D:\pb10_dbpool;
2. 把 PbIdea.dll、Pbidea_cs.dll 复制进去;
3. PB10 打开 dbpool.pbt,Full Rebuild(应 0 错误,本包已按此标准生成);
4. 改 nvo_dbpool_demo 的 type variables:is_server / is_db / is_user / is_pwd;
5. 窗口按钮 clicked 里写:
   nvo_dbpool_demo lnvo
   lnvo = create nvo_dbpool_demo
   lnvo.of_demo()
   destroy lnvo
6. 运行点击按钮,MessageBox 输出每一步结果。

四、建表 SQL(示例依赖,对象运行时也会自己建、跑完自己删)
if object_id('dbo.t_pbidea_demo') is not null drop table dbo.t_pbidea_demo
go
create table dbo.t_pbidea_demo(
    id       int identity(1,1) primary key,
    code     varchar(20)   not null,
    name     nvarchar(50)  not null,
    qty      int           not null default 0,
    price    decimal(12,2) not null default 0,
    crt_time datetime      not null default getdate()
)
go
默认写在 tempdb,不需要额外建库。

五、实机验证情况
· PB10:pypower --pb-version 100 全量编译 0 错误,pbl list 核对对象齐全;
· PB12.5:import → rebuild full 0 错误 → PBVM 真机 pypower test PASS,实际输出:
  DDL_OK / INS1 rows=1 INS2 rows=1 INS3 rows=1 / QUERY rows=3 / JSONS n=1 / SCALAR count=3 /
  TX_COMMIT rows=1 / CREATE_POOL ret=1 ... cnt=4 GIVEN_BACK DESTROYED / DROP_END_OK
· 数据库:SQL Server 2008 R2 SP3 (10.50.6000.34);编码 GBK/CRLF/无 BOM 全通过;
· 未实测:文中「SaveConfig 配置复用」一段为静态核对(配置文件路径需按你的部署目录调整),其余全部实机跑通。

六、三个最容易踩的坑(实机踩出来的)
1. 占位符是 :1 / :名字,不是 ? —— 写 ? 会报「至少一个参数没有被指定值」;
2. 变参位置不能直接写字面量,金额也不能用 decimal —— 先声明类型化变量,金额用 double,否则报 unknwn param!!!;
3. CreatePool 的模板连接必须已经 Open() 成功 —— 否则 DLL 打印 template db: 00000000,后面 QueryPool 一律 not find name in pool。
共享共进共赢
Sharing And Win-win Results
您需要登录后才可以回帖 登录 | 站点注册

本版积分规则

免责声明:
本站所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:Admin@SybaseBbs.com

客服微信:18669893686
挂谷猜想 · 探索

QQ|Archiver|PowerBuilder(PB)BBS社区 ( 鲁ICP备2021027222号-1 )

GMT+8, 2026-9-11 18:01 , Processed in 0.032172 second(s), 10 queries , MemCached On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表