Groovy中互不关联SQL查询互相干扰引发空对象访问错误
解决Groovy操作LIMS时查询互相干扰导致DataSet为空的问题
问题重现
用Groovy向LIMS导入数据时触发错误:
Groovy block failed: Expression Error: "Cannot get property 'trackitemid' on null object"
定位后发现:
- 保留包裹重复检查SQL时,后续查询
trackitem表获取trackitemid的SQL返回sapphire.util.DataSet为空,但在SSMS中执行该查询能得到预期结果- 注释掉包裹检查的SQL后,
trackitem查询恢复正常- 两个查询无共享变量,调整包裹查询位置也无法解决,需要保留包裹重复检查逻辑同时消除干扰
解决方案
1. 显式释放前一个查询的资源
很多LIMS的JDBC封装(比如sapphire的DataSet)不会自动释放Statement/ResultSet资源,前一个查询的资源残留会占用连接状态,导致后续查询异常。在包裹检查查询执行完后,强制关闭DataSet和底层资源:
// 包裹检查查询 def packageCheckDs = sql.executeQuery(packageCheckSql) try { // 处理包裹检查逻辑 if (packageCheckDs.next()) { // 包裹已存在的处理逻辑 } else { // 创建新包裹的逻辑 } } finally { // 显式关闭DataSet,释放底层JDBC资源 packageCheckDs.close() }
2. 为两个查询使用独立数据库连接
部分LIMS的Groovy执行环境会复用连接池中的连接,前一个查询的事务状态或游标位置可能干扰后续查询。可以为两个查询分别申请独立连接:
// 包裹检查用独立连接 def packageConn = sapphire.db.ConnectionManager.getConnection() try { def packageStmt = packageConn.createStatement() def packageDs = packageStmt.executeQuery(packageCheckSql) // 处理包裹检查逻辑 packageDs.close() packageStmt.close() } finally { packageConn.close() } // trackitem查询用新的独立连接 def trackitemConn = sapphire.db.ConnectionManager.getConnection() try { def trackitemStmt = trackitemConn.createStatement() def trackitemDs = trackitemStmt.executeQuery(trackitemSqlStr) if (trackitemDs.next()) { def trackitemId = trackitemDs.getLong('trackitemid') // 后续业务逻辑 } trackitemDs.close() trackitemStmt.close() } finally { trackitemConn.close() }
3. 检查事务隔离与自动提交设置
如果包裹检查逻辑包含写操作(比如插入新包裹),未提交的事务会导致后续查询在同一个事务中看不到预期数据。确保包裹检查完成后显式提交事务:
def conn = sql.getConnection() conn.setAutoCommit(false) try { // 包裹检查与可能的插入操作 def packageCheckDs = sql.executeQuery(packageCheckSql) if (!packageCheckDs.next()) { sql.executeInsert(packageInsertSql) } conn.commit() // 显式提交事务 } catch (Exception e) { conn.rollback() throw e } finally { conn.setAutoCommit(true) // 恢复默认自动提交 conn.close() }
4. 验证实际执行的SQL语句
虽然你确认两个查询无共享变量,但Groovy的变量作用域或隐式参数传递可能导致实际执行的SQL和SSMS测试的不一致。在执行trackitem查询前输出完整SQL:
println "实际执行的trackitem查询SQL: ${trackitemSqlStr}" def trackitemDs = sql.executeQuery(trackitemSqlStr)
把输出的SQL复制到SSMS中执行,确认和手动测试的语句完全一致。
5. 调整DataSet的遍历方式
部分sapphire.util.DataSet实现需要调用first()来定位游标,而非next(),可以尝试调整遍历逻辑:
def trackitemDs = sql.executeQuery(trackitemSqlStr) if (trackitemDs.first()) { // 用first()确保游标定位到第一条数据 def trackitemId = trackitemDs.getLong('trackitemid') }
内容的提问来源于stack exchange,提问作者Corvus
相关产品推荐
相关产品推荐

