畢設大部分功能已經完成,目前還在完善一些輔助功能,其中數據庫管理就是一個重點,這一主要介紹通過Ext顯示數據庫的表。
一、服務器端的實現
由於之前已經有過實現的經驗,因此服務器端的實現已經輕車熟路,下邊先介紹這一塊。
其實從面向對象的角度來說,特別是采用了SSH框架之后,我們可以直接通過hibernate獲取對應表對象的集合,然后將集合發送至前台交給Ext處理。這樣看起來挺不錯,而且實際實現也不難,但是你仔細查看就會發現個很不爽的事情,每次發送的集合包含了大量的無用數據(甚至絕大多數的數據),為什么會這樣呢?表關聯的原因。hibernate生成的model會引入關聯表的對象(或對象集合),而struts—json插件會將他們全部序列化,這在我們實際Ext的顯示中不需要的。當然,你或許可以無視,但這絕對是個糟糕的設計,你將大量的網絡流量耗費在了無效數據上。
那么怎么解決這個問題呢?有兩個方法:一是通過struts—json提供的方法(一般用注解或result配置)指定這些數據不序列化,聽着挺好,但做着費勁,你要么在model類中寫入很多和這個類本身沒有任何關系的注解,要么需要就在action的<result>標簽內對每一個字段進行指定,這個聽聽都頭疼;另外一種方法或許靠譜些,可以對每個model對應一個簡化的model,在hibernate讀取集合后將集合轉化為該類型的,事實上我在處理多表關聯數據顯示時就用的這個方法,但是你覺得對於每個實體定義兩個model合適嗎?
既然這兩種都不行,那么怎么處理呢?其實完全可以拋開hibernate,采用原始點的方法:先sql讀取所有表名,然后根據表名讀出字段名和對應數據,其中表數據使用二維數據存數即可,事實證明這種方法更靈活。
下邊是實現代碼:

