实现百万EXCLE数据导入

1. 目标:

30分钟内实现 百万级数据导入到数据库中。

2. 实现原理:

  通过流的方式(InputStream)读入excel表,每个sheet对应一个 inputStream。将sheet内容转换为轻量级的 ReadOnlySharedStringsTable, (同比SharedStringsTable数据量大的时候会报 out of memory) 然后使用 SAX(同样为轻量级的)进行数据读取,在对处理好的每行数据进行数据库导入。

数据库导入是通过批处理进行,我这里设置 每1000次实现一次导入,最后结束时进行提交。

3. 重点介绍:SAX

SAX(simple API for XML)是一种XML解析的替代方法。相比于DOM,SAX是一种速度更快,更有效的方法。它逐行扫描文档,一边扫描一边解析。同时它的缺点时操作复杂。下面为介绍:

一个XML文档举例来说

<doc>

<para>Hello,World!</para>

</doc>其解析的过程为:1.start document

2.start element:doc......

3.start element:para.....

4.characters:Hello,World!

5.end element:para......

6.end element;doc......

7.end document

对于解析过程中的每一步都会有事件发生,都会触发相应接口中的事件处理程序。

编写程序的步骤为:

(1).创建事件处理程序(也就是编写ContentHandler的实现类,一般继承自DefaultHandler类,采用adapter模式)

(2).创建SAX解析器

(3).将事件处理程序分配到解析器

(4).对文档进行解析,将每个事件发送给事件处理程序

3.1SAX 模型

编辑

.过程:

1.首先SAXParserFactory来创建一个SAXParserFactory实例

SAXParserFactory factory = SAXParserFactory.newInstance();

2.根据SAXParserFactory实例来创建SAXParser

3.SAXParser产生SAXReader

XMLReader reader = factory.newSAXParser().getXMLReader();

4.XMLReader 加载XML,然后解析XML,在解析的过程中触发相对于接口中的事件处理程序。

3.2接口介绍

ContentHandler接口 (主要用到的接口)

ContentHandler是Java类包中一个特殊的SAX接口,位于org.xml.sax包中。该接口封装了一些对事件处理的方法,当XML解析器开始解析XML输入文档时,它会遇到某些特殊的事件,比如文档的开头和结束、元素开头和结束、以及元素中的字符数据等事件。当遇到这些事件时,XML解析器会调用ContentHandler接口中相应的方法来响应该事件。

ContentHandler接口的方法有以下几种:

void startDocument()//文件打开时调用

void endDocument()//当到文档的末尾调用

void startElement(String uri, String localName, String qName, Attributes atts)//当遇到开始标记时调用

void endElement(String uri, String localName, String qName)//当遇到节点结束时调用

void characters(char[ ] ch, int start, int length)//当分析器遇到无法识别为标记或者指令类型字符时调用

4代码

分别为 3个类: BigExcelReader (实现数据导入到库中的类) RowReader(针对每行数据的操作类) BigExcelReaderTest(JUNIT的测试类)

 

RowReader类:

package net.sf.excelutils;

import java.sql.Connection;
import java.sql.PreparedStatement;

public class RowReader {		
		private Integer curRow=-1;
		private int excutCount =0;//执行次数
		/**
		 * 执行处理每一条行数据,进行导入到数据库中
		 * @param sheetIndex
		 * @param curRow
		 * @param row
		 * @param connection
		 * @param statement
		 * @throws Exception
		 */
		public void getRows(int sheetIndex, int curRow, String[] row,Connection connection ,PreparedStatement statement) throws Exception {
			
			if(this.curRow!=curRow) {
			
			    statement.setString(1,row[0]);				
    			statement.setString(2, row[0]);
    			statement.setString(3, row[0]);
    			statement.setString(4, row[0]);
    			statement.setString(5, row[0]);
    			statement.setString(6, row[5]);
    			statement.setString(7, row[6]);
    			statement.setString(8, row[7]);
    			statement.setString(9, row[8]);
    			statement.setString(10, row[9]);
    			statement.setString(11, row[10]);
    			statement.setString(12, row[11]);
    			statement.setString(13, row[12]);
    			statement.setString(14, row[13]);
    			statement.setString(15, row[14]);
    			statement.setString(16, row[15]);
    			statement.addBatch();	
    			excutCount++;//自增
				//批量执行提交
				if(curRow%1000==0){					
						System.out.println("执行批量提交数据");
						statement.executeBatch();
						statement.clearBatch();				
				}
			
				this.curRow=curRow;
			}
			
			
		}
		public int getExcutCount() {
			return excutCount;
		}
		
		
	
}
package net.sf.excelutils;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Pattern;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

/**
 * 类名称:BigExcelReader 类描述:实现大数据导入到数据库中
 */
