马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?站点注册
×
PBIDEA:用 uo_database 与 uo_database_pool 做多库连接和连接池复用
经典 PowerBuilder 里我们习惯只用全局 SQLCA 一个事务对象,连一个库、跑一段嵌入式 SQL 就完事。一旦要同时连 SQL Server、Oracle、SQLite 多个库,或者程序里有多个线程/请求并发访问数据库,单 SQLCA 就不够用了:事务对象必须能并存、能复用,而不是每次都现建现连。
PBIDEA 在 sql.pbl 里给了两个对象解决这件事:uo_database(继承自 transaction,一个可独立创建的连接)和 uo_database_pool(连接池,按名字批量开通、随取随还)。这篇只讲清楚一件事:怎样用这两个对象把"多库连接"和"事务对象复用"做对。
一、单个 uo_database:比 SQLCA 多一个"可创建"的自由
uo_database 继承自 transaction,所以 DBMS、DBParm、ServerName、LogId、LogPass、Database 这些属性和经典事务对象一模一样;区别是它是对象,可以 create 任意多个,互不干扰。构造时它会自动调用底层 sqlCreate(),你不用管。
前置:本例用 SQLite(免安装、指定库文件路径即可跑)。其它库把 DBMS 改成 'MSS'/'Oracle'/'MySQL' 等,并把 DBParm 换成对应连接串(如 ServerName=...;LogId=...;LogPass=...;Database=...)。
步骤:1) 在按钮 clicked 事件里贴入代码;2) 确保已存在库文件 D:\demo.db 与表 t_user(name);3) 运行后看 MessageBox 弹出的行数。 - // 示例输入:数据库类型(SQLite 免安装,便于演示)
- string ls_dbms
- ls_dbms = 'SQLite'
- // 示例输入:连接串(SQLite 指定库文件路径;其它库换成 ServerName/LogId/LogPass/Database 等)
- string ls_dbparm
- ls_dbparm = 'Database=D:\demo.db'
- // 1) 创建并连接(uo_database 继承 transaction,构造时自动 sqlCreate)
- uo_database db
- db = create uo_database
- db.DBMS = ls_dbms
- db.DBParm = ls_dbparm
- if not db.Open() then
- MessageBox('连接失败', 'Open 返回 false,请检查 DBMS / DBParm')
- return
- end if
- // 2) 执行查询,直接拿回 uo_recordset
- uo_recordset rs
- rs = db.ExecuteSQL('SELECT name FROM t_user ORDER BY id')
- // 3) 逐行读取:FetchNext 取下一行,取不到返回 false
- string ls_name
- long ll_count
- ll_count = 0
- do while rs.FetchNext()
- ll_count++
- ls_name = rs.GetString('name')
- // 真实业务在这里处理 ls_name;本例仅做计数演示
- loop
- // 4) 提交并释放资源(顺序:先关记录集,再关连接)
- db.CommitTrans()
- rs.Close()
- destroy rs
- db.Close()
- destroy db
- MessageBox('查询结果', '共读取 ' + string(ll_count) + ' 行')
复制代码
要点:ExecuteSQL(sql) 直接返回 uo_recordset,不用再 DECLARE cursor;FetchNext() 推进游标、GetString('列名') 按列名取值。读完后记得 rs.Close() + destroy rs,再 db.Close() + destroy db,资源才真正释放。
二、多库并存:一个对象连一个库
要做"同时连两个库",就各 create 一个 uo_database,分别设不同的 DBMS/DBParm,各自 Open(),谁也不挡谁。下面演示一个连 SQL Server、一个连 SQLite 的最小骨架(只列连接部分,查询写法同一节):
- // 示例输入:SQL Server 连接信息
- string ls_mss_dbparm
- ls_mss_dbparm = 'ServerName=192.168.1.10;LogId=sa;LogPass=Pass@123;Database=erp'
- // 示例输入:SQLite 连接信息
- string ls_sqlite_dbparm
- ls_sqlite_dbparm = 'Database=D:\local.db'
- // 两个独立连接,互不影响
- uo_database db_mss
- db_mss = create uo_database
- db_mss.DBMS = 'MSS'
- db_mss.DBParm = ls_mss_dbparm
- db_mss.Open()
- uo_database db_lite
- db_lite = create uo_database
- db_lite.DBMS = 'SQLite'
- db_lite.DBParm = ls_sqlite_dbparm
- db_lite.Open()
- // 各自跑各自的查询,事务互不串
- uo_recordset rs_mss
- rs_mss = db_mss.ExecuteSQL('SELECT count(*) AS c FROM t_order')
- long ll_orders
- ll_orders = 0
- if rs_mss.FetchNext() then
- ll_orders = long(rs_mss.GetNumber('c'))
- end if
- db_mss.CommitTrans()
- rs_mss.Close()
- destroy rs_mss
- db_mss.Close()
- destroy db_mss
- db_lite.Close()
- destroy db_lite
- MessageBox('多库统计', 'SQL Server 订单数:' + string(ll_orders))
复制代码
注意 GetNumber('c') 返回 double,用 long(...) 包一层转成整数再赋给 long 变量,避免类型不匹配的编译告警。
三、连接池:用 uo_database_pool 复用事务对象
每次都 create+Open 连数据库,高并发下开销很大。PBIDEA 的 uo_database_pool 解决"复用":先用一个模板连接告诉它连哪个库,它按这个名字批量开通若干连接;业务里用 QueryPool(名字) 取一条空闲连接,用完 GiveBackPool(db) 还回去,连接并不真正断开,下次别人还能用。
前置:uo_database_pool 构造时自动 InitPool();CreatePool(名字, 模板db) 会按模板 db 的 DBMS/DBParm 批量开通预设连接。池里的连接若 10 分钟无活动会被自动断开,取用时用 IsOpen()/IsAlive() 探活即可。
步骤:1) 建模板连接 db_tpl 并 Open();2) create 连接池并 CreatePool;3) 业务里 QueryPool 取连接 → 干活 → GiveBackPool 归还;4) 程序退出前 DestroyPool() 释放全部底层连接。 - // 示例输入:连接池名称(一组连接共用,例如 sql1 / ora1)
- string ls_pool
- ls_pool = 'demo_pool'
- // 示例输入:模板连接的库类型与连接串(连接信息从它身上取)
- string ls_dbms
- ls_dbms = 'SQLite'
- string ls_dbparm
- ls_dbparm = 'Database=D:\demo.db'
- // 1) 模板连接(只用来提供连接信息,并作为首批开通依据)
- uo_database db_tpl
- db_tpl = create uo_database
- db_tpl.DBMS = ls_dbms
- db_tpl.DBParm = ls_dbparm
- if not db_tpl.Open() then
- MessageBox('模板连接失败', 'Open 返回 false')
- return
- end if
- // 2) 创建连接池,按模板批量开通预设连接
- uo_database_pool pool
- pool = create uo_database_pool
- pool.CreatePool(ls_pool, db_tpl)
- // 3) 业务侧从池中申请一条空闲连接
- uo_database db
- db = pool.QueryPool(ls_pool)
- if not db.IsOpen() then
- MessageBox('取连接失败', 'QueryPool 返回的连接未打开')
- return
- end if
- // 4) 用这条连接统计行数(事务对象被复用,不再现建现连)
- uo_recordset rs
- rs = db.ExecuteSQL('SELECT count(*) AS c FROM t_user')
- long ll_total
- ll_total = 0
- if rs.FetchNext() then
- ll_total = long(rs.GetNumber('c'))
- end if
- db.CommitTrans()
- // 5) 归还连接(destroy 也会自动归还;这里显式还,方便后续请求复用)
- pool.GiveBackPool(db)
- // 6) 程序退出前销毁整个池,释放全部底层连接
- pool.DestroyPool()
- MessageBox('连接池演示', '从池取连接统计得 ' + string(ll_total) + ' 行,已归还并销毁池')
复制代码
生命周期要记牢:取连接用 QueryPool,用完用 GiveBackPool 还;不要对从池里取出来的 db 调 destroy(销毁池 DestroyPool() 会统一释放全部底层连接)。模板连接 db_tpl 在 CreatePool 之后也可以 db_tpl.Close() + destroy 关掉,不影响池里已开通的连接。
小结
- uo_database = 一个可独立 create 的事务对象,Open() 连、ExecuteSQL() 取 uo_recordset、FetchNext()+GetString/GetNumber 取值、CommitTrans()/Close() 收尾。
- 多库并存 = 每种库各 create 一个 uo_database,属性互不干扰。
- 连接池复用 = uo_database_pool 的 CreatePool → QueryPool 取 → GiveBackPool 还 → DestroyPool 释放;事务对象不再现建现连,高并发下省开销。
- 资源释放顺序固定:先 rs.Close()+destroy rs,再 db.Close()+destroy db(池连接用 GiveBackPool 代替 destroy)。
|