2019独角兽企业重金招聘Python工程师标准>>>
环境:
jdk1.8+poi-3.17+mysql-5.6+excel2010(xlsx)
工具类:
1.excel解析工具类,使用poi sax模式解析excel。生成数据格式为List>
private List> results&#61;new ArrayList<>();
public void process(LinkedHashMap
if(headerMap.isEmpty()){
for(Map.Entry
if(null !&#61; e.getKey() && null !&#61; e.getValue()){
headerMap.put(e.getKey(), e.getValue().toLowerCase());
}
}
}else{
LinkedHashMap
for (Map.Entry
String key &#61; e.getValue();
String value &#61; null&#61;&#61;dataMap.get(e.getKey())?"":dataMap.get(e.getKey());
data.put(key, value);
}
count.getAndIncrement();
results.add(data);
}
}
2.数据库的excel的配置信息t_fields--可以配置多个excel
fname--excel标题字段
fcode--标题字段对应的数据库字段
data_type--建表字段信息
ftable--excel对应的数据表
3.excel配置表实体类---jpa
package com.fdrx.model;
import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
/**
* Created by shea on 2019-06-05.
*/
&#64;Data
&#64;Entity
&#64;Table(name &#61;"t_fields")
public class ExcelBean implements Serializable {
&#64;Id
&#64;GeneratedValue(strategy&#61; GenerationType.IDENTITY)
private Integer id;
&#64;Column(length &#61; 10)
private String fcode;
&#64;Column(length &#61; 10)
private String fname;
&#64;Column(length &#61; 20)
private String dataType;
&#64;Column(length &#61; 10)
private String ftable;
}
package com.fdrx.dao;
import com.fdrx.model.ExcelBean;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* Created by shea on 2019-06-05.
*/
&#64;Repository
public interface ExcelBeanDao extends JpaRepository
}
4.excel工具导入服务类
package com.fdrx.service;
import com.fdrx.dao.ExcelBeanDao;
import com.fdrx.model.ExcelBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;
/**
* Created by shea on 2019-06-06.
*/
&#64;Service
&#64;Slf4j
public class ExcelPipeline {
&#64;Resource
private ExcelBeanDao excelBeanDao;
private HashMap
/**
* 写入数据库
* &#64;param datas excel解析数据
* &#64;param table 目标表
* &#64;return
*/
public void saveData(List> datas,String table){
//1.获取配置文件
List
//2.根据表 获取对应字段映射关系
allFields&#61;all.stream().filter(m -> table.equals(m.getFtable()))
.collect(Collectors.toMap(ExcelBean::getFname,ExcelBean::getFcode,(k1, k2)->k2,LinkedHashMap::new));
//3.根据数据库配置信息,生成建表sql
String collect &#61; all.stream().filter(m -> table.equals(m.getFtable()))
.map(m -> String.format("&#96;%s&#96; %s comment &#39;%s&#39;",m.getFcode(), StringUtils.isEmpty(m.getDataType())?"text":m.getDataType(),m.getFname()))
.collect(Collectors.joining(","));
String createSql &#61; String.format("create table IF NOT Exists %s (%s)ENGINE&#61;InnoDB DEFAULT CHARSET&#61;utf8mb4", table,collect);
//4.根据excel标题 获取导入字段
List
//5.根据导入字段&#xff0c;生成对应的导入数据列表
List> importLists &#61; convert2List(datas,title);
//6.根据导入字段&#xff0c;生成导入语句
String inserSql &#61; createSql(title, table);
//7.开始msql导入
Connection con&#61;null;
try {
con &#61; getConnector();//jdbc链接
con.setAutoCommit(false);//不自动提交
//指定建表语句&#xff0c;存在则不创建
executeUpdate(createSql,new ArrayList<>(),con);
for (List
try {
executeUpdate(inserSql, strings, con);
} catch (SQLException e) {
log.error("{}&#61;》写入失败&#xff01;",strings.get(0));
}
}
} catch (Exception e) {
log.error(e.getMessage());
}finally {
try {
if(con!&#61;null){
con.setAutoCommit(true);
con.close();
}
} catch (SQLException e) {
log.error(e.getMessage());
}
}
log.info("导入完成");
}
/**
* 执行 sql 更新
* &#64;param sql sql
* &#64;param params params
* &#64;param conn conn
* &#64;return
* &#64;throws SQLException
*/
private void executeUpdate(String sql, List
try (PreparedStatement ps &#61; conn.prepareStatement(sql)){
int paramIdx &#61; 1;
for(Object obj: params){
ps.setObject(paramIdx, obj);
paramIdx &#43;&#43;;
}
ps.executeUpdate();
}
}
/**
* 根据excel数据生成插入语句
*/
private String createSql( List
Optional
.map(str -> String.format("&#96;%s&#96;", allFields.get(str.trim())))
.filter(s->!"".equals(s)&& null!&#61;s)
.reduce((a, b) -> String.format("%s,%s", a, b));
Optional
.map(str -> String.format("&#96;%s&#96;", allFields.get(str.trim())))
.filter(s->!"".equals(s)&& null!&#61;s)
.map(str->"?").reduce((a, b) -> String.format("%s,%s", a, b));
String sql &#61; String.format("replace into &#96;%s&#96;(%s) values(%s)",table, sqlField.orElse(""), sqlValue.orElse(""));
return sql;
}
/**
* 映射excel标题行 与 数据库配置信息&#61;&#61;&#61;》确定要导入的数据列
* &#64;param headers
* &#64;return
*/
private List
return headers.keySet().stream()
.filter(str -> allFields.get(str.trim())!&#61;null)
.collect(Collectors.toList());
}
/**
* 转换数据list
* &#64;param datas excel数据
* &#64;param title 导入字段列表
* &#64;return
*/
private List> convert2List(List> datas,List
List> collect &#61; datas.stream().map(data -> {
List
return single;
}).collect(Collectors.toList());
return collect;
}
/**
* jdbc
* &#64;return
* &#64;throws SQLException
*/
public Connection getConnector() throws SQLException {
Connection conn &#61; null;
String url &#61; "jdbc:mysql://localhost:3306/weixin?useUnicode&#61;true&characterEncoding&#61;utf-8";
String user &#61; "root";
String passwd &#61; "root";
try {
Class.forName("com.mysql.jdbc.Driver");
conn &#61; DriverManager.getConnection(url, user, passwd);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return conn;
}
}
5.测试
package com.fdrx.demo;
import com.fdrx.Application;
import com.fdrx.service.ExcelPipeline;
import com.fdrx.service.JdbcPipeline;
import com.fdrx.util.excel.Excel2007Parser;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.List;
/**
* Created by shea on 2019-06-05.
*/
&#64;RunWith(SpringRunner.class)
&#64;SpringBootTest(classes &#61; Application.class)//如果需要注入服务&#xff0c;则要启
&#64;Slf4j
public class TestDemo {
&#64;Resource
private JdbcPipeline jdbcPipeline;
&#64;Resource
private ExcelPipeline excelPipeline;
&#64;Test
public void test1()throws Exception{
String table &#61;"t_excel2";
InputStream in &#61; new FileInputStream("C:\\Users\\shea\\Desktop\\excel2.xlsx");
//调用事件驱动解析excel
Excel2007Parser parser &#61; new Excel2007Parser(in);
parser.parse();
//解析结果
List> datas &#61; parser.getResults();
excelPipeline.saveData(datas,table);
}
}
excel导入都可以通过只配置mysql数据表中的excel配置表t_fields即可。
再导入excel的服务类中指定导入excel对应的ftable 即可&#xff0c;程序会自动创建对应的excel表&#xff0c;excel导入就可以实现快捷导入。
当导入的excel模板发生变化的时候&#xff0c;也能通过t_fields配置&#xff0c;实现标题的动态配置。
修改为批量插入&#xff1a;不能使用replace into 进行主键覆盖策略&#xff0c;所有需要根据自己业务情况选择合适的插入方法
#生成插入语句--修改
/**
* 根据第一行生成插入语句
*/
private String createSql( List
Optional
.map(str -> String.format("&#96;%s&#96;", allFields.get(str.trim())))
.filter(s->!"".equals(s)&& null!&#61;s)
.reduce((a, b) -> String.format("%s,%s", a, b));
String sql &#61; String.format("insert into &#96;%s&#96;(%s) values ",table, sqlField.get());
return sql;
}
#生成批量插入
private int batch &#61; 1000;
String inserSql &#61; createSql(title, table);
try {
ArrayList
//使用批量插入---每N条生成一个插入语句
for (List
String s &#61; strings.stream().map(m -> String.format("&#39;%s&#39;", m)).reduce((x,y)->String.join(",",x,y))
.map(m -> String.format("(%s)", m)).get();
res.add(s);
if(res.size() % batch &#61;&#61;0){
String join &#61; String.join(",", res);
executeUpdateBatch(inserSql&#43;join, con);
res.clear();
}
}
String join &#61; String.join(",", res);
executeUpdateBatch(inserSql&#43;join, con);
try {
if(con!&#61;null){
con.setAutoCommit(true);
con.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
/**
* 执行 sql批量更新
* &#64;param sql sql
* &#64;param conn conn
* &#64;return
* &#64;throws SQLException
*/
private void executeUpdateBatch(String sql, Connection conn ) throws SQLException {
AtomicInteger count &#61; new AtomicInteger(1);
PreparedStatement ps &#61; conn.prepareStatement(sql);
ps.executeUpdate();
log.info("第{}次提交&#xff01;",count.getAndIncrement());
}
耗时统计&#xff1a;
测试数据&#xff1a;35个字段&#xff0c;共65330条数据
1.使用单条导入耗时&#xff1a;导入完成,共导入65330条数据,一共耗时24.816秒。
2.使用批量导入耗时&#xff1a;导入完成,共导入65330条数据,一共耗时7.359秒。
新增excel快速导入配置信息到t_fields:
前两行为标题行&#xff0c;第三行为fcode&#xff0c;第四行为data_type.
你也可以直接用poi解析&#xff0c;生成 List
String fieldsTable &#61; "t_fields";//excel字段配置表
String ftable &#61;"t_excel4";//excel对应的table名称
//todo Excel2007Parser解析默认第一行为标题好&#xff0c;数据从第二行开始计算的,所以excel中多定义一行标题
InputStream in &#61; new FileInputStream("C:\\Users\\shea\\Desktop\\t4.xlsx");
Excel2007Parser parser &#61; new Excel2007Parser(in);
parser.parse();
//解析结果
List> datas &#61; parser.getResults();
//直接转换为需要的数据格式
List> values&#61; datas.get(0).keySet().stream().map(k -> {
return IntStream.range(0, 3).boxed().map(i -> datas.get(i).get(k)).collect(Collectors.toList());
}).collect(Collectors.toList());
/**
//获取解析结果转为list
List
return new ArrayList<>(m.values());
}).collect(Collectors.toList());
//实现行转列--生成插入需要的数据结构
List> values &#61; IntStream.range(0, collect.get(0).size()).boxed().map(i -> {
return IntStream.range(0, 3).boxed().map(index -> collect.get(index).get(i)).collect(Collectors.toList());
}).collect(Collectors.toList());
*/
String sql &#61; String.format("insert into %s (&#96;fname&#96;,&#96;fcode&#96;,&#96;data_type&#96;,&#96;ftable&#96;) values (?,?,?,&#39;%s&#39;)",fieldsTable,ftable);
Connection conn &#61; getcon();
values.forEach(v-> {
try {
executeUpdate(sql,v,conn);
} catch (SQLException e) {
e.printStackTrace();
}
});
根据标题自动生成slq字段---中文缩写
1.引入拼音处理包
2.根据中文标题拼音首字母缩写生成字段
&#64;Test
public void createTable()throws Exception{
String fieldsTable &#61; "t_fields";//excel字段配置表
String ftable &#61;"t_excel6";//excel对应的table名称
//todo Excel2007Parser解析默认第一行为标题好&#xff0c;数据从第二行开始计算的,所以excel中多定义一行标题
InputStream in &#61; new FileInputStream("C:\\Users\\shea\\Desktop\\t6.xlsx");
Excel2007Parser parser &#61; new Excel2007Parser(in);
parser.parse();
//解析结果
List> datas &#61; parser.getResults();
LinkedHashMap
datas.get(0).keySet().forEach(k->{
String fcode &#61; HanLP.convertToPinyinFirstCharString(k, "", false);
if(map.containsKey(fcode)){
String code &#61; assertLive(map, fcode, 1);
map.put(code,k);
}else {
map.put(fcode,k);
}
});
List> values &#61; map.entrySet().stream()
.map(m -> Stream.of(m.getKey(), m.getValue(), "text").collect(Collectors.toList()))
.collect(Collectors.toList());
String sql &#61; String.format("insert into %s (&#96;fcode&#96;,&#96;fname&#96;,&#96;data_type&#96;,&#96;ftable&#96;) values (?,?,?,&#39;%s&#39;)",fieldsTable,ftable);
Connection conn &#61; getcon();
values.forEach(v-> {
try {
executeUpdate(sql,v,conn);
} catch (SQLException e) {
e.printStackTrace();
}
});
conn.close();
}
//递归查询&#xff0c;避免重复字段
private String assertLive(Map
String suffix &#61; String.valueOf(index);
if(map.containsKey(code.concat(suffix))){
index&#43;&#43;;
return assertLive(map,code,index);
}
return code.concat(suffix);
}
结果&#xff1a;