day52_BOS專案_04
- 今天內容安排:
1、區域資料批量匯入功能
1.1、jQuery OCUpload(一鍵上傳外掛)
-
ajax不能做檔案上傳。
第一步:在jsp頁面中引入外掛的js檔案
<!-- 引入一鍵上傳控制元件的js檔案 --> <script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery.ocupload-1.1.2.js"></script>
第二步:在頁面中提供任意一個元素
<input id="but1" type="button" value="上傳">
第三步:呼叫該外掛提供的upload方法,動態修改頁面html程式碼
<!-- 使用該控制元件 --> <script type="text/javascript"> $(function() { $("#but1").upload({ action: 'abc', name: 'myFile' }); }); </script>
動態修改頁面html程式碼效果如下圖所示:

1.2、使用apache POI解析Excel檔案
-
Apache POI是Apache軟體基金會的開放原始碼函式庫,POI提供API給Java程式對Microsoft Office格式檔案讀和寫的功能。
第一步:匯入poi-3.9-20121203.jar包
第二步:測試程式碼如下:
package com.itheima.mytest; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Row; import org.junit.Test; public class POITest { /** * 使用POI解析Excel檔案 * @throws IOException * @throws FileNotFoundException */ @Test public void test1() throws FileNotFoundException, IOException{ HSSFWorkbook workbook = new HSSFWorkbook(new FileInputStream(new File("E:\\test\\abc.xls"))); HSSFSheet sheet = workbook.getSheetAt(0); for (Row row : sheet) { String v1 = row.getCell(0).getStringCellValue(); String v2 = row.getCell(1).getStringCellValue(); String v3 = row.getCell(2).getStringCellValue(); String v4 = row.getCell(3).getStringCellValue(); String v5 = row.getCell(4).getStringCellValue(); System.out.println(v1 + " " + v2 + " " + v3 + " " + v4 + " " + v5); } } }
控制檯輸出結果為:
區域編號 省份 城市 區域 郵編 QY001 北京市 北京市 東城區 110101 QY002 北京市 北京市 西城區 110102 QY003 北京市 北京市 朝陽區 110105 QY004 北京市 北京市 豐臺區 110106 QY005 北京市 北京市 石景山區 110107 QY006 北京市 北京市 海淀區 110108 QY007 北京市 北京市 門頭溝區 110109 QY008 北京市 北京市 房山區 110111 QY009 北京市 北京市 通州區 110112 QY010 北京市 北京市 順義區 110113 QY011 北京市 北京市 昌平區 110114 QY012 北京市 北京市 大興區 110115 QY013 北京市 北京市 懷柔區 110116 QY014 北京市 北京市 平谷區 110117 QY015 北京市 北京市 密雲縣 110228 QY016 北京市 北京市 延慶縣 110229 ......
第三步:在RegionAction中提供批量匯入方法
package com.itheima.bos.web.action; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Row; import org.apache.struts2.ServletActionContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import com.itheima.bos.domain.Region; import com.itheima.bos.service.IRegionService; import com.itheima.bos.web.action.base.BaseAction; /** * 區域設定 * @author Bruce * */ @Controller @Scope("prototype") public class RegionAction extends BaseAction<Region> { @Autowired private IRegionService regionService; // 採用屬性驅動的方式,接收上傳過來的檔案 private File myFile; public void setMyFile(File myFile) { this.myFile = myFile; } /** * 批量匯入Xls檔案 * @throws IOException * @throws FileNotFoundException */ public String importXls() throws Exception { String flag = "1"; try { // 使用POI解析Excel檔案 HSSFWorkbook workbook = new HSSFWorkbook(new FileInputStream(myFile)); // 獲得第一個sheet頁 HSSFSheet sheet = workbook.getSheetAt(0); List<Region> list = new ArrayList<Region>(); for (Row row : sheet) { int rowNum = row.getRowNum(); if (rowNum == 0) { // 第一行,標題行,忽略 continue; } String id = row.getCell(0).getStringCellValue(); String province = row.getCell(1).getStringCellValue(); String city = row.getCell(2).getStringCellValue(); String district = row.getCell(3).getStringCellValue(); String postcode = row.getCell(4).getStringCellValue(); Region region = new Region(id, province, city, district, postcode, null, null, null); list.add(region); } regionService.saveBatch(list); } catch (Exception e) { flag = "0"; } // 伺服器響應給瀏覽器一個狀態碼,這種手法常用 ServletActionContext.getResponse().setContentType("text/html;charset=UTF-8"); ServletActionContext.getResponse().getWriter().print(flag); return "none"; } }
第四步:瀏覽器根據伺服器響應回來的狀態碼,進行判斷並給出提示資訊
<!-- 使用上傳控制元件 --> <script type="text/javascript"> $(function() { $("#button-import").upload({ action: '${pageContext.request.contextPath}/regionAction_importXls.action', name: 'myFile', // 瀏覽器根據伺服器響應回來的狀態碼,進行判斷並給出提示資訊 onComplete: function(data) { // alert(data); if (data == '1') { // 檔案上傳成功 $.messager.alert("提示資訊", "區域資料匯入成功!", "info"); } else { // 檔案上傳失敗 $.messager.alert("提示資訊", "區域資料匯入失敗!", "warning"); } } }); }); </script>
1.3、使用Pinyin4J生成簡碼和城市編碼
第一步:匯入pinyin4j-2.5.0.jar包,拷貝PinYin4jUtils.java工具類至utils包中
第二步:測試類程式碼如下:
package com.itheima.mytest; import org.apache.commons.lang3.StringUtils; import org.junit.Test; import com.itheima.bos.utils.PinYin4jUtils; public class Pinyin4JTest { @Test public void test1(){ String province = "河北省"; String city = "石家莊市"; String district = "長安區"; // 城市編碼 --> shijiazhuang city= city.substring(0, city.length() - 1); String[] stringToPinyin = PinYin4jUtils.stringToPinyin(city); String citycode = StringUtils.join(stringToPinyin, ""); System.out.println(citycode); // 簡碼 --> HBSJZCA province= province.substring(0, province.length() - 1); district= district.substring(0, district.length() - 1); String info = province + city + district; // 河北石家莊長安 String[] headByString = PinYin4jUtils.getHeadByString(info); String shortcode = StringUtils.join(headByString, ""); System.out.println(shortcode); } }
2、實現區域的分頁查詢
- 程式碼同取派員的分頁查詢。
3、對分頁程式碼重構
- 在BaseAction中抽取PageBean物件,在BaseAction中提供setPage和setRows方法,並注入給PageBean物件
// 採用屬性驅動的方式,接收頁面提交過來的引數 protected PageBean pageBean = new PageBean(); public void setPage(int page) { // 設定當前頁碼 pageBean.setCurrentPage(page); } public void setRows(int rows) { // 設定每頁顯示記錄數 pageBean.setPageSize(rows); }
- 在BaseAction中抽取條件查詢物件
// 設定離線條件查詢物件,封裝查詢條件 DetachedCriteria detachedCriteria = null;
- 在BaseAction的構造方法中建立條件查詢物件,並注入給PageBean物件
如下圖所示:
- 在BaseAction中抽取將PageBean物件轉為json的方法
/** * 將PageBean物件轉為JSON格式的資料的方法 * @param pageBean * @param excludes * @throws IOException */ public void writePageBean2Json(PageBean pageBean, String[] excludes) throws IOException { // 步驟:先匯入json-lib的jar包+依賴包,步驟連結:https://www.cnblogs.com/chenmingjun/p/9513143.html // 將PageBean物件轉為JSON格式的資料響應給客戶端瀏覽器進行顯示 // 排除不需要的資料和排除關聯物件 JsonConfig jsonConfig = new JsonConfig(); jsonConfig.setExcludes(new String[] {"currentPage", "pageSize", "detachedCriteria"}); JSONObject jsonObject = JSONObject.fromObject(pageBean, jsonConfig); String json = jsonObject.toString(); ServletActionContext.getResponse().setContentType("text/json;charset=UTF-8"); ServletActionContext.getResponse().getWriter().print(json); }
- 在RegionAction中使用分頁方法
/** * 分頁查詢 * @throws IOException */ public String pageBean() throws IOException { // 呼叫方法,設定PageBean物件的其他屬性 regionService.pageBean(pageBean); // 注意:pageBean傳的是物件的引用,呼叫該方法後,物件的屬性就發生改變了 // 呼叫將PageBean物件轉為JSON格式的資料的方法 this.writePageBean2Json(pageBean, new String[] {"currentPage", "pageSize", "detachedCriteria"}); // 實際做專案中,要把沒用到的資料都給幹掉,也就是說不需要顯示的資料有很多 return "none"; }
4、使用jQuery EasyUI 下拉框combobox

combobox下拉框
展示區域資料到下拉框中
<tr> <td>選擇區域</td> <td> <input class="easyui-combobox" name="region.id" data-options="valueField:'id',textField:'name', url:'${pageContext.request.contextPath}/regionAction_listajax1.action'" /> </td> </tr>
效果如下圖所示:

第二步:在RegionAction中提供listajax()方法,查詢所有的區域資料,返回json資料,並將該方法抽取至BaseAction中
RegionAction.java
/** * 查詢所有的區域資料,返回json * @throws IOException */ public String listajax() throws IOException { List<Region> list = regionService.findAll(); String[] exclude = new String[]{"subareas"}; // 實際做專案中,要把沒用到的資料都給排除掉,也就是說不需要顯示的資料有很多,本例中只排除掉了一個 this.writeList2Json(list, exclude); return null; }
BaseAction.java
/** * 將List集合物件轉為JSON格式的資料的方法 * @param list * @param exclude * @throws IOException */ public void writeList2Json(List list, String[] exclude) throws IOException { JsonConfig jsonConfig = new JsonConfig(); jsonConfig.setExcludes(new String[] {"currentPage", "pageSize", "detachedCriteria"}); JSONArray jsonObject = JSONArray.fromObject(list, jsonConfig); String json = jsonObject.toString(); ServletActionContext.getResponse().setContentType("text/json;charset=UTF-8"); ServletActionContext.getResponse().getWriter().print(json); }
為了使返回的json中含有name欄位,需要在Region類中提供getName()方法
// 序列化一個物件的時候,找的是getter方法 public String getName() { return province + city + district; }
瀏覽器返回的json資料效果如下圖所示:

頁面效果如下圖所示:

5、新增分割槽
第一步:頁面位置:/bos19/WebContent/WEB-INF/pages/base/subarea.jsp
為了便於處理,我們先將subarea.jsp中的 分揀編碼
選項框刪掉,該編號我們讓其自動生成。
我們在Subarea.hbm.xml中更改主鍵生成策略,程式碼如下:
<id name="id" type="java.lang.String"> <column name="id" length="32" /> <!-- generator:主鍵生成策略,uuid:生成32位的不重複隨機字串當做主鍵 --> <generator class="uuid" /> </id>
第二步:為新增視窗中的“儲存按鈕”繫結事件
<div class="datagrid-toolbar"> <a id="save" icon="icon-save" href="#" class="easyui-linkbutton" plain="true" >儲存</a> <script type="text/javascript"> $(function() { $("#save").click(function() { var v = $("#addSubareaForm").form("validate"); if (v) { $("#addSubareaForm").submit(); } }); }); </script> </div>
第三步:建立SubareaAction類,提供add方法,處理分割槽新增動作
SubareaAction.java
package com.itheima.bos.web.action; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Controller; import com.itheima.bos.domain.Subarea; import com.itheima.bos.web.action.base.BaseAction; /** * 分割槽設定 * @author Bruce * */ @Controller @Scope("prototype") public class SubareaAction extends BaseAction<Subarea>{ /** * 新增分割槽的方法 * @return */ public String add() { subareaService.save(model); return "list"; } }
在第三步之前,我們將所有的注入service,抽取至BaseAction中,將修飾符public改為protected,使其子類能夠訪問
BaseAction.java
// 注入service @Autowired protected IUserService userServie; @Autowired protected IStaffService staffService; @Autowired protected IRegionService regionService; @Autowired protected ISubareaService subareaService;
第四步:配置struts.xml
<!-- 分割槽管理:配置subareaAction--> <action name="subareaAction_*" class="subareaAction" method="{1}"> <result name="list">/WEB-INF/pages/base/subarea.jsp</result> </action>
6、解決區域分頁查詢的bug
Method public java.lang.String org.apache.commons.lang.exception.NestableRuntimeException.getMessage(int) threw an exception when invoked on net.sf.json.JSONException: There is a cycle in the hierarchy!
- 延遲載入也稱為懶載入,是Hibernate3關聯關係物件預設的載入方式,所謂延遲載入就是當在真正需要資料的時候,才真正執行資料載入操作。簡單理解為,只有在使用的時候,才會發出sql語句進行查詢。
- Hibernate中主要是通過代理(proxy)機制來實現延遲載入。我們在查詢區域的時候,區域關聯的分割槽沒有立即查詢,因為所有的關聯查詢預設都是延時載入(懶載入)。那麼返回來的就是代理物件,而代理物件是不能被序列化的。
- 如何解決呢?
答:因為PageBean中的屬性有集合list,此時的list集合中存放的是Region物件,而Region物件中又關聯一個集合set,該set集合存放的Subareas物件,該Subareas物件預設是懶載入的,而此時我們沒有用到Subareas物件的資料,所以我們就應該將其排除掉。
this.writePageBean2Json(pageBean, new String[] {"currentPage", "pageSize", "detachedCriteria", "subareas"});
7、實現分割槽分頁查詢(沒有過濾條件)
-
程式碼同區域的分頁查詢。
小區別:當我們查詢分割槽表的時候,需要立即去查詢關聯的區域表
我們需要修改分割槽的Hibernate配置檔案Subarea.hbm.xml中的載入時機,修改程式碼如下:
<!-- lazy="false" 表示:當我們查詢分割槽表的時候,立即去查詢關聯的區域表,即不使用懶載入,使用立即載入,就不返回代理物件了 --> <many-to-one lazy="false" name="region" class="com.itheima.bos.domain.Region" fetch="select"> <column name="region_id" length="32" /> </many-to-one> <many-to-one name="decidedzone" class="com.itheima.bos.domain.Decidedzone" fetch="select"> <column name="decidedzone_id" length="32" /> </many-to-one>
8、實現分割槽組合條件分頁查詢
EasyUI Datagrid 資料網格的load()方法:
載入並顯示第一頁的行,如果指定 'param' 引數,它將替換 queryParams 屬性。通常情況下,通過傳遞一些從引數進行查詢,該方法被呼叫來從伺服器載入新資料。
查詢分割槽頁面如下圖所示:

第一步:為“查詢按鈕”繫結事件,呼叫datagrid的load()方法,重新發起ajax請求,並提交輸入框引數,這裡我們使用一個工具方法:將指定的表單中的輸入項序列化為json物件
// 工具方法:可以將指定的表單中的輸入項序列化為json物件 $.fn.serializeJson = function() { var serializeObj = {}; var array = this.serializeArray(); $(array).each( function() { if (serializeObj[this.name]) { if ($.isArray(serializeObj[this.name])) { serializeObj[this.name].push(this.value); } else { serializeObj[this.name] = [serializeObj[this.name], this.value]; } } else { serializeObj[this.name] = this.value; } }); return serializeObj; }; // 繫結事件:執行查詢 $("#btn").click(function() { // 將表單序列化為json物件 var p = $("#searchForm").serializeJson(); // json物件格式:{id:'xxx', name:'xxx', age:'xxx', ...} // 重新發起ajax請求,並提交新的引數(包括原來的引數) $("#grid").datagrid("load", p); // 關閉查詢視窗 $("#searchWindow").window("close"); });
瀏覽器的除錯截圖:

第三步:修改SubareaAction中的分頁查詢方法,封裝分頁查詢的條件
/** * 分頁查詢 * @return * @throws IOException */ public String pageQuery() throws IOException { // 由於提交的表單中新增了其他條件,所以我們在分頁查詢之前,需要封裝條件 DetachedCriteria detachedCriteria2 = pageBean.getDetachedCriteria(); // QBC查詢語句:當關聯查詢時,也即多表查詢,需要建立別名,通過別名才可以訪問關聯物件的屬性 // 先根據分割槽的地址關鍵字進行模糊查詢 String addresskey = model.getAddresskey(); if (StringUtils.isNotBlank(addresskey)) { detachedCriteria2.add(Restrictions.like("addresskey", "%" + addresskey + "%")); } // 再根據 省/市/區 進行模糊查詢 Region region = model.getRegion(); if (region != null) { // 建立別名,用於多表查詢 detachedCriteria2.createAlias("region", "r"); String province = region.getProvince(); String city = region.getCity(); String district = region.getDistrict(); if (StringUtils.isNotBlank(province)) { // 根據 省 進行模糊查詢(注意:關聯查詢) detachedCriteria2.add(Restrictions.like("r.province", "%" + province + "%")); } if (StringUtils.isNotBlank(city)) { // 根據 市 進行模糊查詢(注意:關聯查詢) detachedCriteria2.add(Restrictions.like("r.city", "%" + city + "%")); } if (StringUtils.isNotBlank(district)) { // 根據 區 進行模糊查詢(注意:關聯查詢) detachedCriteria2.add(Restrictions.like("r.district", "%" + district + "%")); } } subareaService.pageQuery(pageBean); this.writePageBean2Json(pageBean, new String[] {"currentPage", "pageSize", "detachedCriteria", "decidedzone", "subareas"}); return "none"; }
9、分割槽資料匯出功能
- 匯出Excel檔案提供客戶下載
第一步:為“匯出”按鈕繫結事件
// 匯出Excel檔案,注意:檔案下載必須是同步提交方式 function doExport() { // get方式提交 window.location.href = "${pageContext.request.contextPath}/subareaAction_exportXls.action"; }
第二步:在SubareaAction中提供匯出方法
/** * 使用POI寫入Excel檔案,提供下載 * @return * @throws IOException */ public String exportXls() throws IOException { List<Subarea> list = subareaService.findAll(); // 在記憶體中建立一個Excel檔案,通過輸出流寫到客戶端提供下載 HSSFWorkbook workbook = new HSSFWorkbook(); // 建立一個sheet頁 HSSFSheet sheet = workbook.createSheet("分割槽資料"); // 建立標題行 HSSFRow headRow = sheet.createRow(0); // 設定標題行單元格的內容 headRow.createCell(0).setCellValue("分割槽編號"); headRow.createCell(1).setCellValue("區域編號"); headRow.createCell(2).setCellValue("地址關鍵字"); headRow.createCell(3).setCellValue("省市區"); // 遍歷list集合 for (Subarea subarea : list) { HSSFRow dataRow = sheet.createRow(sheet.getLastRowNum() + 1); dataRow.createCell(0).setCellValue(subarea.getId()); dataRow.createCell(1).setCellValue(subarea.getRegion().getId()); dataRow.createCell(2).setCellValue(subarea.getAddresskey()); Region region = subarea.getRegion(); dataRow.createCell(3).setCellValue(region.getProvince() + region.getCity() + region.getDistrict()); } String filename = "分割槽資料.xls"; String agent = ServletActionContext.getRequest().getHeader("User-Agent"); // 瀏覽器型別 filename = FileUtils.encodeDownloadFilename(filename, agent); // 一個流兩個頭 ServletOutputStream out = ServletActionContext.getResponse().getOutputStream(); String contentType = ServletActionContext.getServletContext().getMimeType(filename); ServletActionContext.getResponse().setContentType(contentType); ServletActionContext.getResponse().setHeader("content-disposition", "attchment;filename=" + filename); workbook.write(out); return "none"; }
瀏覽器介面效果圖如下:
