本节重点总结olap4j开发
根据上一章节的schemal,我们用olap4j方式实现查询。
package com.muge.as.dao;
import mondrian.olap.Util;
import org.olap4j.Axis;
import org.olap4j.CellSet;
import org.olap4j.OlapConnection;
import org.olap4j.layout.CellSetFormatter;
import org.olap4j.layout.RectangularCellSetFormatter;
import org.olap4j.mdx.IdentifierNode;
import org.olap4j.metadata.Cube;
import org.olap4j.metadata.Member;
import org.olap4j.metadata.NamedList;
import org.olap4j.query.Query;
import org.olap4j.query.QueryDimension;
import org.olap4j.query.Selection;
import java.io.InputStream;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
//import mondrian.olap.Connection;
/**
* @author xj
*/
public class TestFSCK_olap4j_m4 {
private static String driver = "oracle.jdbc.driver.OracleDriver";
private static String url="jdbc:oracle:thin:@127.0.0.1:1521:ORCL";
private static String userName = "superVision";
private static String password = "1";
// 立方体定义文件
//private static String xmlFile = "sample.xml";
//private static String xmlFile = "./foodmart_fd/olapSchema2.xml";
private static String xmlFile = "FSCK.xml";
public static void main(String[] args) throws Exception {
// olap4j的mdx接口,是一个jdbc实现,推荐
System.out.println("\r\n===================olap4j接口==================");
//String mdxStr = "select {[Year].Members} on columns,{[Entity].Members} on rows from 模型一";
// 建立连接
String strUrl = "jdbc:mondrian:";
strUrl += "Jdbc=" + url;
strUrl += ";JdbcUser=" + userName;
strUrl += ";JdbcPassword=" + password;
// strUrl += ";Catalog=" + xmlFile;
strUrl += ";CatalogContent=" + getCatalogContent();
Class.forName("mondrian.olap4j.MondrianOlap4jDriver");
Connection olap4jConn = DriverManager.getConnection(strUrl);
OlapConnection olapConn = (OlapConnection) olap4jConn.unwrap(OlapConnection.class);
NamedList<Cube> cubes =
olapConn.getOlapSchema().getCubes();
Cube cube = cubes.get("Col_trade_detail");
Query myQuery =
new Query("model1Query", cube);
myQuery.execute();
System.out.println("------------myQuery------------:"+myQuery);
QueryDimension year = myQuery.getDimension("Year");
QueryDimension account = myQuery.getDimension("Account");
myQuery.getAxis(Axis.COLUMNS).addDimension(year);
myQuery.getAxis(Axis.ROWS).addDimension(account);
Member year2010 =
cube.lookupMember(
IdentifierNode.ofNames ("Year", "2010年")
.getSegmentList());
year.include(year2010);
account.include(
Selection.Operator.CHILDREN,
IdentifierNode.ofNames("Account", "其他收入", "其他收入")
.getSegmentList());
myQuery.validate();
System.out.println(
myQuery.getSelect().toString());
String s=myQuery.getSelect().toString();
System.out.println("s:"+s);
CellSet cellSet = myQuery.execute();
// 输出结果
CellSetFormatter formatter = new RectangularCellSetFormatter(false);
formatter.format(cellSet, new PrintWriter(System.out, true));
}
/**
* 初始化数据库,建表
*
* @throws Exception
*/
private static void initDB() throws Exception {}
private static String getCatalogContent() throws Exception {
InputStream inputStream =null;
try {
inputStream= Util.readVirtualFile(xmlFile);
final byte[] bytes = Util.readFully(inputStream, 1024);
String s=new String(bytes, "UTF-8");
return s;
}catch(Exception e){
e.printStackTrace();
return "";
}
finally {
if (inputStream != null) {
inputStream.close();
}
}
}
}
结果如下2段内容:
s:SELECT
{[Year].[YearLy].[2010年]} ON COLUMNS,
{[Account].[Account_L].[其他收入].[其他收入].Children} ON ROWS
FROM [Col_trade_detail]
生成的规则是:维度-->Hierarchy name='Account_L'---><Level attribute="Level1projectname"/>的元素-->2的元素
| | 2010年 |
+------+------+----------------+------------+
| 其他收入 | 其他收入 | #null | |
| | | 办公楼处置 | |
| | | 产权交易佣金收入 | |
| | | 场租收入 | |
| | | 城市基础设施配套手续费 | |
| | | 出租广告位 | |
| | | 档案整理综合服务费 | |
| | | 房改办证工本费 | |
| | | 房租收入 | **.66 |
| | | 附属单位缴款 | |
| | | 工程安全生产保证金 | 69**00. |
| | | 公安保证金 | 5*0. |
| | | 挂号收入 | |
看到这个数字,我想探索一下非税收入中历年(09年-17年)房租变化,动手修改语句:
year.include(year2009);
year.include(year2010);
year.include(year2011);
year.include(year2012);
year.include(year2013);
year.include(year2014);
year.include(year2015);
year.include(year2016);
year.include(year2017);
account.include(
Selection.Operator.CHILDREN,
IdentifierNode.ofNames("Account", "其他收入", "其他收入","房租收入")
.getSegmentList());
自动生成如下mdx:
SELECT
{[Year].[YearLy].[2009年], [Year].[YearLy].[2010年], [Year].[YearLy].[2011年], [Year].[YearLy].[2012年], [Year].[YearLy].[2013年], [Year].[YearLy].[2014年], [Year].[YearLy].[2015年], [Year].[YearLy].[2016年], [Year].[YearLy].[2017年]} ON COLUMNS,
{[Account].[Account_L].[其他收入].[其他收入].[房租收入].Children} ON ROWS
FROM [Col_trade_detail]
执行结果如下:
从结果看,该县9年间,政府房租收入涨了9倍多。
上面生成的mdx语句中:[Account].[Account_L].[其他收入].[其他收入].[房租收入].Children
因此 结果中有个#null,如何去掉这个,还是个问题。
有包括必然有排除某个维度成员的方法,如下例排除垃圾清运费:
account.exclude(IdentifierNode.ofNames("Account", "其他收入", "其他收入","垃圾清运费" )
.getSegmentList());
生成的mdx如下:
SELECT
{[Year].[YearLy].[2009年], [Year].[YearLy].[2010年], [Year].[YearLy].[2011年], [Year].[YearLy].[2012年], [Year].[YearLy].[2013年], [Year].[YearLy].[2014年], [Year].[YearLy].[2015年], [Year].[YearLy].[2016年], [Year].[YearLy].[2017年]} ON COLUMNS,
{Except({[Account].[Account_L].[其他收入].[其他收入].Children}, {[Account].[Account_L].[其他收入].[其他收入].[垃圾清运费]})} ON ROWS
代码中,年度添加的方式有点丑陋,后面再说。
遍历查询结果:关键词:Positions & Axis:
a cell is found by specifying a tuple of positions along every axis
ps:a tuple of 元组
沿着轴(axis)指定元组的位置,就可以找到对应的单元格。
上代码:
for (Position axis_0 :
cellSet.getAxes().get(Axis.ROWS.axisOrdinal()).getPositions()) {
for (Position axis_1 :
cellSet.getAxes().get(Axis.COLUMNS.axisOrdinal()).getPositions()) {
Cell currentCell = cellSet.getCell(axis_0, axis_1);
Object value = currentCell.getValue();
}
}
关键是Position 对象及Axis对象。
后面,我打算自己开发展示层,会用到这里的位置及结果。
Sorting axis :
可以根据度量轴排序:
这个比较简单,官文中有例子,看一下就明白
(myQuery .getAxis(Axis.ROWS) .sort(SortOrder.DESC); )。
SortOrder.DESC、SortOrder.BDESC
这里就不展开。
olap4j对象:
解析与校验:
要保证mdx执行正确,最好先进行必要的解析与校验,parser用于分析语法,校验用于验证数据?
Parsing a query with the MdxParser:
1: MdxParserFactory pFactory = olapConnection.getParserFactory()
2: MdxParser parser = pFactory.createMdxParser(olapConnection);
3: try {
4: SelectNode parsedObject = parser.parseSelect("SELECT {} on Bacon FROM [MyCube]");
5: } catch (OlapException e) {
6: System.out.println(e.getMessage());
7: }
MdxValidator 校验:
1: MdxParserFactory pFactory = olapConnection.getParserFactory();
2: MdxParser parser = pFactory.createMdxParser(olapConnection);
3: SelectNode parsedObject = parser.parseSelect("SELECT {} on
Bacon FROM [Sales]");
4: MdxValidator validator =
pFactory.createMdxValidator(olapConnection);
5: try {
6: validator.validateSelect(parsedObject);
7: } catch (OlapException e) {
8: System.out.println(e.getMessage());
9: }
纯mdx执行:
OlapConnection olapConn = (OlapConnection) olap4jConn.unwrap(OlapConnection.class);
// 执行查询
OlapStatement statement = olapConn.createStatement();
CellSet cellSet = statement.executeOlapQuery(mdxStr);
org.olap4j.mdx.SelectNode ---MDX Object model
可以用这个动态生成mdx:
MdxParserFactory pFactory = olapConn.getParserFactory();
MdxParser parser = pFactory.createMdxParser(olapConn);
SelectNode selectNode = parser.parseSelect("SELECT {} ON ROWS FROM [Sales]");
selectNode
.getAxisList()
.get(Axis.COLUMNS.axisOrdinal())
.setExpression(
IdentifierNode.parseIdentifier("[Product]"));
System.out.println(
selectNode.toString());
System.out.println("===============================================");
控制台输出:
SELECT
[Product] ON ROWS
FROM [Sales]
===============================================
IdentifierNode.parseIdentifier("[Product]"))放的是字符串,无法判断类型,为此,olap4j提供了CallNode对象。具体看附后的参考资料文档。
Scenarios, writeback and statistical simulations
olap4j提供了回写功能,这个回写会写入数据库吗,我没有研究,但mondrian4是支持回写的,我见过有公司通过回写编制全面预算的功能。我这里只是对财政非税数据进行分析,回写可以用于编制计划,用计划控制业务?
ps:参考资料:
Olap4j Introduction - An end-user perspective