JAVA使用JDBC批量插入SQL

数据库连接使用proxool连接JDBC


代码 JAVA类


package com.czr.proxool;

import org.logicalcobwebs.proxool.ProxoolException;
import org.logicalcobwebs.proxool.configuration.JAXPConfigurator;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * 使用proxool连接orcale
 * 当使用JDBC操作数据时出现起出打开游标最大数的原因:一般出现在执行大量插入语句 时,没有关闭Statement.
解决方法:每次插入一条数据时或找执行一次批量提交时,记得
 * @author chenzhirong
 */
public class ProxoolUtile {
	public static String dburl = "jdbc:oracle:thin:@192.168.104.192:1521:tim";
	public static String user = "tim";
	public static String password = "tim_8968888";
	private static ProxoolUtile proxoolUtile;
	private static Connection conn;
	private static Statement stmt;
	private static int count;

	public static ProxoolUtile getProxoolUtile() {
		// TODO Auto-generated method stub
		if (proxoolUtile == null) {
			proxoolUtile = new ProxoolUtile();
		}
		return proxoolUtile;
	}

	/**
	 * JDBC方式测试
	 * 
	 * @throws Exception
	 */
	public static void test1() throws Exception {
		String testsql = "select * from village t where lastid = 346";
		// 1:注册驱动类
		Class.forName("oracle.jdbc.driver.OracleDriver");
		// 2:创建数据库连接
		Connection conn = DriverManager.getConnection(dburl, user, password);
		// 3:创建执行SQL的对象
		Statement stmt = conn.createStatement();
		// 4:执行SQL,并获取返回结果
		ResultSet rs = stmt.executeQuery(testsql);
		// 5:处理返回结果,此处打印查询结果
		while (rs.next()) {
			System.out.print(rs.getLong("id") + "\t");
			System.out.print(rs.getString("name") + "\t");
			System.out.println();
		}
		// 6:关闭数据库连接
		conn.close();
	}

	/**
	 * proxool方式测试 创建数据库连接
	 * 
	 * @throws Exception
	 */
	public static Connection getConn() {
			// Java应用中先要加载配置文件,否则谁知道你配置给谁用的
			try {
				if (conn == null) {
					JAXPConfigurator.configure("proxool.xml", false);
					// 1:注册驱动类,这次这个驱动已经不是Oracle的驱动了,是Proxool专用的驱动
					Class.forName("org.logicalcobwebs.proxool.ProxoolDriver");
					// 2:创建数据库连接,这个参数是一个字符串,是数据源的别名,在配置文件中配置的timalias,参数格式为:proxool.数据源的别名
				}
					if (conn == null || conn.isClosed() == true) {
					conn = DriverManager.getConnection("proxool.DBczr");
				}
			} catch (ProxoolException e) {
				System.out.println("配置文件不正確");
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				System.out.println("加載驱动");
				e.printStackTrace();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				System.out.println("数据库连接断开");
				e.printStackTrace();
			}
		return conn;
	}

