IBM MB Esql coding 心得

 

这篇是纯粹的“coding心得”,撇开MB那些啰嗦的配置不谈,专门讲学习ESQL的痛苦经历,有些内容可能前面的笔记有介绍过,这里做一个全面的汇总。虽然有些编程的tips已经忘记了,以后如果想起来还会继续补充。

概述
ESQL的语法和数据库的存储过程语句很像,虽然我从未写过存储过程,但是平心而论,ESQL的基本语法和概念还是很好理解的,毕竟,ESQL没有类、对象、多态这些OOP的东西,也没有指针、位移操作这些C的概念;没有C的函数指针、指针的指针、内存分配这种让新手头晕的术语,也不像Java那样各类框架满天飞,开足马力都学不过来。所以,ESQL还是很好入门的。但是,切记,只是“入门”而已。你看懂那些示例的ESQL很容易,无非是逻辑树的增删改;消息流也是那么一目了然,消息从一个节点出来,进入另一个节点,不知不觉一个“业务流”就完成了,so simple,naïve!我一开始也是这么觉得的,但真正动手的时候,才发觉ESQL代码中,危机四伏!下面一一列举

基本类型
数字
ESQL的基本类型很少,无非是数字、字符串、逻辑、时间,还有引用。数字类型包括int、float和decimal(就是double,高精度小数),一般很少会考虑三者的差别,把它们与java的等同起来,其实不然,如果随便乱用,会冒出很多无聊而又浪费时间的bug

数据库查询
在使用oracle的时候,通常都会用Number类型作为主键id等数字类型字段,可是你知道用select语句取Number到ESQL中是什么吗?是decimal。由于ESQL里面,消息树中的字段类型是隐式的、可变的(类似PHP),也就是你可以随便赋任何值给某个消息节点。按理说这种脚本语言的特性可以方便编程,是好事。不过请先看完下面的描述。

数据库插入
这个问题是最近才发现的,在64位的linux上,MB使用64位数据源访问oracle,在一条insert语句上屡屡失败,而这条insert语句之前在32位windows平台上却很正常的执行。抛出的异常提示:“oracle:String data, right truncated”,在网上搜了一下大部分人都说是数据太长,只有dw论坛上有人说可能是64bit数据源的关系,但具体原因也不清楚。请了IBM的支持来搞了半天也没任何结果,绝望之际我干脆用排除法,每次修改一两个字段为很简单的常数(那样总不会出问题了),在排除到最后一个字段时,才发现把一个decimal的数据插进去会有问题,如果换成float就ok了!这个问题前后浪费了我两天,当时忍不住说了几句脏话,一个简单的问题搞得这么恶心,错误提示也纯粹误导用户。天知道以后换成其他平台会不会又这样呢?

函数调用
ESQL是弱类型的?是的,某种程度上是弱类型的,可是遇到函数调用的时候,它的类型强度简直胜过java——你不能把一个int作为参数传递给声明为double的参数,那样会抛出异常。问题是有时候你根本不知道某个消息树节点啥时变成了int或者是float或是double,就像上文说到的数据库查询结果。请打开debug,慢慢找吧,总会有柳暗花明的一刻

字符串类型
估计MB为了照顾比较“原始”的消息格式,即非XML、而是CWF或者TDS格式的消息,ESQL的字符串类型不仅仅有字符类型“char”,还有字节类型“BLOB”和比特类型“BIT”,的确是大大方便了比特流的处理。关于字符串,发现的问题不多,下面列出几点需要稍加注意之处。

字符串连接“||”
使用“||”连接字符串时,记得每个被连接的变量都不能为null,否则连接后的结果就变成null了,可以用“变量 is not null”来判断

BLOB类型
BLOB类型在ESQL中的表示方式为 X'ABCD',实际上是一种BCD码,每个字符表示一个十六进制,即半个字节。注意这里要是偶数个长度,否则在打包或者部署时会报错

时间类型
和一般编程语言不同的是ESQL有时间类型的变量,这很大程度上是因为MB里面支持很多XML的特性,而时间类型也是标准XML中包含的,例如xsd:dateTime等等。我没学过高级的XML,自然对这些名目繁多的时间类型之间的差别不甚了解,所以简单地列出几点比较常用的特性:

