欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用CSV文件批量导入数据库

程序员文章站 2022-06-11 13:27:51
...

说明

当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率。

CSV文件:逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。CSV是一种通用的、相对简单的文件格式,被用户、商业和科学广泛应用。最广泛的应用是在程序之间转移表格数据,而这些程序本身是在不兼容的格式上进行操作的(往往是私有的和/或无规范的格式)。因为大量程序都支持某种CSV变体,至少是作为一种可选择的输入/输出格式。

应用

package test;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class CsvInsert extends Thread {
	private static final String user = "test";
	private static final String pwd = "123";
	private static final String url = "jdbc:sqlserver://localhost:1433; DatabaseName=Person";
	private static final String driver = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
	private static String SEPARATOR = ",";	 		// CSV文件的分隔符为","
	private static int largest_numbers = 200; 	// 某些数据库一次批量最多为5W条数据,可调整
	private String file_path;

//	构造方法,传入文件位置
	public CsvInsert(String file_path) {
		this.file_path = file_path;
	}

//	重写线程run()方法
	@Override
	public void run() {
		this.insertDB(largest_numbers, this.file_path);
	}

	public static void main(String[] args) throws FileNotFoundException {
		CsvInsert insertDB1 = new CsvInsert("xxx/xxx/students.csv");
		insertDB1.start(); // 开启线程
	}

//	@SuppressWarnings("resource")
	public boolean insertDB(int rc, String file_path) {
//		System.out.println(file_path);
		Connection con = null;
		boolean flag = false;
		PreparedStatement pst = null;
		
//		计算一共插入多少条数据
		long rowCount = 0;
		
		String insertSql = "insert into students (学号,姓名,年级,学院,专业) values(?,?,?,?,?)";
		try {
			con = getCon();
			con.setAutoCommit(false); // 关闭自动提交,开启事务

			pst = con.prepareStatement(insertSql);

//			根据路径生成File
			File raf = new File(file_path);
			BufferedReader buf = null;
			
//			根据自己的需要可以改变这里读取文件的编码
			buf = new BufferedReader(new InputStreamReader(new FileInputStream(raf), "gb2312"));

			String line_record = buf.readLine();
			long sqlstart = System.currentTimeMillis(); 	// 开始计时

//			逐行读取
			while (line_record != null) {
//				解析每一条记录,按","切割数据
				String[] fields = line_record.split(SEPARATOR);

//				对Insert语句的合法性进行判断
				if (fields.length != 5) { 	// 要插入的列为5列
					System.out.println("csv文件中有"+fields.length+"列,要插入的数据列数和表的数据列不相匹配!停止执行!");
					System.out.println("问题出现在第"+rowCount+"行");
					break;
				}
				
				for (int i = 1; i <= 5; i++) {
					pst.setString(i, fields[i-1]);
					// 使用if、switch等,可以根据需要调整顺序,某列是别的类型也可以改变pst.setxxx的内容
				}

				rowCount++;
//				添加批
				pst.addBatch();
				
//				分段提交,每5W条数据提交一次
				if (rowCount % rc == 0 && rowCount != 0) {
//					执行批量导入
					pst.executeBatch();

					con.commit();
//					清除积攒的sql
					pst.clearBatch();
					
//					con.setAutoCommit(false);	// 开始事务
//					pst = con.prepareStatement(insertSql);
					System.out.println("已满" + rowCount + "条");
				}
//				下一行
				line_record = buf.readLine();
			}

//			最后提交剩余的不足5W条的数据
			pst.executeBatch();
//			清除原来的sql
//			pst.clearBatch();
			con.commit();
			
			System.out.println("共写入行数:" + rowCount);

			long sqlend = System.currentTimeMillis(); // 停止计时

			System.out.println("执行时间为:" + (sqlend - sqlstart) + " ms");
			buf.close();
			pst.close();
			con.setAutoCommit(true);	//再把自动提交打开
			con.close();
		} catch (Throwable e) {
			if (con != null) {
				try {
//					异常回滚
					System.out.println("出错在"+rowCount+"行");
					con.rollback();
				} catch (SQLException e1) {
					e1.printStackTrace();
				}
			}
			throw new RuntimeException(e);
		} finally {
			if (pst != null) {
				try {
					pst.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}

			if (con != null) {
				try {
					System.out.println("出错在"+rowCount+"行");
					con.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
		return flag;
	}

//	获取连接
	public static Connection getCon() {
		Connection con = null;
		try {
			Class.forName(driver).newInstance();
			con = DriverManager.getConnection(url, user, pwd);
			if (con != null) {
				System.out.println("你已连接到数据库:" + con.getCatalog());
			}
		} catch (Exception e) {
			System.out.println("连接数据库失败!");
			e.printStackTrace();
		}
		return con;
	}
}

1 采用PreparedStatement.addBatch()实现批处理,由于发送的是预编译后的SQL语句,执行效率高。由于没有采取自动提交的方式,使用Connection.setAutoCommit(false); 就必须注意回滚和事务提交具体解释请查看大神的解释->http://blog.csdn.net/macwhirr123/article/details/45578181

2 采用线程的方式处理文件,使得程序更加灵活

运行

数据库结构

使用CSV文件批量导入数据库

测试

使用CSV文件批量导入数据库