本节的要点是:
- 根据模型动态生成mdx语句
打开表单进行数据展示的逻辑:
判断分析表单是否包含所有维度
是否是大数据表单
如果不是大数据表单,就全表打开。
查找业务规则、在界面上加载业务规则 runBusinessRule(formId,loadType,isFullMode,hasRule);
在界面上加载表单。OlapManagement?method=olapShowData&formId="+formId
是否需要全新生成,(完全没有打开的表要全新生成)
查找【期间】可分摊维度在行或列上,(如果不在行列上使用行上第一个维度进行分摊)
是否是只读表单
生成formGrid:(这个方法,可以理解成由formshadow 转换为 formGrid的方法,由后端模型转换为前端模型)
queryFormGridByFormId(form, isFullMode, formShadow, userId,pageMembers);
调用showData:()
...
//根据formid 获取CForm对象
CForm form = getBgFormById(formId);
...
/**根据form对象,构建FormShadow ifFilter--是否根据权限过滤数据
FormShadows表单映射对象,formShadow可以看成前端模型
**/
FormShadow formShadow = new FormShadow(form, userid, ifFilter);
//检查过滤后是否拥有可见维度成员(行、列、页面、视点),如果有,才能走一步
boolean ifHasMember = checkHasMembersOnRowCOl(formShadow.getColMembers());
boolean ifHasMember2 = checkHasMembersOnRowCOl(formShadow.getRowMembers());
checkHasMembersOnPageView(formShadow.getPageMembers(),pageMemberList) //检查页面上是否有成员
checkHasMembersOnPageView(formShadow.getViewMembers(), viewMemberList 检查视点上是否有成员
//获取前端的formgrid
FormGrid formGrid0 = (FormGrid) request.getAttribute("formGrid");
....
//构建formGrid
formGrid = queryFormGridByFormId(form,isFullMode, formShadow, userId,pageMembers);
......
showData(defaultAliasTable,showMap, html, formGrid, formShadow,request.getContextPath(),formReadyOnly);
...
queryFormGridByFormId接口
/**
* @param showAll (true - 以全部下钻方式打开分析表单 false - 以全部上卷方式打开成本表)
* 通过 ID查询分析表单,返回FormGrid类实例
* @param pageLayout 页面轴上选定的维度成员 --批量导出分析表单数据时用到此参数
*/
public FormGrid queryFormGridByFormId(Form form, boolean showAll,FormShadow formShadow,Long userId, int[] pageLayout);
showData接口:
/**
* 显示表单数据
* @param map 参数
* @param html
* @param formGrid
* @param form
* @param rootPath
* @param formReadyOnly
*/
public void showData(String defaultAliasTable, Map<String, Object> map, StringBuffer html, FormGrid formGrid,FormShadow form,String rootPath,boolean formReadyOnly);
FormShadow 表单映射对象:
FormShadow 继承自form, 主要是扩展了各个维度的详细信息,第一小节里,用的是formModel这个word,描述它。
FormGrid 是个网格对象。第一小节里,用的是viewGrid这个word,描述它。一个是与界面的分析表格视图(view)对应的类:viewGrid。
在进一步分析实现方法前,先回顾一下第一小节的相关内容:
- CommObject
- CommForm
- FormShadow
- Layout
- FormGrid
CommObject 是个通用类,把所有对象共有的属性放到这个类当中:
/**
* 所有实体类的父类
*/
public class CommObject {
private int id;
private String name;
private int objType;
private int parent;
private int generation;
private int hasChildren;
private int description;
private int queryType; //创建及更改form表单时表示member的查询函数类型,只在各种维度成员对象中才有意义
private int formulaType;
private float position; //排序key
......
public boolean equals(CommObject obj) {
if(obj!=null && this.getId()==obj.getId()){
return true;
}
return false;
}
}
服务端模型CommForm & FormShadow:
public class CommForm extends CommObject {
private int cubeId;
private int scale = 7;
......
public CommForm() {
setObjType(CommConstants.BG_OBJECT_FORM);
setParent(CommConstants.BG_OBJECT_ROOT);
}
/**
* 返回列维度对象
* @return
*/
public List<CommDimension> getColDims() {
return getFormDimsByLayoutType(this,CommConstants.LAYOUT_COL);
}
/**
* 返回行维度对象
* @return
*/
public List<CommDimension> getRowDims() {
return ...
}
/**
* 返回页面维度对象
* @return
*/
public List<CommDimension> getPageDims() {
return getFormDimsByLayoutType(this,CommConstants.LAYOUT_PAGE);
}
/**
* 返回视点维度对象
* @return
*/
public List<CommDimension> getViewDims() {
......
}
/**
* 返回列维度成员对象的集合(展开查询函数)
* @return
*/
public List<List<List<DimMember>>> getColMembers() {
List<List<List<DimMember>>> result = getFormMbrsAsLayoutGroup(this,
CommConstants.LAYOUT_COL, true);
return result;
}
/**
* 返回列维度成员对象的集合(不展开查询函数)
* @return
*/
public List<List<List<DimMember>>> getColMembersWithQueryType() {
List<List<List<DimMember>>> result = getFormMbrsAsLayoutGroup(this, CommConstants.LAYOUT_COL, false);
return result;
}
/**
* 返回行维度成员对象的集合(展开查询函数)
* @return
*/
public List<List<List<DimMember>>> getRowMembers() {......
}
/**
* 返回行维度成员对象的集合
* @return
*/
public List<List<List<DimMember>>> getRowMembersWithQueryType() {......
}
/**
* 返回页面维度成员对象的集合(展开查询函数)
* @return
*/
public List<List<DimMember>> getPageMembers() {......
}
/**
* 返回页面维度成员对象的集合
* @return
*/
public List<List<DimMember>> getPageMembersWithQueryType() {......
}
/**
* 返回视点维度成员对象的集合(展开查询函数)
* @return
*/
public List<List<DimMember>> getViewMembers() {......
}
/**
* 返回视点维度成员对象的集合
* @return
*/
public List<List<DimMember>> getViewMembersWithQueryType() {......
}
/**
* 返回此Form布局的列数
* @return
*/
public int getRowCount() {
return BusinessMethods.getRowColCount(getRowMembers());
}
/**
* 返回此CommForm布局的行数
* @return
*/
public int getColCount() {
return BusinessMethods.getRowColCount(getColMembers());
}
......
}
/**
* 分析表单映射对象
*
*/
public class FormShadow extends CommForm {
private List<IcpDimension> colDims = null;
private List<IcpDimension> rowDims = null;
private List<IcpDimension> pageDims = null;
private List<IcpDimension> viewDims = null;
private List<List<List<DimMember>>> colMembers = null;
private List<List<List<DimMember>>> rowMembers = null;
private List<List<DimMember>> pageMembers = null;
private List<List<DimMember>> viewMembers = null;
/**
* 非明细成员
*/
private Map<Integer,Integer> noDetailMap=new HashMap<Integer, Integer>();
/**
* @param commForm
* @param userId 用户标识
* @param ifFilter 是否过滤
*/
public FormShadow(CommForm commForm,long userId,boolean ifFilter) {
colDims = commForm.getColDims();
rowDims = commForm.getRowDims();
pageDims = commForm.getPageDims();
viewDims = commForm.getViewDims();
colMembers = commForm.getColMembers();
rowMembers = commForm.getRowMembers();
pageMembers = commForm.getPageMembers();
viewMembers = commForm.getViewMembers();
colGroupReadonly = commForm.getColGroupReadonly();
rowGroupReadonly = commForm.getRowGroupReadonly();
this.setId(commForm.getId());
this.setName(commForm.getName());
this.setObjType(commForm.getObjType());
this.setDescription(commForm.getDescription());
this.setCubeId(commForm.getCubeId());
this.setFormFolder(commForm.getFormFolder());
this.setFormShuoMingId(commForm.getFormShuoMingId());
this.setScale(commForm.getScale());
this.setLastModifyTime(commForm.getLastModifyTime());
this.setApproval(commForm.getApproval());
......
}
......
}
前端表格模型FormGrid(viewGrid):
/**
* 网格对象
*/
public class FormGrid {
private int axisCount;
private int cubeId;
private int formId;
// 行列维度成员
private int[][] rowMembers = null;
private int[][] colMembers = null;
// 页面
private int[] pageLayout = null;
// 视点
private int[] vpLayout = null;
// 事实数据
private DataCell[][] factDatas = null;
//BgForm影子,供生成MDX字符串使用
private CommForm formShadow = null;
/*
* rowGroup表示列布局上的分组信息
* 列:
* rowCrgoup={0, 5, 10}表示第一组坐标0~4 ,第二组坐标5~9 ,第三组坐标10~rowGroup.lenght
*/
private int[] rowGroupInfo = null;
private int[] colGroupInfo = null;
//行维度
private int[] rowDims=null;
//列维度
private int[] colDims=null;
//单元格维度位置
private int[] cellDims=null;
......
public FormGrid(
int cubeId,
int formId,
int[][] rowMembers,
int[][] colMembers,
int[] pageLayout,
int[] vpLayout,
DataCell[][] factDatas,
CommForm formShadow) {
this.cubeId = cubeId;
this.formId = formId;
this.rowMembers = rowMembers;
this.colMembers = colMembers;
this.pageLayout = pageLayout;
this.vpLayout = vpLayout;
this.factDatas = factDatas;
this.formShadow = formShadow;
}
......
}
由form模型--->grid模型,其中有个概念:布局(layout)
这里回顾一下第一小节总结的布局的概念:
public class FormLayout implements Comparable<FormLayout>{
private int id;
private int formId; //表单
private int layoutType; //行 列 视点 页面 等
private int ordinal; //序数
private int dimId; //维度
private int layoutGroup; //组
private int readonly; //是否只读
......
public int compareTo(FormLayout o) {
int result = -1;
if (o != null) {
result = this.getLayoutType() - o.getLayoutType();
if (result == 0) {
result = this.getOrdinal() - o.getOrdinal();
}
}
return result;
}
}
表单布局:(layout):
分析表单的构建,根据的事行维、列维度、页面、视点的定义,和一个分析表单相关的行、列维度等就是表单布局。
布局定义了展示给用户看的分析表格的结构。
(表单与布局)
布局里的分组,以适应表单中对行维、列维度进行分组的需要;如果不显示对行、维度进行分组,默认只有第一组;
一组当中,需要依次定义维度;
表单布局的属性包括:布局id,分析表单id,维度id,布局组、排序、是否只读等信息。
这其中布局(layout_type)类型分为:行维(2)、列维(3)、页面(1)、视点(0);
例如,我们需要根据行政区划按月度分析全省交罚收入情况,可以设置如下的行、列、页面、视点。
行维:行政区划(各地财政局)
列维:我们用期间作为维度;
页面:我们采用版本、与年度。
视点:交罚项目(科目)。
布局相关数据库表如下图:
(布局的数据库表)
分析表单维度成员:
维度成员有在分析表单中有一定顺序,分析表单维度成员必然在一个布局当中。
下面是几个重点方法之一:
queryFormGridByFormId:
/**
* 以全部下钻方式打开分析表
*通过Form ID查表单,返回FormGrid类实例
*/
public FormGrid queryFormGridByFormId(CommForm bgForm, boolean showAll,FormShadow formShadow,Long userId,int[] pageLayout) {
FormGrid formGrid = null;
try{
clearOlapCache();
/**int[] pageMembers = null; // 存放页面成员id array,这里pageLayout 就是pageMembers?
/ps:第一层key是uerid;第二层key 是formId 后面放的是布局id数组
public Map<Long,Map<Integer,int[]>> memoryPageMap=new HashMap<Long, Map<Integer,int[]>>
**/
if(pageLayout==null && memoryPageMap.containsKey(userId)){
获取全部pageLayout id//
pageLayout=memoryPageMap.get(userId).get(bgForm.getId());
}
String mdx = null;
if (!showAll) { // 以全部上卷方式展现成本表
formShadow = buildAllRollUpForm(bgForm);
}
mdx = getMdxString(formShadow,pageLayout);
long start2=System.currentTimeMillis();
Result result = olapDaoImpl.queryMdx(mdx);
// 输出结果
PrintWriter pw = new PrintWriter(System.out);
result.print(pw);
pw.flush();
long end2=System.currentTimeMillis();
System.out.println("构建result耗时"+(end2-start2));
formGrid = buildFormGridByResult(result, formShadow, pageLayout, formShadow);
if(pageLayout!=null){
formGrid.setPageLayout(pageLayout);
}
BusinessMethods.fillGroupInfoInGrid(formGrid);
}catch (Exception e) {
......
}
return formGrid;
}
这个方法中:一个核心方法代码是:
mdx = getMdxString(formShadow,pageLayout); //动态构造mdx语句!
Result result = olapDaoImpl.queryMdx(mdx);
动态构造mdx方法:
/**
* 对外提供取得多维查询语句的接口
* @return 多维查询语句
*/
private String getMdxString(FormShadow formShadow,int[] pageLayout){
formShadow.getNoDetailMap().clear();
formShadow.getShareMemberMap().clear();
IcpCube cube=formShadow.getCube();
String cubeName=cube.getName();
List<List<List<DimMember>>> colMemberObjs = formShadow.getColMembers();
List<List<List<DimMember>>> rowMemberObjs = formShadow.getRowMembers();
List<List<DimMember>> pageMemberObjs = formShadow.getPageMembers();
List<List<DimMember>> viewMemberObjs = formShadow.getViewMembers();
String onCols = conMembers(colMemberObjs,formShadow);
String onRows = conMembers(rowMemberObjs,formShadow);
String onWheres = conWhereMembers(pageMemberObjs,viewMemberObjs,pageLayout,formShadow);
String mdxString = makeMdxString(cubeName,onCols,onRows,onWheres);
String withMemsStr = conWithMembers(colMemberObjs,rowMemberObjs,formShadow);
if(withMemsStr!=null){
mdxString = withMemsStr + mdxString;
mdxString=replaceTempFormulaMember(mdxString, formShadow);
}
return mdxString;
}
这个方法中,重点是根据后端模型,动态构建:
mdx语句被划分成cube, col、row、where 四个部分,且动态生成之。
col、row mdx 子部分语句的生成方法conMembers如下:
/**
* 根据行/列成员对象连接成员字符串
* @param formShadow
* @param membersList 行成员/列成员对象的集合
* @return 连接成员字符串
*/
private static String conMembers(List<List<List<DimMember>>> membersList0, FormShadow formShadow) {
StringBuffer strBuffer=new StringBuffer();
if (membersList0 != null&&membersList0.size()>0) {
strBuffer.append("{");
for (int k = 0; k < membersList0.size(); k++) { //遍历行和列上的维度
List<List<DimMember>> membersList = membersList0.get(k);
if (membersList != null&&membersList.size()>0) {
for (int i = 0; i < membersList.size(); i++) { //遍历一个维度上的分组
strBuffer.append("{");
List<DimMember> members=membersList.get(i);
for (int j = 0; j < members.size(); j++) { //遍历组成员
DimMember member=members.get(j);
CommDimension dim=member.getDimension();
String memberN=member.getNavigation(); /**负责生成类似"[Year].[YearLy].[2010年]" 语句
**{[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年]}**/
......
strBuffer.append(memberN);
if (j != members.size() - 1) {
strBuffer.append(",");
}
}
strBuffer.append("}");
if (i != membersList.size() - 1) {
strBuffer.append("*"); //这里是乘积的运算符?
}
}
}
if(k!=membersList0.size()-1){
strBuffer.append(",");
}
}
strBuffer.append("}");
}
return strBuffer.toString();
}
生成mdx条件语句部分:
/**
* 根据页面、视点成员对象连接成员字符串
* @param pageMembersList 页面成员对象的集合
* @param viewMembersList 视点成员对象的集合
* @param formShadow
*param pageLayout 参数存放的是由维度组成页面数组中,
*选中的维度成员,初始化用的是一个维度中第一个维度成员,
*如果用户有选择,就用用户的选择,用户选择在pageLayout 这个数组中
* @return 连接成员字符串
*
*/
private static String conWhereMembers(List<List<DimMember>> pageMembersList,List<List<DimMember>> viewMembersList, int[] pageLayout, FormShadow formShadow) {
StringBuffer strBuffer=new StringBuffer();
if (pageMembersList!=null&&pageMembersList.size()>0) {
strBuffer.append("(");
int dimCount=pageMembersList.size();
for (int i = 0; i < dimCount; i++) { //遍历所有页面维度 页面上可以存在多个维度
List<DimMember> members=pageMembersList.get(i);
if (members.size()>0) {
DimMember firstMember=members.get(0); //只取得第一个维度成员
CommDimension dim=firstMember.getDimension();
DimMember member = null;
String memberN=null;
if(pageLayout==null){
member=members.get(0);
dim=member.getDimension();
memberN=member.getNavigation();
strBuffer.append(member.getNavigation());
if(member.getHasChildren()==1){
//非明细成员
formShadow.getNoDetailMap().put(member.getMemberId(), member.getDimension().getId());
}else{
memberN=buildShareMember(member, dim, formShadow,memberN);
}
}else{
member = new CacheFortified().getMemberInDimByMemberId(pageLayout[i], dim.getId());
memberN=member.getNavigation();
if(member.getHasChildren()==1){
formShadow.getNoDetailMap().put(member.getMemberId(), member.getDimension().getId());
}else{
memberN=buildShareMember(member, dim, formShadow,memberN);
}
strBuffer.append(memberN);
}
}
if (i != dimCount-1||viewMembersList!=null&&viewMembersList.size()>0) {
strBuffer.append(",");
}
} //结束遍历所有页面维度
}
if (viewMembersList!=null&&viewMembersList.size()>0) {
if (strBuffer.length()==0) {
strBuffer.append("(");
}
int dimCount=viewMembersList.size();
for (int i = 0; i < dimCount; i++) {
List<DimMember> members=viewMembersList.get(i);
if (members.size()>1) {
return null;
}
if (members.size()==1) {
DimMember member=members.get(0);
String memberN=member.getNavigation();
CommDimension dim=member.getDimension();
if(member.getHasChildren()==1){
formShadow.getNoDetailMap().put(member.getMemberId(), member.getDimension().getId());
}else{
memberN=buildShareMember(member, dim, formShadow,memberN);
}
strBuffer.append(memberN);
}
if (i != dimCount-1) {
strBuffer.append(",");
}
}
}
if (strBuffer.length()>0) {
strBuffer.append(")");
}
return strBuffer.toString();
}
拼接整个mdx语句:
/**
*
* @param cubeName 立方体名
* @param onCols 列成员字符串
* @param onRows 行成员字符串
* @param onWheres 页面视点成员字符串
* @return 多维查询语句
*/
private static String makeMdxString (String cubeName, String onCols, String onRows,String onWheres){
String mdxString = null;
if (onCols!=null || onRows!=null) {
mdxString = "SELECT ";
String addStr = "{[Measures].[金额]} ON COLUMNS";
if (onCols!=null) {
addStr = (onCols + "*")+addStr;
if (onRows!=null) {
addStr += ",";
}
}
mdxString += addStr;
if (onRows!=null) {
mdxString += (onRows + " ON ROWS");
}
mdxString += (" FROM [" + cubeName + "]");
if (onWheres!=null && !onWheres.equals("")) {
mdxString += (" WHERE " + onWheres);
}
}
return mdxString;
}
mdx语句拼接完成,查询结构后,如何显示,下一章就总结这个。