public class BigExcelReader {
	private XSSFReader xssfReader;
	private final int minColumnCount; // 获取一行时最小数组长度
	private int currentRow = 0;
	private int sheetIndex = -1;
	private String[] record;
	private int thisColumnIndex = -1;
	private boolean dateFlag;// 日期标志
	private boolean numberFlag;// 数字标志
	private boolean isTElement;
	private RowReader rowReader;// 创建的处理行数据的类
	private Connection connection;// 数据库连接 数据库是否在这个创建可以自行选择
	private PreparedStatement statement;// 预编译
	private OPCPackage pkg = null;

	public void setRowReader(RowReader rowReader) {
		this.rowReader = rowReader;
	}

	/**
	 * 构造方法
	 */
	public BigExcelReader(String filename, int minCols) throws Exception {
		if (StringUtils.isEmpty(filename))
			throw new Exception("文件名不能空");
		this.minColumnCount = minCols;
		record = new String[this.minColumnCount];
		this.pkg = OPCPackage.open(filename);

		// 创建数据库连接
		Class.forName("oracle.jdbc.driver.OracleDriver");
		connection = DriverManager.getConnection(
				"jdbc:oracle:thin:@192.168.60.36:1521:orcl", "csctest",
				"csctest");
		String sql = "insert into CSC_PURCHASETRACK values (SEQ_PURCHASETRACK_ID.NEXTVAL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
		connection.setAutoCommit(false);
		statement = connection.prepareStatement(sql);

	}

	/**
	 * 初始化BigExcelReader
	 * 
	 * @param is
	 * @param minCols
	 * @throws Exception
	 */
	public BigExcelReader(InputStream is, int minCols) throws Exception {
		if (null == is)
			throw new Exception("文件不能空");
		this.minColumnCount = minCols;
		record = new String[this.minColumnCount];// 创建放行数据的数组
		this.pkg = OPCPackage.open(is);
	}

	/**
	 * 遍历每个sheet,进行数据处理
	 * 
	 * @param filename
	 * @throws Exception
	 */

	public void process() throws Exception {
		ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(
				this.pkg);
		XSSFReader xssfReader = new XSSFReader(this.pkg);
		StylesTable styles = xssfReader.getStylesTable();
		XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) xssfReader
				.getSheetsData();
		int index = 0;
		while (iter.hasNext()) {
			InputStream stream = iter.next();
			processSheet(styles, strings, stream);
			stream.close();
			++index;
		}
	}

	/**
	 * 创建 调用 SAX 流程 使用 详细见: 3.1SAX 模型 -过程
	 * 
	 * @param styles
	 * @param strings
	 * @param sheetInputStream
	 * @throws IOException
	 * @throws ParserConfigurationException
	 * @throws SAXException
	 */
	public void processSheet(StylesTable styles,
			ReadOnlySharedStringsTable strings, InputStream sheetInputStream)
			throws IOException, ParserConfigurationException, SAXException {
		InputSource sheetSource = new InputSource(sheetInputStream);
		SAXParserFactory saxFactory = SAXParserFactory.newInstance();
		SAXParser saxParser = saxFactory.newSAXParser();
		XMLReader sheetParser = saxParser.getXMLReader();
		ContentHandler handler = new PagingHandler(strings);
		sheetParser.setContentHandler(handler);
		sheetParser.parse(sheetSource);
	}

	/**
	 * 创建内部类,实现DefaultHandle,详细见
	 */
	private class PagingHandler extends DefaultHandler {
		private ReadOnlySharedStringsTable sst;
		private String lastContents;
		private boolean nextIsString;
		private String index = null;

		private PagingHandler(ReadOnlySharedStringsTable sst) {
			this.sst = sst;
		}

		/**
		 * 开始元素
		 */
		@Override
		public void startElement(String uri, String localName, String name,
				Attributes attributes) throws SAXException {
			if (name.equals("c")) {
				index = attributes.getValue("r");
				int firstDigit = -1;
				for (int c = 0; c < index.length(); ++c) {
					if (Character.isDigit(index.charAt(c))) {
						firstDigit = c;
						break;
					}
				}
				thisColumnIndex = nameToColumn(index.substring(0, firstDigit));

				// 判断是否是新的一行
				if (Pattern.compile("^A[0-9]+$").matcher(index).find()) {
					currentRow++;
				}
				String cellType = attributes.getValue("t");
				if (cellType != null && cellType.equals("s")) {
					nextIsString = true;
				} else {
					nextIsString = false;
				}
				// 日期格式
				String cellDateType = attributes.getValue("s");
				if ("1".equals(cellDateType)) {
					dateFlag = true;
				} else {
					dateFlag = false;
				}
				String cellNumberType = attributes.getValue("s");
				if ("2".equals(cellNumberType)) {
					numberFlag = true;
				} else {
					numberFlag = false;
				}
			}
			// 当元素为t时
			if ("t".equals(name)) {
				isTElement = true;
			} else {
				isTElement = false;
			}
			lastContents = "";
		}

		/**
		 * 获取value
		 */
		@Override
		public void endElement(String uri, String localName, String name)
				throws SAXException {
			if (nextIsString) {
				int idx = Integer.parseInt(lastContents);
				lastContents = new XSSFRichTextString(sst.getEntryAt(idx))
						.toString();
				nextIsString = false;
			}
			// t元素也包含字符串
			if (isTElement) {
				String value = lastContents.trim();
				record[thisColumnIndex] = value;
				isTElement = false;
				// v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引
				// 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符
			} else if ("v".equals(name)) {
				String value = lastContents.trim();
				value = value.equals("") ? " " : value;
				// 日期格式处理
				if (dateFlag) {
					try {
						Date date = HSSFDateUtil.getJavaDate(Double
								.valueOf(value));
						SimpleDateFormat dateFormat = new SimpleDateFormat(
								"yyyy-MM-dd");
						value = dateFormat.format(date);
					} catch (NumberFormatException e) {
					}
				}
				// 数字类型处理
				if (numberFlag) {
					try {
						BigDecimal bd = new BigDecimal(value);
						value = bd.setScale(3, BigDecimal.ROUND_UP).toString();
					} catch (Exception e) {
					}
				}
				record[thisColumnIndex] = value;

			} else {
				if (name.equals("row")) {
					if (minColumnCount > 0) {
						try {
							rowReader.getRows(sheetIndex, currentRow,
									record.clone(), connection, statement);
						} catch (Exception e) {
							// TODO 自动生成的 catch 块
							e.printStackTrace();
						}

						for (int i = 0; i < record.length; i++) {
							record[i] = null;
						}
					}
				}
			}
		}

		/**
		 * 到文档结束时进行调用实现最后一次执行和提交
		 */
		@Override
		public void endDocument() {
			try {
				statement.executeBatch();
				connection.commit();
				System.out.println("导入的数据数量" + rowReader.getExcutCount());
			} catch (SQLException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			} finally {

				// 注意关闭连接
				try {
					if (statement != null) {

						statement.close();

					}
					if (connection != null) {
						connection.close();
					}
				} catch (SQLException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
			}

		}

		@Override
		public void characters(char[] ch, int start, int length)
				throws SAXException {
			lastContents += new String(ch, start, length);
		}

	}

	private int nameToColumn(String name) {
		int column = -1;
		for (int i = 0; i < name.length(); ++i) {
			int c = name.charAt(i);
			column = (column + 1) * 26 + c - 'A';
		}
		return column;
	}

	public Connection getConnection() {
		return connection;
	}

	public void setConnection(Connection connection) {
		this.connection = connection;
	}

	public PreparedStatement getStatement() {
		return statement;
	}

	public void setStatement(PreparedStatement statement) {
		this.statement = statement;
	}

	public static void main(String[] args) throws Exception {
		// 起始日期
		Date datestart = new Date();
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-DD HH:mm:ss");
		String dateStartSt = format.format(datestart);

		Long starttime = new Date().getTime();// 获取执行时的起始时间
		RowReader rowrReader = new RowReader();
		BigExcelReader reader = new BigExcelReader("D:/181121.xlsx", 97);
		reader.setRowReader(rowrReader);
		try {
			reader.process();
		} catch (Exception e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
			// 不能提交返回
			reader.getConnection().rollback();
		}
		Long endtime = new Date().getTime();// 获取结束的时间
		Date dateEnd = new Date();
		String dateEndSt = format.format(dateEnd);
		System.out.println("所需要的的时间" + (endtime - starttime));
		System.out.println("起始时间" + dateStartSt + "结束日期" + dateEndSt);

	}
}
package net.sf.excelutils;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.junit.Test;

public class BigExcelReaderTest {

	@Test
	public void test() throws Exception {

		//起始日期
		Date datestart = new Date();
		SimpleDateFormat format  = new SimpleDateFormat("yyyy-MM-DD HH:mm:ss");
		String dateStartSt = format.format(datestart);
		
		Long starttime =   new Date().getTime();//获取执行时的起始时间
		RowReader rowrReader = new RowReader();
		BigExcelReader reader = new BigExcelReader("D:/181121.xlsx",97);
		reader.setRowReader(rowrReader);
		try {
			reader.process();
		} catch (Exception e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
			//不能提交返回
			reader.getConnection().rollback();
		}
		Long endtime = new Date().getTime();//获取结束的时间
		Date dateEnd = new Date();
		String dateEndSt =  format.format(dateEnd);
		System.out.println("所需要的的时间"+(endtime-starttime));
		System.out.println("起始时间"+ dateStartSt +"结束日期"+dateEndSt);
		
	
	}

}

测试结果:我导入了367652 数据,花费了4分钟;

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要使用Java实现将Excel数据导入MySQL数据库,可以按照以下步骤进行: 1. 首先需要导入相关的Java库,包括Apache POI用于读取Excel文件中的内容,以及JDBC用于连接和操作MySQL数据库。 2. 使用Apache POI库读取Excel文件。可以使用HSSFWorkbook类来表示整个Excel文件,然后通过HSSFSheet和HSSFRow来遍历每个工作表和每一行。 3. 在读取Excel文件时,可以通过HSSFCell获取每个单元格的值。根据Excel文件的结构和数据需求,可以自定义解析单元格的逻辑。 4. 将读取到的数据存储在Java对象中,可以使用ArrayList或其他数据结构。 5. 建立与MySQL数据库的连接,使用JDBC的相关API。需要提供数据库的URL、用户名和密码等信息。 6. 创建数据库表,在MySQL中执行CREATE TABLE语句。可以使用PreparedStatement预编译SQL语句,确保数据的安全性和可扩展性。 7. 将读取到的数据插入到MySQL数据库中,可以使用INSERT语句。通过PreparedStatement设置参数,将Java对象中的数据传递给SQL语句。 8. 执行INSERT语句,将数据写入MySQL数据库。 9. 关闭资源,包括关闭Excel文件和数据库连接等。 以上就是使用Java实现Excel数据导入MySQL数据库的基本步骤。实际操作中需要考虑异常处理、数据校验和转换等问题,以保证数据的完整性和准确性。同时也可以根据需求进行扩展,例如支持多个工作表、数据更新和删除等操作。 ### 回答2: 要使用Java实现将Excel数据导入MySQL数据库,可以按照以下步骤进行: 1. 使用Java中的Apache POI库读取Excel文件。Apache POI是一个开源库,可以用于读取和写入Microsoft Office文档格式,包括Excel文件。通过POI库,可以获取Excel文件中的每个单元格的数据。 2. 创建一个Java对象来表示MySQL数据库连接。可以使用JDBC驱动程序来连接MySQL数据库。可以通过使用`java.sql`包中的`Connection`、`Statement`和`ResultSet`等类,来建立与数据库的连接和执行SQL语句。 3. 在Java中解析Excel文件的数据,并将其存储在适当的数据结构中。根据Excel文件的结构,可以将数据存储在Java数组、列表或实体类对象中。 4. 根据数据结构中的数据,编写SQL插入语句。可以使用Java中的字符串操作方法,或者使用SQL模板引擎来创建插入语句。 5. 在Java中执行SQL插入语句,将Excel数据插入到MySQL数据库中。可以使用`Statement`对象的`executeUpdate()`方法来执行插入语句。 6. 最后,关闭数据库连接和Excel文件,释放资源。 注意事项: - 在导入数据之前,需要确保MySQL数据库已经正确地设置和配置,并且在Java项目中添加了MySQL驱动。 - 需要处理Excel文件的格式和结构,确保文件中的表格和数据符合预期。 - 可以使用异常处理机制来处理可能发生的错误,并提供适当的错误处理和日志记录。 通过以上步骤,可以实现将Excel数据导入MySQL数据库的功能。使用Java编写代码,可以灵活地处理不同类型的Excel文件和数据库表格,提高数据导入的效率和准确性。 ### 回答3: 要通过Java实现Excel数据导入MySQL数据库,可以按照以下步骤进行: 1. 首先,需要导入相关的Java库,如Apache POI用于解析Excel文件,以及MySQL驱动程序用于连接和操作MySQL数据库。 2. 在Java代码中,使用POI库来读取Excel文件的内容。可以使用Workbook类来加载Excel文件,并通过Sheet和Row类来遍历和读取工作表和行。 3. 在读取Excel数据时,可以根据需要进行数据处理和验证。例如,可以检查数据的有效性、格式是否正确,并将数据存储在Java对象中。 4. 英九读取Excel数据后,可以使用MySQL驱动程序来连接到MySQL数据库。将数据库连接信息提供给Java代码,以建立与数据库的连接。 5. 创建适当的数据库和表以存储Excel数据。可以使用MySQL Workbench或命令行工具来创建和管理数据库和表结构。 6. 使用Java代码将Excel数据插入到MySQL数据库中。可以使用PreparedStatement来执行插入操作,并将从Excel中读取的数据绑定到查询参数中。 7. 在数据插入过程中,可以通过事务管理来确保数据的完整性和一致性。通过使用事务,可以在一次操作中插入所有数据,并在失败时回滚所有操作。 8. 最后,检查和验证数据插入是否成功。可以使用SQL查询来检查插入的数据,并与Excel文件中的数据进行对比。 通过以上步骤,可以实现将Excel数据导入MySQL数据库的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值