olap分析平台的设计与实现(十二)- 从后端模型到前端表格模型(上)

本节的要点是:

  1. 根据模型动态生成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。

在进一步分析实现方法前,先回顾一下第一小节的相关内容:

  1. CommObject
  2. CommForm
  3. FormShadow
  4. Layout
  5. 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语句拼接完成,查询结构后,如何显示,下一章就总结这个。

                                     

                

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值