	/**
	 * 使用Statement批量提交SQL
	 * 
	 * @param sql SQL语句
	 * @return
	 */
	public int[] insertStmtBatchSql(String sql) {
		int[] i = null;
		if (count == 0) {
			try {
				if(conn == null || conn.isClosed() == true){
				 conn = ProxoolUtile.getConn();
				}
				stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
						ResultSet.CONCUR_READ_ONLY);
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
		}
		try {
			// 增加批量语句
			if(stmt == null){
				stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
						ResultSet.CONCUR_READ_ONLY);
			}
			stmt.addBatch(sql);
			count++;
			// 500条SQL时,执行批量提交
			if (count > 500) {
				i = stmt.executeBatch();
				conn.commit();
				stmt.close();
				conn.close();
				count = 0;
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return i;
	}

	/**
	 * 
	 * Statement
	 * 不完整个,自己加SQL语句参数
	 * @param sql
	 * @return
	 */
	public void insertBatchSql(String sql) {
		int count = 0;
		try {
			if (conn == null) {
				// Java应用中先要加载配置文件,否则谁知道你配置给谁用的
				JAXPConfigurator.configure("proxool.xml", false);
				// 1:注册驱动类,这次这个驱动已经不是Oracle的驱动了,是Proxool专用的驱动
				Class.forName("org.logicalcobwebs.proxool.ProxoolDriver");
				// 2:创建数据库连接,这个参数是一个字符串,是数据源的别名,在配置文件中配置的timalias,参数格式为:proxool.数据源的别名
				conn = DriverManager.getConnection("proxool.DBczr");
				// 设置手动提交
				conn.setAutoCommit(false);
			}
			PreparedStatement ps = conn.prepareStatement(sql);
			count++;// 记录
			if (count % 500 == 0) {// 当增加了500个批处理的时候再提交
				ps.executeBatch();// 执行批处理
				conn.commit();// 提交
				stmt.close();
				conn.close();// 关闭数据库
			}
		} catch (ProxoolException e) {
			System.out.println("配置文件不正確");
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			System.out.println("加載驱动");
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			System.out.println("数据库连接断开");
			e.printStackTrace();
		}
	}

	/**
	 * 查询序列
	 * Statement记得关闭,不然使用批量插入会有问题
	 * @param sql
	 * @return
	 */
	public Long querySeq(String sql) {
		Long seq = Long.valueOf(1);
//		Connection conn = null;
		Statement stmt = null;
		try {
			if(conn == null || conn.isClosed() == true ){
				conn = ProxoolUtile.getConn();
			}
				stmt = conn.createStatement();
			ResultSet rs = stmt.executeQuery(sql);
			while (rs.next()) {
				seq = rs.getLong("nextval");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			try {
				stmt.close();
//				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return seq;
	}

	/**
	 * 关闭连接
	 */
	public static void closeConn() {
		try {
			if (conn != null) {
				
				conn.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) throws Exception {
		// getStatement();
	}
}

proxool  XML配置文件


<?xml version="1.0" encoding="UTF-8"?> 
<something-else-entirely> 
        <proxool> 
                <alias>DBczr</alias> 
                <!--数据源的别名 10.142.54.121 -->
                <driver-url>jdbc:oracle:thin:@127.0.0.1:1521:orcl</driver-url> 
                <!--url连接串--> 
                <driver-class>oracle.jdbc.driver.OracleDriver</driver-class> 
                <!--驱动类--> 
                <driver-properties> 
                        <property name="user" value="czr"/> 
                        <!--用户名--> 
                        <property name="password" value="czr"/>
                        <!--密码--> 
                </driver-properties> 
                <!--最大连接数(默认5个),超过了这个连接数,再有请求时,就排在队列中等候,最大的等待请求数由maximum-new-connections决定 --> 
                <maximum-connection-count>100</maximum-connection-count> 
                <!--最小连接数(默认2个)--> 
                <minimum-connection-count>10</minimum-connection-count> 
                <!--proxool自动侦察各个连接状态的时间间隔(毫秒),侦察到空闲的连接就马上回收,超时的销毁 默认30秒--> 
                <house-keeping-sleep-time>90000</house-keeping-sleep-time> 
                <!--没有空闲连接可以分配而在队列中等候的最大请求数,超过这个请求数的用户连接就不会被接受--> 
                <maximum-new-connections>10</maximum-new-connections> 
                <!--最少保持的空闲连接数(默认2个)--> 
                <prototype-count>5</prototype-count> 
                <!--在使用之前测试--> 
                <test-before-use>true</test-before-use> 
                <!--用于保持连接的测试语句 --> 
                <house-keeping-test-sql>select sysdate from dual</house-keeping-test-sql> 
        </proxool> 
</something-else-entirely>


耍要的JAR包


dom4j-1.6.1.jar
log4j-1.2.13.jar
ojdbc14.jar
proxool-0.9.1.jar
proxool-cglib.jar

<think>在Java使用JDBC批量插入数据可以提高数据库插入操作的效率,尤其是当需要插入大量数据时。批量插入可以减少与数据库的交互次数,从而显著减少网络传输开销和数据库处理时间。 ### 实现步骤: 1. 加载数据库驱动(对于JDBC 4.0及以上,通常可以自动加载)。 2. 建立数据库连接。 3. 创建PreparedStatement对象,使用参数化SQL语句。 4. 关闭自动提交(以便手动控制事务)。 5. 循环设置参数,并添加到批处理中。 6. 执行批处理。 7. 提交事务(如果之前关闭了自动提交)。 8. 处理异常,必要时回滚事务。 9. 关闭资源。 ### 示例代码: 以下是一个使用JDBC批量插入数据的示例代码: ```java import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; public class BatchInsertExample { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/testdb"; String user = "username"; String password = "password"; Connection conn = null; PreparedStatement pstmt = null; try { // 1. 建立连接 conn = DriverManager.getConnection(url, user, password); // 2. 设置手动提交事务 conn.setAutoCommit(false); String sql = "INSERT INTO users (name, email) VALUES (?, ?)"; pstmt = conn.prepareStatement(sql); // 3. 批量插入数据 for (int i = 1; i <= 1000; i++) { pstmt.setString(1, "User" + i); pstmt.setString(2, "user" + i + "@example.com"); pstmt.addBatch(); // 添加到批处理 // 每100条执行一次批处理,避免内存溢出 if (i % 100 == 0) { pstmt.executeBatch(); conn.commit(); // 提交事务 } } // 执行剩余的批处理 pstmt.executeBatch(); conn.commit(); // 提交事务 System.out.println("批量插入完成!"); } catch (SQLException e) { e.printStackTrace(); try { if (conn != null) { conn.rollback(); // 回滚事务 } } catch (SQLException ex) { ex.printStackTrace(); } } finally { // 4. 关闭资源 try { if (pstmt != null) pstmt.close(); if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } ``` ### 关键点解释: 1. **关闭自动提交**:通过`conn.setAutoCommit(false)`关闭自动提交,这样可以将多个插入操作作为一个事务处理,要么全部成功,要么全部失败(通过回滚)。 2. **批处理添加**:使用`addBatch()`方法将参数化的预编译语句添加到批处理中。 3. **分批执行**:在循环中,每100条记录执行一次批处理并提交,这样可以避免一次性添加太多数据导致内存溢出。 4. **事务提交**:在批处理执行后,需要手动提交事务(`conn.commit()`)。 5. **异常处理**:在catch块中打印异常,并回滚事务。在finally块中关闭资源,确保资源释放。 ### 注意事项: - **批处理大小**:根据实际情况调整批处理的大小(如每100条、500条等),以平衡内存使用和性能。 - **事务管理**:批量插入通常在一个事务中完成,如果插入的数据量非常大,可能需要考虑分多个事务提交,以避免长事务和锁表问题。 - **数据库驱动**:不同的数据库(如MySQL、Oracle、PostgreSQL等)对批处理的支持可能有所不同,需要确保使用的驱动支持批处理操作。 - **内存管理**:如果一次性添加太多数据到批处理中,可能会导致内存溢出(OutOfMemoryError),因此建议分批次执行。 ### 相关问题: §§相关问题§§: 1. 在JDBC中,如何设置事务的隔离级别? 2. 使用PreparedStatement和Statement在批处理中有什么区别? 3. 如何处理JDBC批处理过程中的异常? 4. 在批量插入时,如何获取自动生成的主键?</think>### Java使用JDBC批量插入数据 在Java中,使用JDBC批量插入数据可以显著提高数据库操作效率,特别是当需要插入大量数据时。以下是完整的实现方法和代码示例: #### 方法实现 ```java import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; public class BatchInsertExample { // 数据库连接信息 private static final String URL = "jdbc:mysql://localhost:3306/testdb"; private static final String USER = "username"; private static final String PASSWORD = "password"; public static void main(String[] args) { try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD)) { // 1. 关闭自动提交(关键步骤!) conn.setAutoCommit(false); // 2. 准备SQL语句(使用占位符) String sql = "INSERT INTO employees (name, email, department) VALUES (?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(sql)) { // 3. 批量设置参数 for (int i = 1; i <= 1000; i++) { pstmt.setString(1, "Employee_" + i); // name pstmt.setString(2, "emp" + i + "@company.com"); // email pstmt.setString(3, "Dept_" + (i % 5)); // department pstmt.addBatch(); // 添加到批处理 // 4. 分段执行(每100条执行一次) if (i % 100 == 0) { pstmt.executeBatch(); conn.commit(); // 提交事务 } } // 5. 执行剩余批处理 int[] updateCounts = pstmt.executeBatch(); conn.commit(); // 最终提交 System.out.println("批量插入完成,影响行数: " + updateCounts.length); } } catch (SQLException e) { e.printStackTrace(); // 实际应用中应添加事务回滚逻辑 } } } ``` #### 关键优化点 1. **事务管理**: - `conn.setAutoCommit(false)`:禁用自动提交(核心优化) - 手动分批提交事务(`conn.commit()`),避免单条提交的开销 2. **批处理机制**: - `addBatch()`:将参数化语句加入批处理队列 - `executeBatch()`:一次性发送整批SQL到数据库 - 返回`int[]`数组表示每条SQL影响的行数 3. **分段执行**: - 每100条执行一次,避免内存溢出(OOM) - 根据数据库配置调整批次大小(通常500-2000条/批) #### 性能对比 | 插入方式 | 10,000条耗时 | 资源消耗 | |----------------|--------------|----------| | 单条插入 | 15-20秒 | 高 | | 批量插入 | 0.5-2秒 | 低 | | 带事务的批量 | 0.3-1秒 | 最低 | #### 常见问题原因 1. **内存溢出(OutOfMemoryError)**: - 原因:一次性添加过多批处理未及时执行 - 解决:分批执行(如每500条执行一次) 2. **事务超时**: - 原因:单批次数据量过大,执行时间超过数据库等待超时 - 解决:减小批次大小或增加`wait_timeout` 3. **主键冲突**: - 原因:批量操作中部分数据违反唯一约束 - 解决:使用`try-catch`捕获`BatchUpdateException`,通过`getUpdateCounts()`定位失败行 4. **连接泄漏**: - 原因:未正确关闭`PreparedStatement`和`Connection` - 解决:使用try-with-resources语法(Java 7+) #### 高级技巧 ```java // 1. 获取自动生成的主键(需在SQL中指定) PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); // 2. 批处理异常处理 try { pstmt.executeBatch(); } catch (BatchUpdateException e) { int[] successCounts = e.getUpdateCounts(); // 获取成功执行的计数 // 处理部分失败的情况 } // 3. 使用BatchedStatements(MySQL优化) // 在JDBC URL后添加参数:jdbc:mysql://...?rewriteBatchedStatements=true ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值