消息定义中的时间类型
消息定义文件里头也可以声明某个节点类型为timestamp(消息定义文件本质上就是XML SCHEMA文件,自然支持xsd:命名空间),不过在实践中发现从MQ读入的一个XML字符串,解析为timestamp类型时,经常有一些格式上的问题而导致解析失败,后来干脆把timestamp全部替换为string了,IBM的技术支持也是这么“推荐”的,估计是他们也找不出原因所在

计算花费的时间
JAVA里面很常用的就是通过计算系统时间之差,来得到一段代码的运行时间毫秒数,然后用时间类可以进行格式转换,ESQL同样可以做到,而且更加简单。比如要计算一个消息流的运行时间,那么在消息流开头用 CURRENT_TIME得到起始时间,保存在环境树中的T1节点;然后在消息流结尾,再次用 CURRENT_TIME得到结束时间T2,两个时间值相减,再用下面这段代码将其转换成毫秒数

SET OutputRoot.MRM.process_time = CAST( (T2-Environment.Variables.T1) SECOND AS FLOAT) *1000;

很遗憾的,ESQL最小只支持“秒”的时间间隔(“时间间隔”INTERVAL也是一种时间类型),不过得到的float值通常是小数位很长的,包含了毫秒信息,譬如0.2352436,因此乘以1000也完全够用

全局变量
问过IBM的人好几次,自己也去查了不少资料,一直没有发现ESQL中有足够理想的全局变量或者全局常量。我们知道,ESQL的代码层次从高到低依次是:schema->module->function or procedure,越是局部的变量,优先级越高,这一点和普通编程语言一样。所以,没有变量的声明可以超越schema这一级,包括所谓的外部变量external,因此,在不同schema的消息流之间不能共享全局变量的,这个限制有时候很麻烦,比如所有消息流都要访问同一个数据源、或者Oracle的schema,或者是你自己定义的全局常量,那你就只能在每个schema中设置了,还好schema不会很多,或者你在消息流开始的时候,从文件、数据库等地方读取配置参数也是一种选择。

对于定义好的全局变量,可以用{ }进行变量值的替换,从而实现动态功能,比如 OutputBody.{myvar}.value,花括号的值会在运行期间指定。但是这样一来ESQL的编辑器就无法判断这个节点是否存在,会给出“无法解析该引用”的警告,这不影响使用。

全局函数
刚才说到的在schema作用域中定义的全局变量,在其他schema不能引用。但是在schema中定义的全局函数或者过程,则可以在其他schema中引用,只要在定义schema时使用PATH将其他schema导入即可,或者在调用函数时指定完整路径,如 CALL com.xxx.GLOBAL_FUNCTION。很多人一开始会对全局函数寄予厚望,因为可以减少代码的重复,增加复用度,实则符合圣人们的教诲。只可惜呢,全局函数中不能使用通常在节点中的Root、ExceptionList、Environment等逻辑树,所以我在定义全局函数时,第一个参数通常都是REFERENCE类型,用来把Environment等逻辑树传进去。

数组、ROW和LIST
ESQL里面有数组类型,但你不能DECLARE一个数组变量;有ROW类型,同时有个ROW函数专门用来产生一个ROW变量,还有个LIST函数用来产生数组。

先谈谈数组,数组是什么大家都知道了,在ESQL里面,由于不能声明一个数组,所以我习惯把数组保存在Environment或者LocalEnvironment中。因为消息树的每个节点都可以往下增加子结点,所以每个消息树的中间节点其实都是数组,我们说 set LocalEnvironment.Variables.temp = xxx ,实际上就是 set LocalEnvironment.Variables.temp[1] = xxx

至于ROW类型,《精通WMB》上说是个XML单行数据,执行这条ESQL语句:set LocalEnvironment.root = ROW('aa' AS a, 'bb' AS b),(注意root旁边没有方括号[ ])生成的XML片段如下:

<root>

<a>aa</a>

<b>bb</b>

</root>

然而,ROW其实是对应与数据库的一行数据,仅此而已。上面的root节点相当于一行,a和b则是该行数据的字段,就这么简单。所以,执行select语句时,返回的就是一个ROW的数组

LIST函数是产生数组用的,例如:set LocalEnvironment.Var.root[ ] =LIST( 'aa', 'bb'),(root旁边这次有方括号[])产生如下XML:


<Var>
<root>aa</root>
<root>bb</root>

</Var>

详细内容,请参考老陈的《精通WMB》后面的附录P321(我也是最近才无意中翻到ROW和LIST)

 

暂时想到这些,还有很多的,有空再补充

 

 

你可能感兴趣的:(oracle,数据库,xml,schema,IBM,float)