1 /** 2 * 獲取表名 3 * @return List<表明> 4 */ 5 public List<MyDataBase> getTables(){ 6 List<MyDataBase> mdbls=new ArrayList<MyDataBase>(); 7 Session se=HibernateSessionFactory.getSession(); 8 SQLQuery sq=se.createSQLQuery("show tables"); 9 List<String> sls=sq.list(); 10 for(String str:sls){ 11 if("jbpm".equals(str.substring(0, 4))) 12 continue; 13 MyDataBase mdb=new MyDataBase(); 14 mdb.setTablename(str); 15 mdbls.add(mdb); 16 } 17 return mdbls; 18 } 19 20 /** 21 * 獲取字段名 22 * @param tablename 表名 23 * @return 24 */ 25 public List<MyDataBase> getFields(String tablename){ 26 List<MyDataBase> mdbls=new ArrayList<MyDataBase>(); 27 Session se=HibernateSessionFactory.getSession(); 28 SQLQuery sq=se.createSQLQuery("desc "+tablename).addScalar("Field"); 29 List<String> sls=sq.list(); 30 for(String str:sls){ 31 MyDataBase mdb=new MyDataBase(); 32 mdb.setFieldname(str); 33 mdbls.add(mdb); 34 } 35 return mdbls; 36 } 37 38 /** 39 * 獲取字段 40 * @param tablename 表名 41 * @return 42 */ 43 public String[] getFieldsInArray(String tablename){ 44 String[] fields; 45 Session se=HibernateSessionFactory.getSession(); 46 SQLQuery sq=se.createSQLQuery("desc "+tablename).addScalar("Field"); 47 List<String> sls=sq.list(); 48 fields=new String[sls.size()]; 49 for(int i=0;i<sls.size();i++){ 50 fields[i]=sls.get(i); 51 } 52 return fields; 53 } 54 55 /** 56 * 獲取表內容 57 * @param tablename 58 * @return 59 */ 60 public String[][] getTableContent(String tablename){ 61 String[][] content=null; 62 Session se=HibernateSessionFactory.getSession(); 63 SQLQuery sq=se.createSQLQuery("select * from "+tablename); 64 List<Object[]> ls=sq.list(); 65 content=new String[ls.size()][getFields(tablename).size()]; 66 for(int i=0;i<ls.size();i++){ 67 Object[] strs=ls.get(i); 68 for(int j=0;j<strs.length;j++){ 69 content[i][j]=strs[j]==null?"":strs[j].toString(); 70 } 71 } 72 return content; 73 } 74 75 /** 76 * 將字段名集合處理為JSON格式的字符串 77 * @param tablename 對應表名 78 * @return 字符串數組,每一項為一個Ext中grid的一個header 79 */ 80 public String[] getfiledsHeaders(String tablename){ 81 String[] fields=this.getFieldsInArray(tablename); 82 String[] content=new String[fields.length]; 83 String temp="{text:'#1',dataIndex:'#1',flex:1}"; 84 85 for(int i=0;i<fields.length;i++){ 86 content[i]=temp.replace("#1", fields[i]); 87 } 88 return content; 89 } 90 91 /** 92 * 將數據處理為JSON格式的字符串 93 * @param tablename 對應表名 94 * @return 字符串數組,每一項為一條數據庫記錄 95 */ 96 public String[] getContentStr(String tablename){ 97 String[] fields=this.getFieldsInArray(tablename); 98 String[][] content=this.getTableContent(tablename); 99 String jsonContent[]=new String[content.length]; 100 for(int i=0;i<content.length;i++){ 101 String temp=""; 102 for(int j=0;j<fields.length;j++){ 103 temp=temp+"'"+fields[j]+"':'"+content[i][j]+"',"; 104 } 105 temp="{"+temp.substring(0, temp.length()-1)+"}"; 106 jsonContent[i]=temp; 107 } 108 109 return jsonContent; 110 }
其中最終發送客戶端的字段頭格式如下:
"fieldheader":["{text:'rid',dataIndex:'rid',flex:1}","{text:'rname',dataIndex:'rname',flex:1}","{text:'rstring',dataIndex:'rstring',flex:1}"],"fields":["rid","rname","rstring"]
表數據格式如下:
"contentstr":["{'rid':'1','rname':'系統管理員','rstring':'admin'}","{'rid':'2','rname':'實訓負責人','rstring':'manager'}","{'rid':'3','rname':'指導老師','rstring':'teacher'}","{'rid':'4','rname':'組長','rstring':'header'}","{'rid':'5','rname':'學生','rstring':'student'}"]
二、Extjs中grid的實現
其實實現動態grid的思路很簡單,我們知道grid的表頭是由columns屬性指定的,而加載的數據是由對應的store決定的,想要讓其動態改變,只需修改columns和store即可,而恰好Ext提供了reconfigure(Ext.data.Store store, Object[] columns)方法來實現這一操作。
Extjs里邊首先需要一個combox下拉框,在打開grid的時候加載表名以供選擇,其實現如下:
{ xtype: 'combobox', width: 219, id:'combtables', fieldLabel: '選擇表', labelWidth: 50, maxWidth: 200, editable:false, valueField: 'tablename', displayField: 'tablename', store:Ext.create('sxpt.store.admin.Tables'), //對應store請求獲取表名 labelWidth: 80 }
為實現動態的grid,我們首先要實現一個未指定store和空columns的grid,其實現代碼如下:

1 Ext.define('sxpt.view.admin.DataBaseList',{ 2 extend: 'Ext.grid.Panel', 3 alias: 'widget.databaselist', 4 id:'databaselist', 5 title: '數據庫管理', 6 region: 'center', 7 renderTo:Ext.getBody(), 8 columns:[], 9 bbar: { 10 xtype: 'pagingtoolbar', 11 id:'datapagebar', 12 displayInfo: true, 13 displayMsg: '顯示 {0} - {1} 共 {2}條', 14 emptyMsg: "沒有數據需要顯示" 15 }, 16 dockedItems:[{ 17 dock: 'top', 18 xtype: 'toolbar', 19 layout:'fit', 20 items: [ 21 { 22 xtype:'tabpanel', 23 height:80, 24 items:[{ 25 xtype:'panel', 26 title:'查詢表', 27 margin: '5 0 0 20', 28 items:[ 29 { 30 xtype: 'combobox', 31 width: 219, 32 id:'combtables', 33 fieldLabel: '選擇表', 34 labelWidth: 50, 35 maxWidth: 200, 36 editable:false, 37 valueField: 'tablename', 38 displayField: 'tablename', 39 store:Ext.create('sxpt.store.admin.Tables'), 40 labelWidth: 80, 41 42 } 43 ] 44 } 45 ] 46 }] 47 });
之后為了每次選擇表后就加載數據,在combobox中添加change監聽器,實現如下:
1 listeners:{ 2 change:function(combo, record,index){ 3 var griddb=Ext.getCmp('databaselist'); 4 Ext.Ajax.request({ 5 url:'admin/admin_database_fieldsa', 6 params:{ 7 tablename:this.value 8 }, 9 method : 'POST', 10 success : function(response) { 11 var strT=response.responseText; 12 13 var json = Ext.JSON.decode(strT.replace(/\"\{/g,"{").replace(/\}\"/g,"}")); //替換掉由 於struts-json序列化產生的多余雙引號 14 var store=Ext.create('Ext.data.Store',{ 15 fields:json.fields, 16 data:json.contentstr 17 }); 18 var pagebar=Ext.getCmp('datapagebar'); 19 pagebar.bind(store); //為分頁欄加載數據 20 store.load(); 21 griddb.reconfigure(store,json.fieldheader); 22 }
至此,動態的grid就已經完成了。
三、一個問題
如果你注意struts—json插件的處理結果的話就會發現,它對String類型的屬性序列化是會自動加上引號,如上邊字段頭格式序列化后為:
"fieldheader":["{text:'rid',dataIndex:'rid',flex:1}","{text:'rname',dataIndex:'rname',flex:1}","{text:'rstring',dataIndex:'rstring',flex:1}"]
但是我們期望的數據格式應該是不帶紅色引號的。這兩者什么區別呢?主要是字符串執行Ext.JSON.decode()后的結果不一樣,帶引號的反序列化之后fieldheader是一個包含三個字符串的集合(這與服務器端一致,但卻並非我們想要的),而不帶引號的反序列化之后fieldheader里邊包含的是3個對象,每個對象里邊包含三個字段,這樣的格式才是Extjs可以正確處理的json數據格式。
因此需要在反序列化之前對接收的字符串進行如下處理:
strT.replace(/\"\{/g,"{").replace(/\}\"/g,"}")
一下是完成的js代碼:

1 Ext.define('sxpt.view.admin.DataBaseList',{ 2 extend: 'Ext.grid.Panel', 3 alias: 'widget.databaselist', 4 id:'databaselist', 5 title: '數據庫管理', 6 region: 'center', 7 renderTo:Ext.getBody(), 8 //store: 'admin.Teacher', 9 columns:[], 10 bbar: { 11 xtype: 'pagingtoolbar', 12 id:'datapagebar', 13 displayInfo: true, 14 displayMsg: '顯示 {0} - {1} 共 {2}條', 15 emptyMsg: "沒有數據需要顯示" 16 }, 17 dockedItems:[{ 18 dock: 'top', 19 xtype: 'toolbar', 20 layout:'fit', 21 items: [ 22 { 23 xtype:'tabpanel', 24 height:80, 25 items:[{ 26 xtype:'panel', 27 title:'查詢表', 28 margin: '5 0 0 20', 29 items:[ 30 { 31 xtype: 'combobox', 32 width: 219, 33 id:'combtables', 34 fieldLabel: '選擇表', 35 labelWidth: 50, 36 maxWidth: 200, 37 editable:false, 38 valueField: 'tablename', 39 displayField: 'tablename', 40 store:Ext.create('sxpt.store.admin.Tables'), 41 labelWidth: 80, 42 listeners:{ 43 change:function(combo, record,index){ 44 var griddb=Ext.getCmp('databaselist'); 45 Ext.Ajax.request({ 46 url:'admin/admin_database_fieldsa', 47 params:{ 48 tablename:this.value 49 }, 50 method : 'POST', 51 success : function(response) { 52 var strT=response.responseText; 53 54 var json = Ext.JSON.decode(strT.replace(/\"\{/g,"{").replace(/\}\"/g,"}")); 55 var store=Ext.create('Ext.data.Store',{ 56 fields:json.fields, 57 data:json.contentstr 58 }); 59 var pagebar=Ext.getCmp('datapagebar'); 60 pagebar.bind(store); 61 store.load(); 62 griddb.reconfigure(store,json.fieldheader); 63 } 64 }) 65 } 66 } 67 } 68 ] 69 } 70 } 71 ] 72 }], 73 initComponent: function () { 74 this.callParent(arguments); 75 } 76 });
源碼暫時就不共享了,等過一段整理好了再發。