有了这功能,报表跨库移植就轻松了
报表跨库移植就是基于 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 脚本数据集功能轻松解决了