你知道的越多,你不知道的越多
点赞再看,养成习惯
如果您有疑问或者见解,欢迎指教:
企鹅:869192208

前言

最近在甲方爸爸的要(威)求(逼)下,项目经理带来了客户的全新需求,希望能够在原有编辑表单填写的过程中,简化列表数据输入过程,通过下载列表对应的数据表的表结构作为 excel 模板,然后客户只需要根据模板提示填写 excel 内容,再导入到数据库中,最终回显到编辑列表。

通过简化这个需求,抽象出核心功能,那就是根据表结构,动态实现数据模板的导出和数据的导入,趁着清明节三天假期,研究一下这个需求的实现逻辑。

思路

  • 使用 POI 作为 excel 的导入导出支撑
  • 需要同时支持 oracle 和 mysql 数据库
  • 导出:
  1. sql 查出数据库指定表的表字段名、数据类型和注释等信息
  2. 将表的字段名,数据类型和注释信息分别输出到 excel 前三行,形成模板
  • 导入:
  1. 先读取字段和字段类型,将其存入 LinkedHashMap
  2. 获取一共有多少个字段需要导入,以此为数据循环的截止参数(字段数量和每行的数据个数一致)
  3. 获取表中的内容,因为前三列分别是字段名、字段类型和描述,从第4行开始才是 excel 中需要插入数据库的内容
  4. 循环获取数据每一行的每一个单元格,根据单元格的数据类型,将数据转换成 String 类型的数据,存储到 List 集合
  5. 组装插入数据库的 insert 语句,表名是动态传入的,跟导出模板一致;字段分别是 excel 模板解析出来的首行字段,以",“分隔;插入数据以”?“充当占位符,以”,"分隔
  6. 将LinkedHashMap存储的字段对应的数据类型为基础,将数据填充到每一个"?"占位符

代码实现

代码中实现了 mysql 和 oracle 两种数据库的动态导入导出 excel,目前代码运行的是 oracle 数据库的操作,如果需要运行 mysql 数据库的操作,需要在 main 方法里面,将 mysql 的连接参数和获取表结构的代码注释去掉,同时将 oracle 的连接参数和获取表结构的代码注释即可。

建表语句

oracle:


-- ----------------------------
-- Table structure for APPR_EXCEL_DEMO
-- ----------------------------
DROP TABLE "APPR_EXCEL_DEMO";
CREATE TABLE "APPR_EXCEL_DEMO" (
"ID" VARCHAR2(50 BYTE) NOT NULL ,
"USER_NAME" VARCHAR2(255 BYTE) NULL ,
"SEX" NUMBER(2) NULL ,
"BIRTHDAY" DATE NULL ,
"PID" VARCHAR2(50 BYTE) NULL
)
LOGGING
NOCOMPRESS
NOCACHE;
COMMENT ON TABLE "APPR_EXCEL_DEMO" IS 'excel导入导出测试表';
COMMENT ON COLUMN "APPR_EXCEL_DEMO"."ID" IS '主键';
COMMENT ON COLUMN "APPR_EXCEL_DEMO"."USER_NAME" IS '用户名称';
COMMENT ON COLUMN "APPR_EXCEL_DEMO"."SEX" IS '性别';
COMMENT ON COLUMN "APPR_EXCEL_DEMO"."BIRTHDAY" IS '生日';
COMMENT ON COLUMN "APPR_EXCEL_DEMO"."PID" IS '父id';-- ----------------------------
-- Indexes structure for table APPR_EXCEL_DEMO
-- ------------------------------ ----------------------------
-- Checks structure for table APPR_EXCEL_DEMO
-- ----------------------------
ALTER TABLE "APPR_EXCEL_DEMO" ADD CHECK ("ID" IS NOT NULL);-- ----------------------------
-- Primary Key structure for table APPR_EXCEL_DEMO
-- ----------------------------
ALTER TABLE "APPR_EXCEL_DEMO" ADD PRIMARY KEY ("ID");

