·场景: 需要将excel中的数据导入到oracle数据库中
·问题:
虽然PL/SQL中有一个ODBC import工具可以实现场景中描述的需求,但是如果excel中存在 合并单元格
的情况存在或者 空行
等情况,该工具就不是特别的好用,需要自己提前手动处理好excel文件
·需要解决:
/** * 用于将excle表中得数据插入到指定数据库中 * 前提条件:指定好excel文件路径,sheet表名称,数据库信息(表名称,连接地址,账号密码) * 输出:数据将插入到指定得表中 * 存在价值:可以解决excel表中合并单元格得问题 会自动识别和填充合并单元格中每格得内容 * 注意:如果整行为空 将不插入数据库 */ public class ExcelJDBC { private static String oracleTable; //插入得表名称 private static String sheetName; //sheet表名称 private static String oracleUrl; //连接地址 private static String user; //用户名 private static String passWord; //密码 private static String excelPath; //excel文件路径 public ExcelJDBC(String p_oracleTable,String p_sheetName,String p_oracleUrl,String p_user,String p_passWord,String p_excelPath){ this.oracleTable = p_oracleTable; this.sheetName = p_sheetName; this.oracleUrl = p_oracleUrl; this.user = p_user; this.passWord = p_passWord; this.excelPath = p_excelPath; } //总的一个执行逻辑方法 public void excelDeal() throws SQLException, IOException { File xlsFile = new File(excelPath);//excel路径 // 获得工作簿 Workbook workbook = WorkbookFactory.create(xlsFile); // 获得工作表个数 int sheetCount = workbook.getNumberOfSheets(); // 获取工作表 Sheet sheet = workbook.getSheet(sheetName); // 获得行数 int rows = sheet.getLastRowNum() + 1; // 获得列数,先获得一行,再得到该行列数 org.apache.poi.ss.usermodel.Row tmp = sheet.getRow(0); if (tmp == null) { System.out.println("首行没有数据"); return; } int cols = (tmp).getPhysicalNumberOfCells();//获取列数 String[][] cellsTemp = new String[rows][cols];//用于保存数据 // 读取数据 for (int row = 1; row < rows; row++) { org.apache.poi.ss.usermodel.Row r = sheet.getRow(row); for (int col = 0; col < cols; col++) { //判断是否为合并单元格 Result resultTemp = isMergedRegion(sheet, row, col); //获取单元格内容 String content = r.getCell(col) == null ? "" : r.getCell(col).toString(); //如果这个单元格是合并单元格且没有被赋值 则说明这个单元格是第一个 if (resultTemp.merged) { String sTemp = cellsTemp[row][col]; //不等于null则说明这个是合并单元格 且已经被附过值 if (sTemp != null) { continue; } //获取合并的行数和列数 int colnum = resultTemp.endCol - resultTemp.startCol; int rownum = resultTemp.endRow - resultTemp.startRow; //这个不等于0 则说明是向下合并单元格 if (colnum != 0) { for (int j = 0; j <= colnum; j++) { cellsTemp[row][col + j] = content; } } //这个不等于0 则说明横向合并单元格 if (rownum != 0) { for (int j = 0; j <= rownum; j++) { cellsTemp[row + j][col] = content; } } //如果都不为0 那么就说明跨行跨列合并单元格 if (colnum != 0 && rownum != 0) { for (int j = 1; j <= colnum; j++) { for (int p = 1; p <= rownum; p++) { cellsTemp[row + p][col + j] = content; } } } } else { cellsTemp[row][col] = content; } } } //控制台打印出数据 //printData(cellsTemp); //执行sql,将数据插入到数据库 StringBuilder sb = runSql(cellsTemp); System.out.println("未成功执行数据为:" + sb.toString()); System.out.println("操作结束"); } //region 以下为被调用的方法 /** * 打印数据 * @param p_Data data */ public static void printData(String[][] p_Data){ for (int k = 0; k < p_Data.length; k++){ for (int q = 0; q < p_Data[k].length; q++){ System.out.printf("%10s", p_Data[k][q]); } System.out.println(); } } /** * 判断是否为合并单元格 * @param sheet 表 * @param row 所在行 * @param column 所在列 * @return */ private static Result isMergedRegion(Sheet sheet, int row, int column) { int sheetMergeCount = sheet.getNumMergedRegions(); for (int i = 0; i < sheetMergeCount; i++) { CellRangeAddress range = sheet.getMergedRegion(i); int firstColumn = range.getFirstColumn(); int lastColumn = range.getLastColumn(); int firstRow = range.getFirstRow(); int lastRow = range.getLastRow(); if (row >= firstRow && row <= lastRow) { if (column >= firstColumn && column <= lastColumn) { return new Result(true, firstRow + 1, lastRow + 1, firstColumn + 1, lastColumn + 1); } } } return new Result(false, 0, 0, 0, 0); } /** * 获取oracle数据库连接 * @return */ public static Connection getConnection() { Connection connection = null;// 创建一个数据库连接 try { // 加载Oracle驱动程序 Class.forName("oracle.jdbc.driver.OracleDriver").newInstance(); System.out.println("开始尝试连接数据库!"); String url = "jdbc:oracle:thin:@" + oracleUrl;//Oracle的默认数据库名 connection = DriverManager.getConnection(url, user, passWord);// 获取连接 System.out.println("数据库连接成功!"); return connection; } catch (Exception e) { e.printStackTrace(); return null; } } /** * 拼接并执行sql语句 * @param p_Data * @return 执行异常的sql语句 */ public static StringBuilder runSql(String[][] p_Data) throws SQLException { List<String> sqlList = new ArrayList<>(); StringBuilder errorSql = new StringBuilder(); for (int k = 0; k < p_Data.length; k++){ String _value = ""; int emptyNum = 0;//一行中含有空值得数量 for (int q = 0; q < p_Data[k].length; q++){ _value += "'" + p_Data[k][q] + "',"; if(p_Data[k][q] == null || p_Data[k][q] == "") emptyNum++; } _value = _value.substring(0,_value.lastIndexOf(",")); if(emptyNum == p_Data[k].length) continue;//如果整行为空 则不进行插入动作 sqlList.add("insert into " + oracleTable + " values(" + _value + ")"); } Connection con = getConnection(); for (String s: sqlList) { Statement statement = con.createStatement(); int modifyNum = statement.executeUpdate(s); if(modifyNum == 0){ errorSql.append(s); } System.out.println("成功执行得sql语句为:" + s); } return errorSql; } /** * 内部类 */ static class Result{ public boolean merged;//是否为合并单元格 true-shi,false-fou public int startRow;//开始的行 public int endRow;//结束的行 public int startCol;//开始的列 public int endCol;//结束的列 public Result(boolean merged,int startRow,int endRow ,int startCol,int endCol){ this.merged = merged; this.startRow = startRow; this.endRow = endRow; this.startCol = startCol; this.endCol = endCol; } } //endregion } 复制代码
使用方法:
1.声明类对象ExcelJDBC
2.调用方法 excelDeal
即可
示例如下:
public static void main(String args[]) throws IOException, SQLException { //region 以下内容需要使用者根据实际情况进行更改 String oracleTable = "tableName";//插入得表名称,例:HANTEST String sheetName = "sheetName";//sheet表名称,例:Sheet1 String oracleUrl = "ip:port:dataName";//数据库连接地址,例:127.0.0.1:1521:orcl String user = "ueserName";//数据库用户名,例:root String passWord = "passWord";//数据库密码,例:root123 String excelPath = "excelPath";//excel文件路径,例:D://Han//test.xlsx //endregion //声明对象 传入参数 ExcelJDBC excelJDBC = new ExcelJDBC(oracleTable,sheetName,oracleUrl,user,passWord,excelPath); 调用方法 执行操作 excelJDBC.excelDeal(); } 复制代码数据库原始表结构:使用PL/SQL的ODBC import工具的数据库导入效果:使用数据库导入效果: