SpringBoot+Jpa實現Excel的導入導出(動態Sql、分頁查詢、聯表join)

Excel (SpringBoot + JPA)

SpringBoot+Jpa實現Excel的導入導出(動態Sql、分頁查詢、聯表join)

  最近做了一個項目甲方需求中要求數據的導入導出到Excel文件,Excel的複雜表頭和數據格式一直是個頭疼的問題,使用poi或者jexcelapi的話就需要花費大量時間處理表頭以及數據格式問題,但是整個項目的開發時間只有一週,對接和聯調一週,那就只能去找快速的“黑科技”了–EasyExcel,雖然EasyExcel只是對poi的再封裝,但是EasyExcel中有一個模板寫入的功能,是可以很好的解決複雜表頭和一些基本的數據格式問題的。抽了點時間整理了一個demo,也包含一些對Jpa的進階用法(動態Sql分頁查詢、聯表join等)。

開發環境

    環境: windows 10
    編譯器: IDEA 2018
    數據庫: MySQL 5.6
    JDK: jdk1.8.0_92
    Maven: 3.6

項目結構樹

│  .gitignore
│  LICENSE
│  pom.xml                                                          --項目pom文件
│  README.md                                                        --readme.md文件						
├─github
│  └─image                                                          --用於存放readme.md中鏈接的圖片
├─other
│      Excel.sql                                                    --數據庫建表Sql語句
│      Excel數據導入測試[PostMan].postman_collection.json            --信息導入接口PostMan導出的json文件
│      個人信息導入.xlsx                                             --個人信息導入測試數據
│      畢業信息導入.xlsx                                             --畢業信息導入測試數據
│      目錄結構樹.txt
├─src
│  ├─main
│  │  ├─java
│  │  │  └─com
│  │  │      └─cyan
│  │  │          └─excel
│  │  │              │  ExcelApplication.java                       --SpringBoot啓動類
│  │  │              │  
│  │  │              ├─controller
│  │  │              │      ExcelController.java                    --Controller層
│  │  │              │     
│  │  │              ├─entity				
│  │  │              │  │  GraduateInfo.java                        --畢業信息實體
│  │  │              │  │  UserInfo.java                            --用戶信息實體
│  │  │              │  │   
│  │  │              │  ├─joint				
│  │  │              │  │      GraduateUserJoint.java               --畢業和用戶聯表Join實體
│  │  │              │  │      
│  │  │              │  └─model				
│  │  │              │          DetailModel.java                    --詳細信息Excel文件對應模型
│  │  │              │          GraduateModel.java                  --畢業信息Excel文件對應模型
│  │  │              │          UserModel.java                      --用戶信息Excel文件對應模型
│  │  │              │          
│  │  │              ├─enums				
│  │  │              │      ExcelFileEnum.java                      --Excel文件類型枚舉
│  │  │              │      SexTypeEnum.java                        --性別類型枚舉
│  │  │              │      
│  │  │              ├─exception				
│  │  │              │  │  ExcelException.java                      --全局異常
│  │  │              │  │  
│  │  │              │  └─handler					
│  │  │              │          ExcelExceptionHandler.java          --全局異常捕獲處理
│  │  │              │          
│  │  │              ├─listener					
│  │  │              │      ExcelListener.java                      --excel數據處理
│  │  │              │      
│  │  │              ├─repository
│  │  │              │  │  GraduateInfoRepository.java              --畢業信息DAO層(jpa)
│  │  │              │  │  GraduateUserJointRepository.java	        --用戶和畢業聯表joinDAO層(jpa)
│  │  │              │  │  UserInfoRepository.java                  --用戶信息DAO層(jpa)
│  │  │              │  │  
│  │  │              │  └─Impl
│  │  │              │          GraduateUserJointRepositoryImpl.java--用戶和畢業聯表joinDAO層(代碼實現分頁/動態Sql)
│  │  │              │          
│  │  │              ├─result
│  │  │              │      ResponseResult.java                     --返回結果體
│  │  │              │      ResponseResultEnum.java                 --返回結果異常枚舉
│  │  │              │      ResponseResultUtils.java                --返回結果工具類
│  │  │              │      
│  │  │              ├─service
│  │  │              │  │  DetailService.java                       --詳細信息的Service層
│  │  │              │  │  GraduateService.java                     --畢業信息的Service層
│  │  │              │  │  UserService.java                         --用戶信息的Service層
│  │  │              │  │  
│  │  │              │  ├─handler
│  │  │              │  │      ImportHandler.java                   --Excel數據導入處理
│  │  │              │  │      
│  │  │              │  └─Impl
│  │  │              │          DetailServiceImpl.java              --詳細信息的Service實現
│  │  │              │          GraduateServiceImpl.java            --畢業信息的Service實現
│  │  │              │          UserServiceImpl.java                --用戶信息的Service實現
│  │  │              │          
│  │  │              ├─utils
│  │  │              │      ConvertUtils.java                       --數據轉換工具類
│  │  │              │      CopyUtils.java                          --Bean拷貝工具類(不拷貝null值)
│  │  │              │      ExcelUtils.java                         --Excel相關工具類
│  │  │              │      FileUtils.java                          --文件相關工具類
│  │  │              │      PathUtils.java                          --路徑相關工具類
│  │  │              │      StreamUtils.java                        --流處理工具類
│  │  │              │      
│  │  │              └─vo
│  │  │                      ImportResultVO.java                    --Excel導入結果VO層
│  │  │                      
│  │  └─resources
│  │      │  application.yml                                        --項目配置文件
│  │      │  	
│  │      ├─static
│  │      │  └─model                                                --Excel導入導出模板
│  │      │          個人信息模板.xlsx						
│  │      │          分類信息模板.xlsx
│  │      │          畢業信息模板.xlsx
│  │      │          詳細信息模板.xlsx