mysql

-- Source Database(数据库名称):exceldemo
-- ----------------------------
-- Table structure for appr_excel_demo
-- ----------------------------
DROP TABLE IF EXISTS `appr_excel_demo`;
CREATE TABLE `appr_excel_demo` (`id` varchar(50) NOT NULL COMMENT '主键',`user_name` varchar(255) DEFAULT NULL COMMENT '用户名-字符串',`sex` int(2) DEFAULT NULL COMMENT '性别',`birthday` datetime DEFAULT NULL COMMENT '生日',`pid` varchar(50) DEFAULT NULL COMMENT '父id',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
package excel;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;/*** <h2>根据表结构动态导入导出Excel</h2>** @author xymy* @date 2021-01-05 19:21*/
public class ExcelDemo {//oracle查询数据库表结构sqlprivate static final String getTableColumnsOracle = "SELECT A.DATA_TYPE \"data_type\", B.COLUMN_NAME \"column_name\", substr(B.COMMENTS,0,decode(instr(B.COMMENTS,'@'),NULL,255,0,255,instr(B.COMMENTS,'@'))-1) \"column_comment\"\n" +"\t\t  FROM USER_TAB_COLUMNS A\n" +"\t\t  LEFT JOIN USER_COL_COMMENTS B\n" +"\t\t    ON B.TABLE_NAME = A.TABLE_NAME\n" +"\t\t   AND A.COLUMN_NAME = B.COLUMN_NAME\n" +"\t\t WHERE A.TABLE_NAME = ?\n" +"order by b.COLUMN_NAME";//mysql查询数据库表结构sql//private static final String getTableColumnsMysql = "select column_name,column_comment,data_type,column_type,is_nullable from information_schema.columns where table_name=? and table_schema=? order by ORDINAL_POSITION asc";private static final String getTableColumnsMysql = "SELECT\n" +"\ta.column_name,\n" +"\ta.column_comment,\n" +"\ta.data_type,\n" +"\ta.column_type,\n" +"\ta.is_nullable,\n" +"\tCASE\n" +"WHEN b.COLUMN_NAME = a.column_name THEN\n" +"\t'true'\n" +"ELSE\n" +"\t'false'\n" +"END is_pk\n" +"FROM\n" +"\tinformation_schema. COLUMNS a\n" +"JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE b ON a.table_name = b.table_name\n" +"WHERE\n" +"\ta.table_name = ?\n" +"AND a.table_schema = ?\n" +"ORDER BY\n" +"\ta.ORDINAL_POSITION ASC";//定义excel操作的根目录private static String PATH="D:/excel/";private static DruidDataSource dataSource=null;/*** 构造函数完成数据库的连接和连接对象的生成* @throws Exception*/public ExcelDemo(){}public void getDbConnect(String url, String driverClassName, String username, String password) throws Exception  {try{if(dataSource==null){dataSource=new DruidDataSource();//设置连接参数dataSource.setUrl(url);dataSource.setDriverClassName(driverClassName);dataSource.setUsername(username);dataSource.setPassword(password);//配置初始化大小、最小、最大dataSource.setInitialSize(1);dataSource.setMinIdle(1);dataSource.setMaxActive(20);//连接泄漏监测dataSource.setRemoveAbandoned(true);dataSource.setRemoveAbandonedTimeout(30);//配置获取连接等待超时的时间dataSource.setMaxWait(20000);//配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒dataSource.setTimeBetweenEvictionRunsMillis(20000);//防止过期dataSource.setValidationQuery("SELECT 'x' from dual");dataSource.setTestWhileIdle(true);dataSource.setTestOnBorrow(true);}}catch(Exception e){throw e;}}/*** 取得已经构造生成的数据库连接* @return 返回数据库连接对象* @throws Exception*/public Connection getConnect(String url, String driverClassName, String username, String password) throws Exception{Connection con=null;try {getDbConnect(url, driverClassName, username, password);con=dataSource.getConnection();} catch (Exception e) {throw e;}return con;}public static void main(String[] args) throws Exception {//测试的表String tableName = "appr_excel_demo";//mysql测试数据库String tableSchema = "exceldemo";//配置mysql连接参数/*String url = "jdbc:mysql://127.0.0.1:3306/exceldemo?useUnicode=true&characterEncoding=utf-8";String driverClassName = "com.mysql.jdbc.Driver";String username = "root";String password = "123456";*///配置oracle连接参数String url = "jdbc:oracle:thin:@127.0.0.1:1521:oanet";String driverClassName = "oracle.jdbc.driver.OracleDriver";String username = "root";String password = "123456";List<Map<String, String>> list = new ArrayList<Map<String, String>>();ExcelDemo dbConnect = new ExcelDemo();Connection connection = dbConnect.getConnect(url, driverClassName, username, password);//mysql获取数据库表结构/*PreparedStatement ps = connection.prepareStatement(getTableColumnsMysql);ps.setString(1, tableName);ps.setString(2, tableSchema);*///oracle获取数据库表结构PreparedStatement ps = connection.prepareStatement(getTableColumnsOracle);ps.setString(1, tableName);ResultSet rs = ps.executeQuery();while(rs.next()){System.out.println(rs.getString("data_type") + " | " + rs.getString("column_name") + " | " + rs.getString("column_comment"));Map<String, String> map = new HashMap<String, String>();map.put("columnName", rs.getString("column_name"));map.put("dataType", rs.getString("data_type"));map.put("desc", rs.getString("column_comment"));list.add(map);}list.stream().forEach(l -> {System.out.println(l.get("columnName") + "," + l.get("dataType") + "," + l.get("desc"));});//将库表的结构导出到excel形成模板exportExcel(list, "testWrite07excel.xlsx");//将excel的数据导入到数据库importExcel(connection, tableName, "testRead07excel.xlsx");}/*** 导入excel* @throws Exception*/private static void importExcel(Connection connection, String tableName, String fileName) throws Exception {//获取文件FileInputStream fileInputStream = new FileInputStream(PATH + fileName);//获取工作薄Workbook workbook = new XSSFWorkbook(fileInputStream);//得到表Sheet sheet = workbook.getSheetAt(0);//所有字段和类型的集合LinkedHashMap<String, String> columns = new LinkedHashMap<>();//记录一行有多少单元格int cellCount = 0;//获取0-字段名称、1-字段类型、2-字段描述Row rowColumnName = sheet.getRow(0);Row rowDataType = sheet.getRow(1);if (rowColumnName != null && rowDataType != null){//获取一行中有多少个单元格cellCount = rowColumnName.getPhysicalNumberOfCells();for (int cellNum = 0; cellNum < cellCount; cellNum++) {//获取单元Cell cellColumnName = rowColumnName.getCell(cellNum);Cell cellDataType = rowDataType.getCell(cellNum);if (cellColumnName != null){//获取类型String cellColumnNameValue = cellColumnName.getStringCellValue();String cellDataTypeValue = cellDataType.getStringCellValue();columns.put(cellColumnNameValue, cellDataTypeValue);System.out.print(cellColumnNameValue+","+ cellDataTypeValue + " | ");}}System.out.println();}//获取表中的内容,从第4行开始是excel中需要插入数据库的内容int rowCount = sheet.getPhysicalNumberOfRows();for (int rowNum = 3; rowNum < rowCount; rowNum++) {//一行记录所有值的集合List<String> values = new ArrayList<>();Row rowData = sheet.getRow(rowNum);if (rowData != null){//读取列for (int cellNum = 0; cellNum < cellCount; cellNum++) {System.out.print("【" + (rowNum+1) + "-" + (cellNum+1) + "】");Cell cell = rowData.getCell(cellNum);//匹配列的数据类型if (cell != null){int cellType = cell.getCellType();String cellValue = "";switch (cellType){case HSSFCell.CELL_TYPE_STRING://字符串System.out.print("【STRING】");cellValue = cell.getStringCellValue();break;case HSSFCell.CELL_TYPE_BOOLEAN://布尔值System.out.print("【BOOLEAN】");cellValue = String.valueOf(cell.getBooleanCellValue());break;case HSSFCell.CELL_TYPE_NUMERIC://数字类型System.out.print("【NUMERIC】");if (HSSFDateUtil.isCellDateFormatted(cell)){//日期System.out.print("【日期】");Date date = cell.getDateCellValue();SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");cellValue = formatter.format(date);}else{// 不是日期格式,则防止当数字过长时以科学计数法显示System.out.print("【转换成字符串】");cell.setCellType(HSSFCell.CELL_TYPE_STRING);cellValue = cell.toString();}break;case HSSFCell.CELL_TYPE_BLANK://空System.out.print("【BLANK】");break;case Cell.CELL_TYPE_ERROR:System.out.print("【数据类型错误】");break;}System.out.println(cellValue);values.add(cellValue);}}}//将数据插入数据库//组装所有的插入字段String inserts = "";//组装所有的插入值的占位符String v = "";List<String> types = new ArrayList<>();for (String s : columns.keySet()) {System.out.println("key=" + s + " value=" + columns.get(s));inserts = inserts + s + ",";v = v + "?,";types.add(columns.get(s));}String insertSql = "insert into "+tableName+"("+inserts.substring(0, inserts.lastIndexOf(","))+")values("+v.substring(0, v.lastIndexOf(","))+")";System.out.println(insertSql);PreparedStatement ps = connection.prepareStatement(insertSql);//构造真正的需要插入的值for (int i = 1; i<= types.size(); i++){String type = types.get(i-1);String value = values.get(i-1);System.out.println(type + "-" + value);if (type.contains("varchar")){//字符串ps.setString(i, value);}else if (type.contains("int") || type.contains("number")){//数字ps.setInt(i, Integer.parseInt(value));}else if (type.contains("date")){//日期if (!value.isEmpty()){ps.setTimestamp(i, new Timestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(value).getTime()));}else {ps.setTimestamp(i, null);}}}int execute = ps.executeUpdate();System.out.println(execute);}fileInputStream.close();}/*** 导出excel* @throws Exception*/private static void exportExcel(List<Map<String, String>> list, String fileName) throws Exception {//创建簿Workbook workbook = new XSSFWorkbook();//创建表Sheet sheet = workbook.createSheet();//写数据for (int rowNum = 0; rowNum < 3; rowNum++) {Row row = sheet.createRow(rowNum);for (int cellNum = 0; cellNum < list.size(); cellNum++) {Cell cell = row.createCell(cellNum);if (rowNum == 0){cell.setCellValue(list.get(cellNum).get("columnName"));}else if (rowNum == 1){cell.setCellValue(list.get(cellNum).get("dataType"));}else if (rowNum == 2){cell.setCellValue(list.get(cellNum).get("desc"));}}}System.out.println("导入模板完成");FileOutputStream fileOutputStream = new FileOutputStream(PATH + fileName);workbook.write(fileOutputStream);fileOutputStream.close();}
}

带你实现java根据表结构动态导入导出Excel相关推荐

  1. Java基于注解和反射导入导出Excel

    代码地址如下: http://www.demodashi.com/demo/11995.html 1. 构建项目 使用Spring Boot快速构建一个Web工程,并导入与操作Excel相关的POI包 ...

  2. sql developer Oracle 数据库 用户对象下表及表结构的导入导出

    Oracle数据库表数据及结构的导入导出   导出的主机与即将导入到的目标主机的tablespace 及用户名需一直!!!!!

  3. java自动生成生成java透视表_java基于poi导出excel透视表代码实例

    这篇文章主要介绍了java基于poi导出excel透视表代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 从前,我是一个前端程序猿,怀着对打通 ...

  4. 咸鱼带你学Java—类的结构之一:属性(field)

    目录 一.概念 二.语法格式 三.属性的初始值 四.成员变量(属性)与局部变量的异同 1.对变量按照声明位置分类 2.两者异同 一.概念 对应类中的成员变量 二.语法格式 修饰符 数据类型 属性名 = ...

  5. java 动态导入excel_java实现导入导出excel数据

    项目需要,要实现一个导入导出excel的功能,于是,任务驱动着我学习到了POI和JXL这2个java操作Excel的插件. 一.POI和JXL介绍 1.POI:是对所有office资源进行读写的一套工 ...

  6. java excel data 导入数据_java实现导入导出excel数据

    项目需要,要实现一个导入导出excel的功能,于是,任务驱动着我学习到了POI和JXL这2个java操作Excel的插件. 一.POI和JXL介绍 1.POI:是对所有office资源进行读写的一套工 ...

  7. Java导入导出Excel工具类ExcelUtil

    前段时间做的分布式集成平台项目中,许多模块都用到了导入导出Excel的功能,于是决定封装一个ExcelUtil类,专门用来处理Excel的导入和导出 本项目的持久化层用的是JPA(底层用hiberna ...

  8. hive:建库建表、表分区、内部表外部表、数据导入导出

    hive建库建表与数据导入 建库 hive中有一个默认的库: 库名: default 库目录:hdfs://hdp20-01:9000/user/hive/warehouse 新建库: create  ...

  9. JAVA工具类(17)--Java导入导出Excel工具类ExcelUtil

    实战 导出就是将List转化为Excel(listToExcel) 导入就是将Excel转化为List(excelToList) 导入导出中会出现各种各样的问题,比如:数据源为空.有重复行等,我自定义 ...

最新文章

  1. 快手上市!员工暴富!人均1300万港元!
  2. 游戏数据的捕捉(郁金香学习笔记)
  3. rest-framework:频率控制
  4. 数字图像处理实验(5):Proj03-01 ~ Proj03-06
  5. python 输出所有大小写字母, range()以及列表切片
  6. caffe学习笔记18-image1000test200数据集分类与检索完整过程
  7. js继承之借用构造函数继承
  8. 【渝粤教育】国家开放大学2018年秋季 1133t文献检索 参考试题
  9. gridview行号
  10. 软件_matplotlib绘图跳过时间段的处理方案[博]
  11. 【转】POJ分类很好很有层次感
  12. oracel vm 安装windows server 2012报错Error 0x000000C4
  13. 新手兼职也能月入5000的副业项目,几乎零门槛
  14. Linux下禁用笔记本触摸板
  15. Retrofit统一异常处理
  16. 鸿蒙电视厂商多少人,国产厂商崛起?鸿蒙之后这家厂商也推送了新系统,体验极佳...
  17. 节拍器在学习音乐过程中起到什么作用?-小星星节拍器怎么样?
  18. 【Arduino+ESP32专题】一起来读INA3221数据手册 1
  19. mysql数据创建用户及授权
  20. The Shawshank Redemption-19

热门文章

  1. 传统隐私权及网络隐私权的界定
  2. javamail发送/回复邮件报错: Local address contains control or whitespace in string
  3. 论文文献引用规范和标准(国标GBT7714)@endnote国标样式@citation交叉引用编号
  4. 硬件知识:红外感应电路
  5. 神经网络算法有哪些模型,神经网络的简单模型是
  6. 对比欧氏距离与余弦相似度
  7. 苹果x漫画脸_延续硬派定位 新一代五十铃mu-X首发
  8. 5.25 FLASH
  9. ognl表达式的研究
  10. MIIX510(MIIX5)如何进入BIOS