有了这功能,报表跨库移植就轻松了

报表跨库移植就是基于 A 库做的报表,可以迁移到 B 库上继续用,比如行业软件公司的解决方案会面对不同的用户,开发的报表也要适应不同的用户,而不同用户的数据库可能不同,这就需要报表能够在不同数据库上移植

跨库移植的困难之处

这个需求听起来简单,但做起来却一点都不轻松,因为各数据库的 SQL 语句是不通用的,

我们用一个报表作为示例来看不轻松在哪
下面的报表,统计自起始日起,各个员工一个月内的订单情况

..

原数据库是 MYSQL 的,取数 SQL 是这样的

SELECT
	CAST(d.订单ID AS CHAR) 订单编号,	d.货主地区,	d.订单金额,	d.货主名称,
	CONCAT( e.姓氏, e.名字 ) 姓名,	d.订购日期,	d.到货日期,	c.公司名称 
FROM
	订单 d,雇员 e,客户表 c 
WHERE
	d.雇员ID = e.雇员ID 
	AND d.客户ID = c.客户ID 
	AND (
	d.订购日期 BETWEEN DATE_FORMAT( '2012-08-01', '%Y-%m-%d' ) 
	AND DATE_FORMAT( '2012-08-01', '%Y-%m-%d' )+ INTERVAL 1 MONTH)

现在要迁移到数据结构相同的 ORACLE 库上,我们就会发现,这个 SQL,不能用了,因为不同的数据库函数有差异,并不完全兼容,ORACLE 识别不了 MYSQL 的函数

ORACLE 对应的 SQL 应该是这样的

SELECT	
        TO_CHAR ( d.订单ID ) 订单编号,d.货主地区,	d.订单金额,	d.货主名称,
	CONCAT( e.姓氏, e.名字 ) 姓名,d.订购日期,	d.到货日期,	c.公司名称 
FROM
	订单 d,雇员 e,客户表 c 
WHERE
	d.雇员ID = e.雇员ID 
	AND d.客户ID = c.客户ID 
	AND (
	d.订购日期 BETWEEN TO_DATE ( '2012-08-01', 'YYYY-MM-DD' ) 
	AND TO_DATE ( '2012-08-01', 'YYYY-MM-DD' )+ NUMTOYMINTERVAL ( 1, 'MONTH' ))

这两句 SQL 的不同之处有
1. 数值类型的订单 ID 转为字符串,转换函数也不同
2. 两个库对字符串转日期的转换函数不同
3. 两个库对计算给定日期后增加一个月的日期计算也不同

写不出通用的 SQL,那就只能是写两个不同的 SQL,做两个不同的报表了,如果只有一张表需要这样做两次倒也无所谓,但项目上动辄都是几百张报表,如果都做两次,就一点都不轻松了,额外的人工成本就会成为一个承受不了的负担,而且相关的维护管理也非常麻烦

但是,有了润乾报表这个功能,报表跨库移植就轻松了

润乾报表有一个SPL 数据准备层,它有个 sqltranslate 方法,做报表的时候,我们只需要写一个通用的 SQL,sqltranslate 就可以将通用的 SQL 翻译成适配不同数据库的 SQL,报表就可以随便切换到任意数据库了

润乾跨库移植的实现原理

所有报表的 SQL 都用通用标准 SQL 来写
名词解释:通用标准 SQL,是 SPL 中的一个定义,它和普通的 SQL 写法类似(后面实现过程里有示例),只是要遵守 SPL 的规范,这样 SPL 的翻译函数,才能识别,然后翻译成各个数据库的语言

然后报表中使用统一的 SPL 脚本数据集,脚本中可以接受项目的数据源信息,要转换成数据库的类型,以及各报表的标准 SQL
然后再把报表的标准 SQL,用 sqltranslate 方法,转换成项目对应的数据库 SQL,再到数据库中取数,传给报表
这样,所有报表,就可以适应多种数据库了

实现过程

下面我们用一张报表看一下具体的操作过程

一:新建报表,定义脚本数据集

新建报表,并增加数据集,数据集为脚本数据集

..
编辑脚本如下,该脚本是通用的,所有报表都可以用这个脚本当数据集,直接复制就可以

A B
1 >dbName=file(“dbsconfig.properties”).property(“dbName”) / 读取配置文件中的数据源名称
2 >dbType=file(“dbsconfig.properties”).property(“dbType”) / 读取配置文件中的数据库类型
3 >sql=sql.sqltranslate(dbType) / 根据 dbType 参数的数据库类型,将 sql 转化为对应库标准 SQL 语句
4 >output(“dbType==”+dbType+“,sql===”+sql) / 输出转换后的 sql
5 >querySql=“sql,”+params / 拼接 query 函数中执行的参数,拼接结果为 sql, 参数 1, 参数 2 这种格式
6 =connect(dbName) / 连接数据源
7 =A6.query@x(${querySql}) / 执行 sql 语句,这里使用宏替换方式
8 return A7 / 将结果返回给报表数据集

二:制作报表,设置参数

制作报表

..
并设置参数

..

bDate:查询日期,为本示例报表自己的参数

sql:每张报表自己的通用标准 SQL 语句,也就是每个报表的写好的通用标准 SQL 都放到自己的参数编辑里

SELECT
	NUMTOCHAR ( d.订单 ID ) 订单编号,	d.货主地区,	d.订单金额,	d.货主名称,
	CONCAT( e.姓氏, e.名字 ) 姓名,	d.订购日期,	d.到货日期,	c.公司名称 
FROM
	订单 d,雇员 e,客户表 c 
