JDBC(4):批处理、存储过程和事务
来源:互联网 发布:加工中心倒角c怎么编程 编辑:程序博客网 时间:2024/06/02 00:41
批处理
需求
当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率。
主要API
添加批处理
void addBatch(String sql)
执行批处理
int[] executeBatch()
清空批处理
void clearBatch()
案例
程序有100个UserInfo对象(list集合),需要插入数据库,使用JDBC的批处理解决
表user_info结构
CREATE TABLE `user_info` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(20) DEFAULT NULL, `password` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`))
批处理实现方式1:Statement.addBatch(sql)
@Testpublic void BatchTest1(){ String driver = "com.mysql.jdbc.Driver"; String url = "jdbc:mysql://127.0.0.1:3306/address"; String user = "root"; String pwd = "password"; Connection conn = null; Statement statement = null; String sql1 = "insert into user_info(username,password) values('now','123')"; String sql2 = "update user_info set password='2333' where id=3"; try { //获取连接 Class.forName(driver); conn = (Connection) DriverManager.getConnection(url,user, pwd); statement = (Statement) conn.createStatement(); statement.addBatch(sql1); //把SQL语句加入到批命令中 statement.addBatch(sql2); //把SQL语句加入到批命令中 statement.executeBatch(); } catch (Exception e) { e.printStackTrace(); } finally { if (statement != null) { try { statement.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } }}
采用Statement.addBatch(sql)方式实现批处理:
优点:
可以向数据库发送多条不同的SQL语句。
缺点:
1.SQL语句没有预编译。
2.当向数据库发送多条语句相同,但仅参数不同的SQL语句时,需重复写上很多条SQL语句。如:
insert into user_info(username,password) values('user1','123');insert into user_info(username,password) values('user2','123');insert into user_info(username,password) values('user3','123');insert into user_info(username,password) values('user4','123');
批处理实现方式2:PreparedStatement.addBatch()
@Testpublic void BatchTest2(){ String driver = "com.mysql.jdbc.Driver"; String url = "jdbc:mysql://127.0.0.1:3306/address"; String user = "root"; String pwd = "password"; Connection conn = null; PreparedStatement psmt = null; String sql = "insert into user_info(username,password) values(?,?)"; try { //获取连接 Class.forName(driver); conn = (Connection) DriverManager.getConnection(url,user, pwd); psmt = (PreparedStatement) conn.prepareStatement(sql); for (int i = 0; i < 100; i++) { psmt.setString(1, "user"+i); psmt.setString(2, "pwd"+i); psmt.addBatch(); } psmt.executeBatch(); } catch (Exception e) { e.printStackTrace(); } finally { if (psmt != null) { try { psmt.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } }}
采用PreparedStatement.addBatch()实现批处理
优点:
发送的是预编译后的SQL语句,执行效率高。
缺点:
只能应用在SQL语句相同,但参数不同的批处理中。因此此种形式的批处理经常用于在同一个表中批量插入数据,或批量更新表的数据。
存储过程
创建存储过程
表user_info结构:
新建一个存储过程
my_procedure1:
CREATE PROCEDURE my_procedure1(IN inputId INT)BEGIN SELECT * FROM user_info WHERE id=inputId;END
sql调用以及输出结果
SQL语句:CALL pro_findById(5);
输出结果:
java代码调用及输出结果
@Test public void testCall() { String driver = "com.mysql.jdbc.Driver"; String url = "jdbc:mysql://127.0.0.1:3306/address"; String user = "root"; String pwd = "password"; Connection conn = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; String sql = "CALL my_procedure1(?)"; try { Class.forName(driver); conn = (Connection) DriverManager.getConnection(url,user, pwd); preparedStatement = (PreparedStatement) conn.prepareStatement(sql); preparedStatement.setInt(1, 3); resultSet = preparedStatement.executeQuery(); while( resultSet.next() ){ String username = resultSet.getString("username"); String password =resultSet.getString("password"); System.out.println("username:"+username +" password:"+password); } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } finally { if (preparedStatement != null) { try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } } }
输出结果:
username:user3 password:pwd3
创建带有输出参数的存储过程
有输出参数的存储过程,在使用java调用时没有ResultSet返回,是通过输出参数来获取执行结果的
新建一个有输出参数的存储过程
my_procedure2:
CREATE PROCEDURE my_procedure2(IN inputId INT, OUT outputname VARCHAR(20))BEGIN SELECT username into outputname FROM user_info WHERE id=inputId;END
输入参数为inputId,输出参数为outputname
sql调用以及输出结果
SQL语句(@name=outputname):CALL my_procedure2(3,@name);
获取输出参数结果:SELECT @name;
输出结果:
java代码调用及输出结果
@Test public void testCall2() { String driver = "com.mysql.jdbc.Driver"; String url = "jdbc:mysql://192.168.1.20:3306/address"; String user = "root"; String pwd = "zaqwsx123"; Connection conn = null; CallableStatement cstmt = null; String sql = "CALL my_procedure2(?,?)"; try { Class.forName(driver); conn = (Connection) DriverManager.getConnection(url,user, pwd); cstmt = (CallableStatement) conn.prepareCall(sql); //这里调用方法不同与testCall那个方法里面的方法,返回的Statement子类也不一样 cstmt.setInt(1, 3); //设置输出参数(注册输出参数) //参数一:参数位置 //参数二:存储过程中的输出参数的jdbc类型 VARCHAR(20) cstmt.registerOutParameter(2, java.sql.Types.VARCHAR); cstmt.executeQuery();//结果不是返回到结果集中,而是返回到输参数中 //得到输出参数的值 // 索引值:预编译sql中的输出参数的位置 String res = cstmt.getString(2);//专门获取存储过程中的输出参数 System.out.println(res); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } finally { if (cstmt != null) { try { cstmt.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } } }
输出结果:
user3
事务
定义
事务是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。
特性:原子性、一致性、隔离性、持久性(简称ACID)。
事务的三个方法
java.sql.Connection类中存在关于事务的三个方法:
- 关闭自动提交 Connection.setAutoCommit(false);
- 开启事务 Connection.rollback();
- 回滚事务 Connection.commit(); —提交事务
典型案例
前提
假设存在一张账户表(mysql表引擎必须为InnoDB,才支持事务), 表建立如下SQL语句,有用户名和账号余额字段,存在用户zhangsan和lisi,余额分别为5000和3000。
CREATE TABLE `account` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(20) DEFAULT NULL, `balance` double DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE= InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
案例1
张三向李四转让了1000,此时张三和李四余额分别为4000和1000,映射到具体代码即为执行两条SQL语句:1,将用户zhangsan的余额改为4000 2.将lisi的余额改为4000。假如执行完1后突然系统异常,2没执行。这是不允许的,我们希望1也能回滚一下,即将用户zhangsan的余额重改为5000。这个时候就需要用到事务。
使用事务代码:
@Test public void trans1(){ String driver = "com.mysql.jdbc.Driver"; String url = "jdbc:mysql://127.0.0.1:3306/address"; String user = "root"; String pwd = "password"; Connection conn = null; PreparedStatement preparedStatement = null; String sql_zs = "update account set balance=balance-1000 where username='zhangsan'"; String sql_ls = "update account set balance=balance+1000 where username='lisi' 2333"; //人为设置SQL语句错误 try { //获取连接 Class.forName(driver); conn = (Connection) DriverManager.getConnection(url,user, pwd); conn.setAutoCommit(false); //开启事务,需手动进行提交 //------------第一次 张三-1000----------------------- preparedStatement = (PreparedStatement) conn.prepareStatement(sql_zs); preparedStatement.executeUpdate(); //------------第二次 李四+1000----------------------- preparedStatement = (PreparedStatement) conn.prepareStatement(sql_ls); preparedStatement.executeUpdate(); } catch (Exception e) { try { conn.rollback(); //发生异常时,进行事务回滚操作 } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); throw new RuntimeException(e); } finally { try { conn.commit(); //最终提交事务 } catch (SQLException e1) { e1.printStackTrace(); } if (preparedStatement != null) { try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } } }
以上代码中,人为设置SQL语句String sql_lisi错误。不用事务提交回滚的话将会出现zhangsan的账户余额改为4000,而lisi的账户余额没变的情况。使用事务回滚zhangsan和lisi账户余额都没变。
案例2
张三第一次向李四转让了1000,此时转让成功,操作也都执行了,张三李四余额分别为都4000。第二次李四发现张三转多了,向张三转回500,这次出错了,错误跟案例1类似。也能回滚一下,但会滚到第一次转账结束后即可,这个时候需要用到SavePoint。
@Test public void trans2(){ String driver = "com.mysql.jdbc.Driver"; String url = "jdbc:mysql://127.0.0.1:3306/address"; String user = "root"; String pwd = "password"; Connection conn = null; PreparedStatement preparedStatement = null; String sql_zs1 = "update account set balance=balance-1000 where username='zhangsan'"; String sql_ls1 = "update account set balance=balance+1000 where username='lisi'"; Savepoint savepoint = null; String sql_zs2 = "update account set balance=balance+500 where username='zhangsan'"; String sql_ls2 = "update account set balance=balance-500 where username='lisi' 23333"; try { //获取连接 Class.forName(driver); conn = (Connection) DriverManager.getConnection(url,user, pwd); conn.setAutoCommit(false); //开启事务,需手动进行提交 //------------第一次 张三-1000 李四+1000 假设执行成功----------------------- preparedStatement = (PreparedStatement) conn.prepareStatement(sql_zs1); preparedStatement.executeUpdate(); preparedStatement = (PreparedStatement) conn.prepareStatement(sql_ls1); preparedStatement.executeUpdate(); //设置回滚点 savepoint = conn.setSavepoint(); //------------第二次 张三+500 李四-500 sql语句出错----------------------- preparedStatement = (PreparedStatement) conn.prepareStatement(sql_zs2); preparedStatement.executeUpdate(); preparedStatement = (PreparedStatement) conn.prepareStatement(sql_ls2); preparedStatement.executeUpdate(); } catch (Exception e) { try { conn.rollback(savepoint); //发生异常时,进行事务回滚到设置好的保存点 } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); throw new RuntimeException(e); } finally { try { conn.commit(); //最终提交事务 } catch (SQLException e1) { e1.printStackTrace(); } if (preparedStatement != null) { try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } } }
- JDBC(4):批处理、存储过程和事务
- JDBC存储过程、批处理、事务
- JDBC之批处理、事务、存储过程
- Java-JDBC调用批处理、存储过程、事务
- Java-JDBC调用批处理、存储过程、事务
- Java-JDBC调用批处理、存储过程、事务
- 批处理,主键,存储过程和事务
- JDBC事务和批处理
- 数据库JDBC 、事务、存储过程
- 批处理和存储过程
- java JDBC最基本的操作读取、调用存储过程、执行批处理、事务等
- java JDBC最基本的操作读取、调用存储过程、执行批处理、事务等
- JDBC 学习笔记(二)—— 大数据+存储过程+批处理+事务
- web 学习笔记15-JDBC大数据 批处理 存储过程 事务
- JDBC中执行存储过程和对事务的处理
- jdbc--的事务深入和存储过程的调用
- JDBC简单的处理事务和批处理
- jdbc批处理和事务的例子
- 自学Android之ContentProvider
- 5种IE hasLayoutt的属性及其值
- JPA学习笔记【六】【二级缓存,JPQL】
- JDBC(3):实现DDL、DML、DQL和代码优化
- [caffe笔记005]:通过代码理解faster-RCNN中的RPN
- JDBC(4):批处理、存储过程和事务
- ARM:Makefile编写、链接脚本编写、裸板shell框架
- Mysql 8.0 新增特性
- 《即学即用的APP时间管理术》笔记
- C语言中如何去理解预处理阶段
- 你应该了解的基础和2017测试行业的趋势
- Qt之QThread用法
- 51NOD 1109 01组成的N的倍数
- Android FrameWork源码浅析之(二)--转发一篇文章讲binder