本節的要點是:
- 根據模型動態生成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語句拼接完成,查詢結構後,如何顯示,下一章就總結這個。