day52_BOS項目_04

  • 今天內容安排:
    • 1、區域數據批量導入功能
      • jQuery OCUpload(一鍵上傳插件)
      • 使用 apache POI 解析Excel文件
      • 使用 Pinyin4J 生成簡碼和城市編碼
    • 2、實現區域的分頁查詢
    • 3、對分頁代碼重構
    • 4、添加分區(使用combobox下拉框)
    • 5、分區的組合條件分頁查詢
    • 6、分區數據導出功能

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

第一步:在subarea.jsp中使用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、要在添加分區的選擇區域中實現區域的模糊查詢

第一步:在subarea.jsp中使用combobox下拉框展示區域數據到下拉框中,爲組合框(combobox)添加一個屬性:mode:'remote',如果組合框(combobox)從服務器加載就設置爲 'remote'。當設置爲 'remote' 模式時,用戶輸入的值將會被作爲名爲 'q' 的 http 請求參數發送到服務器,以獲取新的數據。 如下圖所示:

瀏覽器效果如下圖所示:

第二步:修改RegionAction中提供listajax()方法,查詢所有的區域數據,返回json數據(此時帶有模糊查詢的條件)

    // 採用屬性驅動的方式,接收頁面傳過來的參數q,即:模糊查詢的條件
    private String q;
    public void setQ(String q) {
        this.q = q;
    }

    /**
     * 查詢所有的區域數據,返回json數據
     * @throws IOException 
     */
    public String listajax() throws IOException {
        List<Region> list = null;
        if (StringUtils.isNotBlank(q)) {
            list = regionService.findByQ(q);
        } else {
            list = regionService.findAll();
        }
        String[] excludes = new String[] {"subareas"}; // 實際做項目中,要把沒用到的數據都給排除掉,也就是說不需要顯示的數據有很多,本例中只排除掉了一個
        this.writeList2Json(list, excludes);
        return "none";
    }

第三步:在RegionDaoImpl中實現分區的模糊查詢

package com.itheima.bos.dao.impl;

import java.util.List;

import org.springframework.stereotype.Repository;

import com.itheima.bos.dao.IRegionDao;
import com.itheima.bos.dao.base.impl.BaseDaoImpl;
import com.itheima.bos.domain.Region;

@Repository
public class RegionDaoImpl extends BaseDaoImpl<Region> implements IRegionDao {

    // sql語句:select * from bc_region where province like '%河北%' or city like '%石家莊%' or district like '%正定縣%'
    // hql語句:from Region where province like ? or city like ? or district like ?
    public List<Region> findByQ(String q) {
        String hql = "from Region where province like ? or city like ? or district like ?";
        return this.getHibernateTemplate().find(hql, "%" + q + "%", "%" + q + "%", "%" + q + "%");
    }

}

瀏覽器效果如上圖“天津”。

6、添加分區

第一步:頁面位置:/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>

7、解決區域分頁查詢的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"}); 

8、實現分區分頁查詢(沒有過濾條件)

  • 代碼同區域的分頁查詢。 小區別:當我們查詢分區表的時候,需要立即去查詢關聯的區域表 我們需要修改分區的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>

9、實現分區組合條件分頁查詢

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";
    }

10、分區數據導出功能

  • 導出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";
    }

瀏覽器界面效果圖如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章