WHERE
	d.雇员 ID = e.雇员 ID 
	AND d.客户 ID = c.客户 ID 
	AND (	d.订购日期 BETWEEN DATETOCHAR (?) 
	AND ADDMONTHS ( DATETOCHAR (?), 1 ))

params: 参数串,值和 sql 中的问号相匹配,比如上面标准 SQL 中,两个问号代表两个参数,这里就对应写两个参数 bDate,bDate
这个 params 是所有报表都需要用到的公共参数,负责把每张报表自己的参数拼成串,传递给脚本

三:配置数据源相关文件

需要配置两个文件
一个是在 raqsoftConfig.xml 中配置需要连接的数据源
一个是在 dbsconfig.properties 中配置需要转换的类型

在报表 raqsoftConfig.xml 中,增加自己的数据源信息,如果有多个,就增加多个,比如下面的 db1,db2,数据源名称可以根据自己需求随意命名

<DB name="db1">
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mysql1?useCursorFetch=true"/>
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="type" value="10"/>
                <property name="user" value="runqian"/>
                <property name="password" value="runqian"/>
                <property name="batchSize" value="0"/>
                <property name="autoConnect" value="false"/>
                <property name="useSchema" value="false"/>
                <property name="addTilde" value="false"/>
                <property name="caseSentence" value="false"/>
			</DB>
<DB name="db2">
                <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"/>
                <property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
                <property name="type" value="1"/>
                <property name="user" value="C##runqian"/>
                <property name="password" value="runqian"/>
                <property name="batchSize" value="1000"/>
                <property name="autoConnect" value="false"/>
                <property name="useSchema" value="false"/>
                <property name="addTilde" value="false"/>
                <property name="caseSentence" value="false"/>
            </DB>

在 dbsconfig.properties 中配置了 dbName 和 dbType

..
dbName 值为数据源名称,要和 raqsoftConfig.xml 配置文件中的数据源名称保持一致

四:查看结果

到这里报表设计完了,相应的配置文件也都配置好了,就可以浏览报表查看结果了
..
因为配置文件目前设置的 dbtype 是 MYSQL,我们可以看到,后台日志 SQL 输出中,已经变成 MYSQL 的 SQL 了

dbType==MYSQL,sql===
SELECT CAST(d.订单ID AS CHAR) 订单编号,d.货主地区,d.订单金额,d.货主名称,     
CONCAT(e.姓氏,e.名字) 姓名,d.订购日期,d.到货日期,c.公司名称  
FROM 订单 d,雇员 e,客户表 c  
WHERE  d.雇员ID = e.雇员ID      
and d.客户ID=c.客户ID      
and (d.订购日期 between DATE_FORMAT(?,  '%Y-%m-%d %H:%i:%S')       
and DATE_FORMAT(?,  '%Y-%m-%d %H:%i:%S')+INTERVAL 1 MONTH)

如需要切换到 ORACLE,只需要在报表应用的 dbsconfig.properties 文件中,更改 dbName 的值为 bd2,dbType 值为 ORACLE,访问报表,SQL 就变成 ORACLE 的了,就直接从 ORACLE 中取数了,后台日志输出为:

dbType==ORACLE,sql===
SELECT TO_CHAR(d.订单ID) 订单编号,d.货主地区,d.订单金额,d.货主名称,     
e.姓氏||e.名字 姓名,d.订购日期,d.到货日期,c.公司名称  
FROM 订单 d,雇员 e,客户表 c  
WHERE  d.雇员ID = e.雇员ID      
and d.客户ID=c.客户ID      
and (d.订购日期 between TO_CHAR(?,'YYYY-MM-DD HH:MI:SS')       
and TO_CHAR(?,'YYYY-MM-DD HH:MI:SS')+NUMTOYMINTERVAL(1,'MONTH'))

五:动态多库切换

前面做的是,项目整体移植到其他数据库的场景需求,只需要修改下配置文件就可以做到

还有一种特殊的场景是,一个项目下会有多个数据库,不同报表会动态切换不同的数据库,这个需求也好解决
可以在 dbsconfig.properties 配置文件中同时配置多个 type 类型,报表中增加一个 dbname 参数,访问报表时可以根据参数值查找对应的数据库类型,实现数据源的动态切换

具体做法如下:

修改配置文件,报表参数和脚本数据集:

..

配置文件中第一列是数据源名称,对应的值为数据库类型

..

参数中增加 dbName 参数,可接收外部传入参数,此处传入数据源名称
脚本数据集:

A B
1 =connect(dbName) / 根据传入参数连接数据源
2 >dbType=file(“dbsconfig.properties”).property(dbName) / 根据 dbName 读取配置文件中的数据库类型
3 >sql=sql.sqltranslate(dbType) / 根据 dbType 参数的数据库类型,将 sql 转化为对应库标准 SQL 语句
4 >output(“dbType==”+dbType+“,sql===”+sql) / 输出转换后的 sql
5 >querySql=“sql,”+params / 拼接 query 函数中执行的参数,拼接结果为 sql, 参数 1, 参数 2 这种格式
6 =A1.query@x(${querySql}) / 执行 sql 语句,这里使用宏替换方式
7 return A6 / 将结果返回给报表数据集

访问报表时给 dbName 传入对应参数就可以了,这样报表就可以在不同数据库间随便切换,移植了

到这里,跨库移植就做完了,原本每个报表都得做两次,或者做多次,现在都只需要做一次就可以了,一个脚本数据集就自动把报表里的普通标准 SQL 翻译成不同数据库的专有 SQL 了,原本让人头疼的报表跨库移植问题,就被润乾报表 SPL 脚本数据集功能轻松解决了