今日内容
- 出货表的导入导出
- 使用模板导出出货表
- 百万数据报表技术
- EasyPOI
- 定时任务
第一章 出货表导出(练习)
1. 跳转出货表页面
在ContractController
中添加下面方法
/**
* 跳转出货表页面
*/
@RequestMapping(value = "/print", name = "跳转出货表页面")
public String print() {
return "/cargo/print/contract-print";
}
2. 需求分析
2.1 需求说明
要求可以按照按照条件导出指定月份的出货表
2.2 sql语句分析
SELECT c.`custom_name` AS customName,
c.`contract_no` AS contractNo,
cp.`product_no` AS productNo,
cp.`cnumber` AS cnumber,
cp.`factory_name` AS factoryName,
c.`delivery_period` AS deliveryPeriod,
c.`ship_time` AS shipTime,
c.`trade_terms` AS tradeTerms
FROM `co_contract` c
JOIN `co_contract_product` cp ON c.id = cp.`contract_id`
WHERE DATE_FORMAT(c.`ship_time`,"%Y-%m") = '2015-01'
AND c.`company_id` = '1'
3. 基础代码
3.1 ContractProductVo 实体类
导入 :export_domain\src\main\java\com\itheima\vo\ContractProductVo.java
@Data
public class ContractProductVo implements Serializable {
private String customName; //客户名称
private String contractNo; //合同号,订单号
private String productNo; //货号
private Integer cnumber; //数量
private String factoryName; //厂家名称
private Date deliveryPeriod; //交货期限
private Date shipTime; //船期
private String tradeTerms; //贸易条款
}
3.2 DownloadUtil
导入工具类 export_commons\src\main\java\com\itheima\utils\DownloadUtil.java
package com.itheima.utils;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class DownloadUtil {
/**
* @param filePath 要下载的文件路径
* @param returnName 返回的文件名
* @param response HttpServletResponse
* @param delFlag 是否删除文件
*/
protected static void download(String filePath, String returnName, HttpServletResponse response, boolean delFlag) {
prototypeDownload(new File(filePath), returnName, response, delFlag);
}
/**
* @param file 要下载的文件
* @param returnName 返回的文件名
* @param response HttpServletResponse
* @param delFlag 是否删除文件
*/
protected static void download(File file, String returnName, HttpServletResponse response, boolean delFlag) {
prototypeDownload(file, returnName, response, delFlag);
}
/**
* @param file 要下载的文件
* @param returnName 返回的文件名
* @param response HttpServletResponse
* @param delFlag 是否删除文件
*/
public static void prototypeDownload(File file, String returnName, HttpServletResponse response, boolean delFlag) {
// 下载文件
FileInputStream inputStream = null;
ServletOutputStream outputStream = null;
try {
if (!file.exists()) return;
response.reset();
//设置响应类型 PDF文件为"application/pdf",WORD文件为:"application/msword", EXCEL文件为:"application/vnd.ms-excel"。
response.setContentType("application/octet-stream;charset=utf-8");
//设置响应的文件名称,并转换成中文编码
//returnName = URLEncoder.encode(returnName,"UTF-8");
//保存的文件名,必须和页面编码一致,否则乱码
returnName = response.encodeURL(new String(returnName.getBytes(), "iso8859-1"));
//attachment作为附件下载;inline客户端机器有安装匹配程序,则直接打开;注意改变配置,清除缓存,否则可能不能看到效果
response.addHeader("Content-Disposition", "attachment;filename=" + returnName);
//将文件读入响应流
inputStream = new FileInputStream(file);
outputStream = response.getOutputStream();
int length = 1024;
int readLength = 0;
byte buf[] = new byte[1024];
readLength = inputStream.read(buf, 0, length);
while (readLength != -1) {
outputStream.write(buf, 0, readLength);
readLength = inputStream.read(buf, 0, length);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
//删除原文件
if (delFlag) {
file.delete();
}
}
}
public static void download(ByteArrayOutputStream byteArrayOutputStream, HttpServletResponse response, String returnName) throws IOException {
response.setContentType("application/octet-stream;charset=utf-8");
//保存的文件名,必须和页面编码一致,否则乱码
returnName = response.encodeURL(new String(returnName.getBytes(), "iso8859-1"));
response.addHeader("Content-Disposition", "attachment;filename=" + returnName);
response.setContentLength(byteArrayOutputStream.size());
//取得输出流
ServletOutputStream outputstream = response.getOutputStream();
//写到输出流
byteArrayOutputStream.writeTo(outputstream);
//关闭
byteArrayOutputStream.close();
//刷数据
outputstream.flush();
}
}
4. 代码编写
4.1 ContractPrintController
创建一个新的Controller文件,编写关于printExcel相关的
package com.itheima.web.controller.cargo;
import com.alibaba.dubbo.config.annotation.Reference;
import com.itheima.service.cargo.ContractService;
import com.itheima.utils.DownloadUtil;
import com.itheima.vo.ContractProductVo;
import com.itheima.web.controller.BaseController;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.List;
@Controller
@RequestMapping("/cargo/contract")
public class ContractPrintController extends BaseController {
@Reference
private ContractService contractService;
/**
* 跳转出货表页面
*/
@RequestMapping(value = "/print", name = "跳转出货表页面")
public String print() {
return "/cargo/print/contract-print";
}
/**
* 导出商品
*/
@RequestMapping(value = "/printExcel", name = "导出商品")
public void printExcel(String inputDate) throws IOException {
// 根据传输的参数查询数据库
List<ContractProductVo> list=contractService.findContractProductVo(inputDate,getCompanyId());
// 创建一个工作簿
Workbook workbook = new XSSFWorkbook();
// 根据工作簿创建一个工作表
Sheet sheet = workbook.createSheet();
/**
* @author mryhl
* 合并单元格
* CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol)
*/
sheet.addMergedRegion(new CellRangeAddress(0,0,1,8));
// 设置列宽
for (int i = 1; i < 9; i++) {
sheet.setColumnWidth(i,15 * 256);
}
// 创建第0行
Row row0 = sheet.createRow(0);
for (int i = 1; i < 9; i++) {
Cell cell = row0.createCell(i);
// 添加样式
cell.setCellStyle(bigTitleStyle(workbook));
}
// 替换出入文件的格式
String s = inputDate.replaceAll("-0", "年").replaceAll("-", "年") + "月份出货表";
// 设置第一行的内容
row0.getCell(1).setCellValue(s);
// 创建数组,保存字段名
String[] objs = {"客户","合同号","货号","数量","工厂","工厂交期","船期","贸易条款"};
// 创建第1行
Row row1 = sheet.createRow(1);
for (int i = 1; i < 9; i++) {
Cell cell = row1.createCell(i);
// 出入数据
cell.setCellValue(objs[i-1]);
// 添加样式
cell.setCellStyle(littleTitleStyle(workbook));
}
// 使用查询到的数据创建第n行
int n = 2;
for (ContractProductVo contractProductVo : list) {
// 使用行创建单元格
Row row = sheet.createRow(n++);
for (int i = 1; i < 9; i++) {
Cell cell = row.createCell(i);
// 添加样式
cell.setCellStyle(textStyle(workbook));
}
// 添加数据
row.getCell(1).setCellValue(contractProductVo.getCustomName());
row.getCell(2).setCellValue(contractProductVo.getContractNo());
row.getCell(3).setCellValue(contractProductVo.getProductNo());
row.getCell(4).setCellValue(contractProductVo.getCnumber());
row.getCell(5).setCellValue(contractProductVo.getFactoryName());
row.getCell(6).setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(contractProductVo.getDeliveryPeriod()));
row.getCell(7).setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(contractProductVo.getShipTime()));
row.getCell(8).setCellValue(contractProductVo.getTradeTerms());
}
// 文件下载
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
workbook.write(outputStream);
DownloadUtil.download(outputStream, response, "出货表.xlsx");
}
//大标题的样式
public CellStyle bigTitleStyle(Workbook wb) {
CellStyle style = wb.createCellStyle();
Font font = wb.createFont();
font.setFontName("宋体");
font.setFontHeightInPoints((short) 16);
font.setBold(true);//字体加粗
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER); //横向居中
style.setVerticalAlignment(VerticalAlignment.CENTER); //纵向居中
return style;
}
//小标题的样式
public CellStyle littleTitleStyle(Workbook wb) {
CellStyle style = wb.createCellStyle();
Font font = wb.createFont();
font.setFontName("黑体");
font.setFontHeightInPoints((short) 12);
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER); //横向居中
style.setVerticalAlignment(VerticalAlignment.CENTER); //纵向居中
style.setBorderTop(BorderStyle.THIN); //上细线
style.setBorderBottom(BorderStyle.THIN); //下细线
style.setBorderLeft(BorderStyle.THIN); //左细线
style.setBorderRight(BorderStyle.THIN); //右细线
return style;
}
//文字样式
public CellStyle textStyle(Workbook wb) {
CellStyle style = wb.createCellStyle();
Font font = wb.createFont();
font.setFontName("Times New Roman");
font.setFontHeightInPoints((short) 10);
style.setFont(font);
style.setAlignment(HorizontalAlignment.LEFT); //横向居左
style.setVerticalAlignment(VerticalAlignment.CENTER); //纵向居中
style.setBorderTop(BorderStyle.THIN); //上细线
style.setBorderBottom(BorderStyle.THIN); //下细线
style.setBorderLeft(BorderStyle.THIN); //左细线
style.setBorderRight(BorderStyle.THIN); //右细线
return style;
}
}
4.2 ContractService
List<ContractProductVo> findContractProductVo(String inputDate, String companyId);
@Override
public List<ContractProductVo> findContractProductVo(String inputDate, String companyId) {
return contractDao.findContractProductVo(inputDate, companyId);
}
4.3 ContractDao
List<ContractProductVo> findContractProductVo(@Param("inputDate") String inputDate, @Param("companyId") String companyId);
<select id="findContractProductVo" resultType="com.itheima.vo.ContractProductVo">
SELECT c.`custom_name` AS customName,
c.`contract_no` AS contractNo,
cp.`product_no` AS productNo,
cp.`cnumber` AS cnumber,
cp.`factory_name` AS factoryName,
c.`delivery_period` AS deliveryPeriod,
c.`ship_time` AS shipTime,
c.`trade_terms` AS tradeTerms
FROM `co_contract` c
JOIN `co_contract_product` cp ON c.id = cp.`contract_id`
WHERE DATE_FORMAT(c.`ship_time`,"%Y-%m") = #{inputDate}
AND c.`company_id` = #{companyId}
</select>
第二章 使用模板导出出货表(重点)
1. 需求说明
自定义生成Excel报表文件是非常复杂的,特别是针对复杂报表头,单元格样式,字体等操作。
为了简便,我们可以使用已经准备好的Excel模板,将模板读到程序中, 然后设置上数据即可。
2. 步骤分析
操作步骤:
-
制作模版文件(一般是由设计人员或者项目经理完成)
-
加载模版文件,从而得到一个工作簿
-
读取工作表
-
读取行,此时发现其实表中的前两行是固定内容, 所以不用处理
-
直接读取第三行中的每个单元格的样式信息,存储在一个数组中,备用
-
接下来开始遍历创建行,设置每行中单元格的文字和样式(使用上步读到的样式)
3. 代码编写
3.1 jsp页面调整
修改\webapp\WEB-INF\pages\cargo\print\contract-print.jsp
中的内容
3.1.1 第一步:复制一个form表单
<form role="form" action="/cargo/contract/printExcelWithTemplate.do">
<div class="input-group input-group-sm" >
<div class="input-group-addon">
<i class="fa fa-calendar"></i>
</div>
<input type="text" name="inputDate" class="form-control pull-right" id="datepicker1">
<span class="input-group-btn">
<button type="submit" class="btn btn-info btn-flat">模板导出</button>
</span>
</div>
</form>
3.1.2第二步:在js中添加
$('#datepicker1').datepicker({
language: "zh-CN",
autoclose: true,
format: 'yyyy-mm',
startView: 'months', //开始视图层,为月视图层
maxViewMode:'years', //最大视图层,为年视图层
minViewMode:'months', //最小视图层,为月视图层
});
3.1.3页面效果如下:
3.2 ContractController
/**
* 按照模板导出商品
*/
@RequestMapping(value = "/printExcelWithTemplate", name = "按照模板导出商品")
public void printExcelWithTemplate(String inputDate) throws IOException {
// 根据传输的参数查询数据库
List<ContractProductVo> list=contractService.findContractProductVo(inputDate,getCompanyId());
// 读取模板
String realPath = session.getServletContext().getRealPath("/make/xlsprint/tOUTPRODUCT.xlsx");
// 通过读入模板文件创建一个工作簿
Workbook workbook = new XSSFWorkbook(new FileInputStream(realPath));
// 根据工作簿创建一个工作表
Sheet sheet = workbook.getSheetAt(0);
// 替换出入文件的格式
String s = inputDate.replaceAll("-0", "年").replaceAll("-", "年") + "月份出货表";
// 设置第一行的内容
sheet.getRow(0).getCell(1).setCellValue(s);
// 读取第2行
Row row2 = sheet.getRow(2);
// 创建样式数组
CellStyle[] cellStyles = new CellStyle[8];
// 向数组中写入数据
for (int i = 0; i < 8; i++) {
cellStyles[i] = row2.getCell(i+ 1).getCellStyle();
}
// 使用查询到的数据创建第n行
int n = 2;
for (ContractProductVo contractProductVo : list) {
// 使用行创建单元格
Row row = sheet.createRow(n++);
for (int i = 1; i < 9; i++) {
Cell cell = row.createCell(i);
// 添加样式
cell.setCellStyle(textStyle(workbook));
cell.setCellStyle(cellStyles[i-1]);
}
// 添加数据
row.getCell(1).setCellValue(contractProductVo.getCustomName());
row.getCell(2).setCellValue(contractProductVo.getContractNo());
row.getCell(3).setCellValue(contractProductVo.getProductNo());
row.getCell(4).setCellValue(contractProductVo.getCnumber());
row.getCell(5).setCellValue(contractProductVo.getFactoryName());
row.getCell(6).setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(contractProductVo.getDeliveryPeriod()));
row.getCell(7).setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(contractProductVo.getShipTime()));
row.getCell(8).setCellValue(contractProductVo.getTradeTerms());
}
// 文件下载
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
workbook.write(outputStream);
DownloadUtil.download(outputStream, response, "出货表.xlsx");
}
4. 效果展示
第三章 百万级别数据报表技术(了解 面试)
1. 概述
对于百万数据量的Excel导入导出,只讨论基于Excel2007的解决方法。Excel2007的数据结构是XML,操作XML有两种模式:
- 用户模式:基于dom4j的解析, 一次性加载xml文件再解析(XSSF对象底层用的就是dom4j,它的缺点是当数据量过大时,会导致内存溢出)
- 事件模式:基于SAX方式解析XML,逐行加载,逐行解析(SXSSF对象底层用的是SAX,它的缺点是不能使用模板,并且不支持过多的CellStyle[62000])
2. Jvisualvm
Jvisualvm是JDK自带的一个Java程序性能检测工具。基于它可以监视程序的运行情况,包括CUP,垃圾回收,内存的分配和使用情况。
此软件放在jdk的安装目录下的bin目录下,名字叫jvisualvm.exe。
双击即可启动图形化检测界面,找到我们的tomcat,点击就可以看到其运行详情。
概述:可以看到进程的启动参数。
监视:左上:cpu利用率,gc状态的监控,右上:堆利用率,永久内存区的利用率,左下:类的监控,右下:线程的监控
线程:能够显示线程的名称和运行的状态,在调试多线程时必不可少,而且可以点进一个线程查看这个线程的详细运行情况
3. 百万级别数据导出
3.1 jsp页面调整
3.1.1第一步:复制一个form表单
<form role="form" action="/cargo/contract/printExcelMillion.do">
<div class="input-group input-group-sm" >
<div class="input-group-addon">
<i class="fa fa-calendar"></i>
</div>
<input type="text" name="inputDate" class="form-control pull-right" id="datepicker2">
<span class="input-group-btn">
<button type="submit" class="btn btn-info btn-flat">百万级别数据导出</button>
</span>
</div>
</form>
3.1.2 第二步:在js中添加
$('#datepicker2').datepicker({
language: "zh-CN",
autoclose: true,
format: 'yyyy-mm',
startView: 'months', //开始视图层,为月视图层
maxViewMode:'years', //最大视图层,为年视图层
minViewMode:'months', //最小视图层,为月视图层
});
3.1.3页面效果如下:
3.2 ContractController代码调整
修改内容:
-
new XSSFWorkbook(); —>改为 new SXSSFWorkbook();
-
导出数据那里模拟一个8000次的循环
-
去掉所有样式设置
@RequestMapping(value = "/printExcelMillion", name = "出货表导出") public void printExcelMillion(String inputDate) throws IOException { //1. 根据输入的参数查询货物列表 List<ContractProductVo> list = contractService.findContractProductVo(inputDate, getCompanyId()); //2. 封装列表为一个工作簿 //2-1) 创建一个工作簿 Workbook workbook = new SXSSFWorkbook(); //2-2) 使用工作簿创建工作表 Sheet sheet = workbook.createSheet(); sheet.addMergedRegion(new CellRangeAddress(0, 0, 1, 8));//合并单元格 for (int i = 1; i < 9; i++) { sheet.setColumnWidth(i, 15 * 256);//设置列宽 } //2-3) 使用工作表创建第0行 Row row0 = sheet.createRow(0); for (int i = 1; i < 9; i++) { Cell cell = row0.createCell(i); } String title = inputDate.replaceAll("-0", "年").replaceAll("-", "年"); row0.getCell(1).setCellValue(title + "月份出货表"); //2-4) 使用工作表创建第1行 Row row1 = sheet.createRow(1); for (int i = 1; i < 9; i++) { Cell cell = row1.createCell(i); } row1.getCell(1).setCellValue("客户"); row1.getCell(2).setCellValue("合同号"); row1.getCell(3).setCellValue("货号"); row1.getCell(4).setCellValue("数量"); row1.getCell(5).setCellValue("工厂"); row1.getCell(6).setCellValue("工厂交期"); row1.getCell(7).setCellValue("船期"); row1.getCell(8).setCellValue("贸易条款"); //2-5) 使用工作表创建第n行 int n = 2; for (ContractProductVo contractProductVo : list) { for (int x = 0; x < 8000; x++) { //2-6) 使用行创建单元格 Row rown = sheet.createRow(n++); for (int i = 1; i < 9; i++) { Cell cell = rown.createCell(i); } //2-7) 向单元格中设置数据(样式) rown.getCell(1).setCellValue(contractProductVo.getCustomName()); rown.getCell(2).setCellValue(contractProductVo.getContractNo()); rown.getCell(3).setCellValue(contractProductVo.getProductNo()); rown.getCell(4).setCellValue(contractProductVo.getCnumber()); rown.getCell(5).setCellValue(contractProductVo.getFactoryName()); rown.getCell(6).setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(contractProductVo.getDeliveryPeriod())); rown.getCell(7).setCellValue(new SimpleDateFormat("yyyy-MM-dd").format(contractProductVo.getShipTime())); rown.getCell(8).setCellValue(contractProductVo.getTradeTerms()); } } //3. 文件下载 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); workbook.write(outputStream); DownloadUtil.download(outputStream, response, "出货表.xlsx"); }
4. 百万级别数据导入
4.1 自定义处理器(直接复制)
package com.itheima.poi;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import java.io.InputStream;
//用于处理百万级别数据
//需要我们做的是在SheetHandler中自定义处理逻辑
public class ExcelParse {
public static void parse(String path) throws Exception {
//解析器
SheetHandler hl = new SheetHandler();
//1.根据 Excel 获取 OPCPackage 对象
OPCPackage pkg = OPCPackage.open(path, PackageAccess.READ);
try {
//2.创建 XSSFReader 对象
XSSFReader reader = new XSSFReader(pkg);
//3.获取 SharedStringsTable 对象
SharedStringsTable sst = reader.getSharedStringsTable();
//4.获取 StylesTable 对象
StylesTable styles = reader.getStylesTable();
XMLReader parser = XMLReaderFactory.createXMLReader();
// 处理公共属性
parser.setContentHandler(new XSSFSheetXMLHandler(styles, sst, hl, false));
XSSFReader.SheetIterator sheets = (XSSFReader.SheetIterator) reader.getSheetsData();
//逐行读取逐行解析
while (sheets.hasNext()) {
InputStream sheetstream = sheets.next();
InputSource sheetSource = new InputSource(sheetstream);
try {
parser.parse(sheetSource);
} finally {
sheetstream.close();
}
}
} finally {
pkg.close();
}
}
public static void main(String[] args) throws Exception {
parse("c:/upload/data.xlsx");
}
}
4.2 自定义解析器
package com.itheima.poi;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.usermodel.XSSFComment;
import java.util.ArrayList;
import java.util.List;
//自定义解析器,负责每行代码的处理
public class SheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {
ContractProductVo contractProductVo = null;
List<ContractProductVo> list = new ArrayList<ContractProductVo>();
//rowIndex 行索引
public void startRow(int rowIndex) {
System.out.println("进入了第" + rowIndex + "行");
contractProductVo = new ContractProductVo();
}
//每个单元格中要做的操作
//cellName 单元格名称 C2
//cellValue 单元格中的值
//xssfComment 单元格批注
public void cell(String cellName, String cellValue, XSSFComment xssfComment) {
System.out.print(cellName + ":" + cellValue+" ");
if (cellName.startsWith("B")){
contractProductVo.setCustomName(cellValue);
}else if (cellName.startsWith("C")){
contractProductVo.setContractNo(cellValue);
}
}
public void endRow(int rowIndex) {
System.out.println("离开了第" + rowIndex + "行");
//
list.add(contractProductVo);
System.out.println(list);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
第四章 EasyPOI(扩展)
1. 简介
easypoi主打的功能就是容易,让一个没见接触过poi的人员,就可以方便的写出Excel导出和导入功能,它通过简单的注解和模板语言(熟悉的表达式语法),完成以前复杂的写法。
2. 快速入门
2.1 环境搭建
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>3.2.0</version>
</dependency>
2.2 定义实体
public class ContractProductVo implements Serializable {
@Excel(name = "客户")
private String customName; //客户名称
@Excel(name = "合同号")
private String contractNo; //合同号,订单号
@Excel(name = "货号")
private String productNo; //货号
@Excel(name = "数量",type = 10)
private Integer cnumber; //数量
@Excel(name = "工厂")
private String factoryName; //厂家名称
@Excel(name = "工厂交期", format = "yyyy-MM-dd",width = 15)
private Date deliveryPeriod; //交货期限
@Excel(name = "船期", format = "yyyy-MM-dd",width = 15)
private Date shipTime; //船期
@Excel(name = "贸易条款")
private String tradeTerms; //贸易条款
public String getCustomName() {
return customName;
}
public void setCustomName(String customName) {
this.customName = customName;
}
public String getContractNo() {
return contractNo;
}
public void setContractNo(String contractNo) {
this.contractNo = contractNo;
}
public String getProductNo() {
return productNo;
}
public void setProductNo(String productNo) {
this.productNo = productNo;
}
public Integer getCnumber() {
return cnumber;
}
public void setCnumber(Integer cnumber) {
this.cnumber = cnumber;
}
public String getFactoryName() {
return factoryName;
}
public void setFactoryName(String factoryName) {
this.factoryName = factoryName;
}
public Date getDeliveryPeriod() {
return deliveryPeriod;
}
public void setDeliveryPeriod(Date deliveryPeriod) {
this.deliveryPeriod = deliveryPeriod;
}
public Date getShipTime() {
return shipTime;
}
public void setShipTime(Date shipTime) {
this.shipTime = shipTime;
}
public String getTradeTerms() {
return tradeTerms;
}
public void setTradeTerms(String tradeTerms) {
this.tradeTerms = tradeTerms;
}
@Override
public String toString() {
return "ContractProductVo{" +
"customName='" + customName + '\'' +
", contractNo='" + contractNo + '\'' +
", productNo='" + productNo + '\'' +
", cnumber=" + cnumber +
", factoryName='" + factoryName + '\'' +
", deliveryPeriod=" + deliveryPeriod +
", shipTime=" + shipTime +
", tradeTerms='" + tradeTerms + '\'' +
'}';
}
}
2.3 创建excel
public class CreateExcel {
public static void main(String[] args)throws Exception {
// 1.定义excel配置
ExportParams exportParams = new ExportParams();
exportParams.setSheetName("Sheet1");// 页名称
exportParams.setTitle("2015年01月份出货表");// 大标题
exportParams.setType(ExcelType.XSSF);// 07及以上版本
// 2.创建workbook对象
Workbook wb = ExcelExportUtil.exportExcel(exportParams, ContractProductVo.class, getData());
// 3.导出到文件
FileOutputStream out = new FileOutputStream("d:/demo.xlsx");
wb.write(out);
wb.close();
}
//从数据库中查找了数据
public static List<ContractProductVo> getData() {
List<ContractProductVo> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
ContractProductVo vo = new ContractProductVo();
vo.setCustomName("客户"+i);
vo.setContractNo("合同号"+i);
vo.setProductNo("货号"+i);
vo.setCnumber(i);
vo.setFactoryName("工厂"+i);
vo.setDeliveryPeriod(new Date());
vo.setShipTime(new Date());
vo.setTradeTerms("条款"+i);
list.add(vo);
}
return list;
}
}
2.4 解析excel
public class ParseExcel {
public static void main(String[] args) {
// 1.解析excel的配置参数
ImportParams params = new ImportParams();
params.setTitleRows(1); // 大标题行数
params.setHeadRows(1); // 列标题行数
// 2.实现解析
List<ContractProductVo> list = ExcelImportUtil.importExcel(new File("D:/demo.xlsx"), ContractProductVo.class, params);
// 3.遍历输出
for (ContractProductVo contractProductVo : list) {
System.out.println(contractProductVo);
}
}
}
第五章 定时任务(重点)
1. 简介
定时任务:按照配置的时间规则,自动执行java类中的方法
实现框架:Quartz、Spring Task
应用场景:
- 某些网站会定时发送优惠邮件
- 银行系统还款日信用卡催收款短信
- 某些应用的生日祝福短信等
Quartz 是一个完全由 Java 编写的开源任务调度框架,为在 Java 应用程序中进行任务调度提供了简单却强大的机制。
官网地址:http://www.quartz-scheduler.org/
2. 核心组件
Quartz 定时任务调度框架(按照配置的时间规则,自动执行java类中的方法)
-
Job:需要执行的java类
-
JobDetail:任务细节,用来指定java类和具体执行的方法
-
Trigger:触发器,配置时间规则和JobDetail
-
Scheduler:调度器,统一调度并管理所有的Trigger
3. 快速入门
需求:每间隔五秒,向控制台输出当前时间
3.1 创建一个新工程,导入依赖
<dependencies>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 设置编译版本为1.8 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
3.2 创建Job类,创建JobDetail方法
import java.util.Date;
/**
* @description:
* @author: mryhl
* @date: Created in 2020/10/18 17:36
* @version:
*/
public class myjob {
public void sendMail(){
System.out.println("发邮件了 " + new Date().toLocaleString());
}
}
3.3 添加一个配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--1.配置Job:自定义java类 -->
<bean id="myjob" class="myjob"></bean>
<!--2.配置JobDetail:执行任务来的方法-->
<bean id="myJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<!--1.确定任务类-->
<property name="targetObject" ref="myjob"/>
<!--2.确认任务的方法-->
<property name="targetMethod" value="sendMail"></property>
</bean>
<!--3.配置Trigger:根据时间规则,触发方法执行-->
<bean id="myTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<!--1.指定时间规则-->
<property name="cronExpression" value="*/5 * * * * ?"></property>
<!--2.指定JobDetail-->
<property name="jobDetail" ref="myJobDetail"></property>
</bean>
<!--4.配置Scheduler:统一管理配置trigger-->
<bean id="myScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<array>
<ref bean="myTrigger"></ref>
</array>
</property>
</bean>
</beans>
3.4 测试
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
/**
* @description:
* @author: mryhl
* @date: Created in 2020/10/18 17:40
* @version:
*/
public class QuartzStart {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext act
= new ClassPathXmlApplicationContext("applicationContext-quartz.xml");
act.start();
System.in.read();
}
}
4. Cron表达式【重点】(7子)
Cron表达式简单来说就是一个字符串,有如下两种语法格式:
- Seconds Minutes Hours DayofMonth Month DayofWeek Year(七个域)
- Seconds Minutes Hours DayofMonth Month DayofWeek(六个域)
cron 时间表达式(配置时间规则)
秒 分 时 日 月 周 年
0-59 0-59 0-23 1-31 1-12 1-7 1970-2099
*/5 * * 5W * 3L *
常用的符号
*:表示任意,例如在Minutes域使用,即表示每分钟都会触发
?:表示忽略,只能用在DayofMonth和DayofWeek两个域
-:表示范围,例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次
/:表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用5/20,则意味着在5分钟触发一次,而25,45等分别触发一次.
,:表示列出枚举值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。
L:表示最后,只能出现在DayofWeek和DayofMonth域。如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。
W: 表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。
例如:在DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。
如果5日是星期天,则在6日(周一)触发;如果5日在星期一到星期五中的一天,则就在5日触发。 另外一点,W的最近寻找不会跨过月份。
LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。
#:用于确定第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。
常用表达式例子
0 0 2 1 * ? * 表示在每月的1日的凌晨2点调整任务
0 15 10 ? * MON-FRI 表示周一到周五每天上午10:15执行作业
0 15 10 ? * 6L 2002-2006 表示2002-2006年的每个月的最后一个星期五上午10:15执行作
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
0 0 12 ? * WED 表示每个星期三中午12点
0 0 12 * * ? 每天中午12点触发
0 15 10 ? * * 每天上午10:15触发
0 15 10 * * ? 每天上午10:15触发
0 15 10 * * ? * 每天上午10:15触发
0 15 10 * * ? 2005 2005年的每天上午10:15触发
0 * 14 * * ? 在每天下午2点到下午2:59期间的每1分钟触发
0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发
0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发
0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发
0 15 10 ? * MON-FRI 周一至周五的上午10:15触发
0 15 10 15 * ? 每月15日上午10:15触发
0 15 10 L * ? 每月最后一日的上午10:15触发
0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发
0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发