Excel模板

1、個人信息模板

個人信息模板 icon

2、分類信息模板

分類信息模板 icon

3、畢業信息模板

畢業信息模板 icon

4、詳細信息模板

詳細信息模板 icon

項目測試

1、導入數據測試

  數據導入時,接收一個formData格式的excel文件對象,參數爲excel,後臺接收MultipartFile類型,暫時只做了單文件導出,有多文件導入需求的可以去度娘查詢。

  Excel的序號一列是不會導入數據庫的,UserInfo使用UserId身份證作爲主鍵, GraduateInfo使用自增主鍵,數據庫裏兩表不設外鍵關係,只在業務層控制外鍵關係。設計數據庫時只是爲了做兩表join的功能,把一對一的數據拆分到倆個表裏了,導致GraduateInfo不控制的話數據會出現大量重複,因此在Service>handler下做了邏輯上的控制,因此GraduateInfo表參考意義不大。

/** 數據導入信息 返回結果體data */
public class ImportResultVO<T> {
    /** 成功數量 */
    private Integer success = 0;
    /** 失敗數量 */
    private Integer failure = 0;
    /** 重複數量 */
    private Integer repeat = 0;
    /** 失敗數據 */
    private List<T> failureList;
    /** 重複數據 */
    private List<T> repeatList;  
      
      //...
}

1、個人信息導入測試

模擬數據:

個人信息測試數據 icon

PostMan測試:

個人信息導入測試 icon

數據庫驗證:

個人信息數據庫 icon

2、畢業信息導入測試

模擬數據:

畢業信息測試數據 icon

PostMan測試:

畢業信息導入測試 icon

數據庫驗證:
注:數據庫之前沒做覆蓋,就存在很多重複數據,手動刪除了一些,又重新導入,所以id不連續

數據庫驗證 icon

2、導出測試

  數據導出測試沒有返回結果體,直接返回Response,會將生成好的excel文件直接以字節流的形式寫入到Response,通過設置Response的Header屬性讓瀏覽器自行解析文件。

關於請求頭相關知識請自行度娘

{
  "Content-Disposition":"attachment;filename=文件名.xlsx",
  "Content-Type": "application/octet-stream"
}

1、個人信息導出測試

個人信息導出測試 icon

2、畢業信息導出測試

畢業信息導出測試 icon

3、分類信息導出測試

  按性別導出主要是爲了滿足,不同的數據分別寫入到同一個excel的不同sheet中的這個需求。

按性別分類導出(男):

按性別分類導出(男) icon

按性別分類導出(女):

按性別分類導出(女) icon

4、詳細信息導出測試

  詳細信息導出用到了聯表Join,用原生SQL查詢的話這其實不算個問題,我這裏最想展示的是當用Jpa時,分頁+動態sql+聯表Join的較爲複雜的實現方式。

public class GraduateUserJointRepositoryImpl {
    @PersistenceContext
    private EntityManager entityManager;

    /**
     * 動態SQL查詢數據分頁
     * @param whereSql
     * @param pageable
     * @return
     */
    @SuppressWarnings("unchecked")
    public Page<GraduateUserJoint> findAllByGraduateUserJointWithWhereSql(String whereSql, Pageable pageable) {
        /** 原生SQL語句結合外部動態構建的whereSql實現動態SQL拼接(在這裏拼也不是不可以) */
        /** 聯表Join時根據需求確定 left/right/inner */
        /** whereSql 中最少要包含 1=1 類似的邏輯表達式 防止SQL報錯 */
        String dataSql = "select * from graduate_info g inner join user_info u on u.user_id = g.user_id where " + whereSql;
        String countSql = "select count(1) from graduate_info g inner join user_info u on u.user_id = g.user_id where " + whereSql;
        
        Query dataQuery = entityManager.createNativeQuery(dataSql, GraduateUserJoint.class);
        Query countQuery = entityManager.createNativeQuery(countSql);
        /** 分頁供能實現 */
        dataQuery.setFirstResult((int) pageable.getOffset());
        dataQuery.setMaxResults(pageable.getPageSize());
        /** 分頁總數統計 */
        BigInteger count = (BigInteger) countQuery.getSingleResult();
        long total = count.longValue();
        /** 分頁數據 */
        List<GraduateUserJoint> graduateUserJointList = total > pageable.getOffset() ? dataQuery.getResultList() : Collections.<GraduateUserJoint> emptyList();
        return new PageImpl<>(graduateUserJointList, pageable, total);
    }
    //...
}
存在bug的詳細信息導出:

詳細信息導出測試 icon

修復bug的詳細信息導出:

詳細信息導出測試(修復) icon

EasyExcel
Spring-Boot
Spring-Data-Jpa

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