使用Java實(shí)現(xiàn)Excel導(dǎo)入并進(jìn)行數(shù)據(jù)校驗(yàn)
一、產(chǎn)品需求
1.下載指定的excel數(shù)據(jù)模板
2.excel模板寫入數(shù)據(jù)并導(dǎo)入
3.導(dǎo)入的時(shí)候根據(jù)校驗(yàn)規(guī)則進(jìn)行篩選,導(dǎo)入成功的返回成功列表,數(shù)據(jù)存在問題的返回失敗列表,失敗列表支持?jǐn)?shù)據(jù)編輯修正
看到需求的第一眼,可能就覺得第三點(diǎn)有點(diǎn)難度,我們知道,傳統(tǒng)的數(shù)據(jù)校驗(yàn)可以通過在傳輸對象dto上面加注解實(shí)現(xiàn)。
//第一種 public Result test1(@RequestBody @Validated TestDTO dto) {...} //第二種 public Result test2(@RequestBody @Valid TestDTO dto{...} //第三種 public Result test3(@RequestBody @Validated(value = {SaveGroup.class}) TestDTO dto) {...}
TestDTO里面會(huì)有一些類似 @NotNull、@NotBlank、@Size等校驗(yàn)注解,這里就不列了。
然后在全局異常攔截那里進(jìn)行統(tǒng)一封裝,使其返回的數(shù)據(jù)結(jié)構(gòu)盡量保持統(tǒng)一,所以一般還得有一個(gè)RestExceptionHandler類。
@ControllerAdvice public class RestExceptionHandler { /** * 處理參數(shù)驗(yàn)證失敗異常 * @param e * @return */ @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseBody @ResponseStatus(HttpStatus.OK) private Response<?> methodArgumentNotValidException(MethodArgumentNotValidException e) { log.warn("MethodArgumentNotValidException", e); FieldError fieldError = e.getBindingResult().getFieldError(); return ResponseUtils.create(CommonCodeEnum.VALIDATE_ERROR.getCode(), CommonCodeEnum.VALIDATE_ERROR.getMessage(), fieldError.getDefaultMessage()); } }
講到常見的數(shù)據(jù)校驗(yàn),那么我們畫風(fēng)一轉(zhuǎn),再回來看需求,可見以上是不滿足需求的,首先,我們的入?yún)⑹且粋€(gè)文件流(指定的Excel模板文件),我們得先解析文件再進(jìn)行數(shù)據(jù)校驗(yàn),合法的放一個(gè)集合,不合法的放另一個(gè)集合;再者,即使入?yún)⑹且粋€(gè)數(shù)組,這種校驗(yàn)一旦不滿足立馬進(jìn)異常處理了,無法返回給前端正確的數(shù)據(jù)結(jié)構(gòu),所以今天就分享解決這類需求的解決方案。
二、解決方法
基礎(chǔ)數(shù)據(jù)
UserExcelVO
import lombok.Data; import java.util.List; /** * */ @Data public class UserExcelVO { /** * 成功列表 */ private List<UserExcel> success; /** * 失敗列表 */ private List<UserExcel> fail; }
UserExcel
import com.alibaba.excel.annotation.ExcelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; import java.io.Serializable; /** * */ @Data @AllArgsConstructor @NoArgsConstructor public class UserExcel implements Serializable { @NotBlank(message = "手機(jī)號(hào)不能為空") @Size(max = 4) @ExcelProperty(value = "用戶名", index = 0) private String name; @ExcelProperty(value = "年齡", index = 1) private Integer age; @Pattern(regexp = "^[1][3,4,5,7,8][0-9]{9}$$", message = "手機(jī)號(hào)不合法") @NotBlank(message = "手機(jī)號(hào)不能為空") @ExcelProperty(value = "手機(jī)號(hào)", index = 2) private String mobile; @ExcelProperty(value = "性別", index = 3) private String sex; }
excel模板數(shù)據(jù):
方案一:大量if-else判斷校驗(yàn)
import com.alibaba.excel.EasyExcel; import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * */ @RestController @RequestMapping("/excel") public class ExcelController { @PostMapping("/importExcel1") public UserExcelVO importExcel(@RequestParam("file") MultipartFile file) { List<UserExcel> list; List<UserExcel> fail = new ArrayList<>(); UserExcelVO userExcelVO = new UserExcelVO(); String mobileReg = "^[1][3,4,5,7,8][0-9]{9}$"; try { list = EasyExcel.read(file.getInputStream(), UserExcel.class, new ModelExcelListener()).sheet().doReadSync(); list.forEach(data -> { // 處理姓名的校驗(yàn) if (StringUtils.isEmpty(data.getName()) || data.getName().length() > 4) { fail.add(data); return; } // 處理手機(jī)號(hào)的校驗(yàn) if (StringUtils.isEmpty(data.getMobile()) || !data.getMobile().matches(mobileReg)) { fail.add(data); return; } // 以下根據(jù)字段多少可能有n個(gè)if... }); userExcelVO.setFail(fail); list.removeAll(fail); userExcelVO.setSuccess(list); } catch (IOException e) { e.printStackTrace(); } return userExcelVO; } }
方案二:請求體加入注解進(jìn)行校驗(yàn)
實(shí)際的業(yè)務(wù)場景,一個(gè)excel里面假如是訂單數(shù)據(jù),最少是幾十個(gè)字段起步的,難道要寫幾十個(gè)if else嗎?方案一明顯是不合理的,因此使用注解的方式幫我們解決。
ValidationUtils
import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; /** * */ public class ValidationUtils { public static Validator getValidator() { return validator; } static Validator validator; static { ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory(); validator = validatorFactory.getValidator(); } }
ModelExcelListener
import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import lombok.extern.slf4j.Slf4j; import java.util.ArrayList; import java.util.List; /** * */ @Slf4j public class ModelExcelListener extends AnalysisEventListener<UserExcel> { private List<UserExcel> datas = new ArrayList<>(); /** * 通過 AnalysisContext 對象還可以獲取當(dāng)前 sheet,當(dāng)前行等數(shù)據(jù) */ @Override public void invoke(UserExcel data, AnalysisContext context) { //數(shù)據(jù)存儲(chǔ)到list,供批量處理,或后續(xù)自己業(yè)務(wù)邏輯處理。 log.info("讀取到數(shù)據(jù){}",data); datas.add(data); //根據(jù)業(yè)務(wù)自行處理,可以寫入數(shù)據(jù)庫等等 } //所有的數(shù)據(jù)解析完了調(diào)用 @Override public void doAfterAllAnalysed(AnalysisContext context) { log.info("所有數(shù)據(jù)解析完成"); } }
請求:
import com.alibaba.excel.EasyExcel; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import javax.validation.ConstraintViolation; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * */ @RestController @RequestMapping("/excel") public class ExcelController { @PostMapping("/importExcel2") public UserExcelVO importExcelV2(@RequestParam("file") MultipartFile file) { List<UserExcel> list; List<UserExcel> fail = new ArrayList<>(); UserExcelVO userExcelVO = new UserExcelVO(); try { list = EasyExcel.read(file.getInputStream(), UserExcel.class, new ModelExcelListener()).sheet().doReadSync(); list.forEach(data -> { Set<ConstraintViolation<UserExcel>> violations = ValidationUtils.getValidator().validate(data); if (violations.size() > 0) { fail.add(data); } }); userExcelVO.setFail(fail); list.removeAll(fail); userExcelVO.setSuccess(list); } catch (IOException e) { e.printStackTrace(); } return userExcelVO; } }
三、測試結(jié)果
方案一的結(jié)果:
{ "success": [ { "name": "張2", "age": 19, "mobile": "13056781235", "sex": "女" }, { "name": "張3", "age": 20, "mobile": "13056781236", "sex": "男" }, { "name": "張4", "age": 21, "mobile": "13056781237", "sex": "女" }, { "name": "張5", "age": 22, "mobile": "13056781238", "sex": "男" }, { "name": "張6", "age": 23, "mobile": "13056781239", "sex": "男" }, { "name": "張7", "age": 24, "mobile": "13056781240", "sex": "男" }, { "name": "張8", "age": 25, "mobile": "13056781241", "sex": "男" }, { "name": "張9", "age": 26, "mobile": "13056781242", "sex": "男" } ], "fail": [ { "name": "張1", "age": 18, "mobile": "3056781234", "sex": "男" }, { "name": "張10", "age": 27, "mobile": "130567812436", "sex": "男" } ] }
方案二的結(jié)果:
{ "success": [ { "name": "張2", "age": 19, "mobile": "13056781235", "sex": "女" }, { "name": "張3", "age": 20, "mobile": "13056781236", "sex": "男" }, { "name": "張4", "age": 21, "mobile": "13056781237", "sex": "女" }, { "name": "張5", "age": 22, "mobile": "13056781238", "sex": "男" }, { "name": "張6", "age": 23, "mobile": "13056781239", "sex": "男" }, { "name": "張7", "age": 24, "mobile": "13056781240", "sex": "男" }, { "name": "張8", "age": 25, "mobile": "13056781241", "sex": "男" }, { "name": "張9", "age": 26, "mobile": "13056781242", "sex": "男" } ], "fail": [ { "name": "張1", "age": 18, "mobile": "3056781234", "sex": "男" }, { "name": "張10", "age": 27, "mobile": "130567812436", "sex": "男" } ] }
發(fā)現(xiàn)兩種方案的測試結(jié)果雖然是一樣的,但是很明顯,方案二更優(yōu)秀。我們后續(xù)寫代碼的時(shí)候,除了做功能,也要考慮代碼的擴(kuò)展性,不然產(chǎn)品說加個(gè)功能,我們又得吭哧吭哧寫代碼了。
以上就是使用Java實(shí)現(xiàn)Excel導(dǎo)入并進(jìn)行數(shù)據(jù)校驗(yàn)的詳細(xì)內(nèi)容,更多關(guān)于Java Excel導(dǎo)入與數(shù)據(jù)校驗(yàn)的資料請關(guān)注腳本之家其它相關(guān)文章!
- Java如何基于EasyExcel實(shí)現(xiàn)導(dǎo)入數(shù)據(jù)校驗(yàn)并生成錯(cuò)誤信息Excel
- Java實(shí)現(xiàn)Excel百萬級(jí)數(shù)據(jù)導(dǎo)入功能的示例代碼
- java批量導(dǎo)入Excel數(shù)據(jù)超詳細(xì)實(shí)例
- Java?Excel數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫的方法
- Java實(shí)現(xiàn)上傳Excel文件并導(dǎo)入數(shù)據(jù)庫
- Java實(shí)現(xiàn)Excel批量導(dǎo)入數(shù)據(jù)
相關(guān)文章
java使用動(dòng)態(tài)代理來實(shí)現(xiàn)AOP(日志記錄)的實(shí)例代碼
AOP(面向方面)的思想,就是把項(xiàng)目共同的那部分功能分離開來,比如日志記錄,避免在業(yè)務(wù)邏輯里面夾雜著跟業(yè)務(wù)邏輯無關(guān)的代碼2013-09-09Java實(shí)現(xiàn)餐廳點(diǎn)餐系統(tǒng)的實(shí)例代碼
這篇文章主要介紹了Java實(shí)現(xiàn)餐廳點(diǎn)餐系統(tǒng),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06Java Spring Security認(rèn)證與授權(quán)及注銷和權(quán)限控制篇綜合解析
Spring Security 是 Spring 家族中的一個(gè)安全管理框架,實(shí)際上,在 Spring Boot 出現(xiàn)之前,Spring Security 就已經(jīng)發(fā)展了多年了,但是使用的并不多,安全管理這個(gè)領(lǐng)域,一直是 Shiro 的天下2021-10-10java中實(shí)現(xiàn)漢字按照拼音排序(示例代碼)
這篇文章主要是對java中將漢字按照拼音排序的實(shí)現(xiàn)代碼進(jìn)行了詳細(xì)的分析介紹。需要的朋友可以過來參考下,希望對大家有所幫助2013-12-12druid ParserException類錯(cuò)誤問題及解決
這篇文章主要介紹了druid ParserException類錯(cuò)誤問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12關(guān)于@SpringBootApplication詳解
這篇文章主要介紹了關(guān)于@SpringBootApplication的使用方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08@Autowired注解注入的xxxMapper報(bào)錯(cuò)問題及解決
這篇文章主要介紹了@Autowired注解注入的xxxMapper報(bào)錯(cuò)問題及解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11詳解Spring Security如何在權(quán)限中使用通配符
小伙伴們知道,在Shiro中,默認(rèn)是支持權(quán)限通配符的?,F(xiàn)在給用戶授權(quán)的時(shí)候,可以一個(gè)權(quán)限一個(gè)權(quán)限的配置,也可以直接用通配符。本文將介紹Spring Security如何在權(quán)限中使用通配符,需要的可以參考一下2022-06-06