JDBC--大数据+存储过程+批处理+事物管理
?
????????????????st.setString(2, "123" + i);
?
????????????????st.setString(3, "aaa" + i + "@sina.com");
?
????????????????st.setDate(4,new Date(1980, 10, 10));
?
?????????????????
?
????????????????st.addBatch();
?
????????????????if(i%1000==0){??????//为防止(list集合) 内存溢出:设定每累加1000条数据就向数据库发送一次
?
????????????????????????st.executeBatch();
?
????????????????????????st.clearBatch();
?
????????????????}
?
????????}
?
????????st.executeBatch();?//当剩余的条数小于1000条时就不会被发送到数据库,所以此处要在发送一次。
?
}
?
?
?
?
?
?
8、采用PreparedStatement.addBatch()实现批处理的优缺点
?
????优点:发送的是预编译后的SQL语句,执行效率高。
?
????缺点:只能应用在SQL语句相同,但参数不同的批处理中。因此此种形式的批处理经常用于在同一个表中批量插入数据,或批量更新表的数据。
?
?
?
?
?
?
9、获得MySQL数据库自动生成的主键
?
????示例:
?
????????Connection conn = JdbcUtil.getConnection();?
?
????????String sql = "insert into user(name,password,email,birthday) ?values('abc','123','abc@sina.com','1978-08-08')";
?
????????????????????????????????????????????????????????????? //重载函数:返回生成的自动主键
?
????????PreparedStatement st = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS );?????????
?
????????st.executeUpdate();
?
?
?
????????ResultSet rs = st.getGeneratedKeys(); ?//得到插入行的主键
?
????????if(rs.next())
?
?????????????System.out.println(rs.getObject(1));
?
????注:此参数仅对insert操作有效。
?
?
?
?Demo样例:?
?
public?class?Demo4?{?
?
??/**
?
???获取自动生成的主键
?
???create?table?test
?
???(
?
?????????id?int?primary?key?auto_increment,
?
?????????name?varchar(40)
?
???);?
?
???*/
?
??public?static?void?main(String[]?args)?throws?SQLException?{?
?
????????Connection?conn?=?null;
?
????????PreparedStatement?st?=?null;
?
????????ResultSet?rs?=?null;?
?
????????try{
?
??????????????conn?=?JdbcUtils.getConnection();
?
??????????????String?sql?=?"insert?into?test(name)?values('aaa')";
?
??????????????st?=?conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
?
??????????????st.executeUpdate();
?
?????????
?
??????????????rs?=?st.getGeneratedKeys();
?
??????????????if(rs.next()){
?
????????????????????System.out.println(rs.getInt(1));
?
??????????????}?
?
????????}finally{
?
??????????????JdbcUtils.release(conn,?st,?rs);
?
????????}
?
??}?
?
}
?
?
?
?
?
?
?
?
?
10、JDBC调用存储过程
?
编写存储过程(参看mysql文档)
?
本文重在JDBC对存储过程的调用,关于其知识内容,将在《Oracle数据库 知识》博文中详细介绍
?
?存储过程Demo样例:
?
?CREATE?PROCEDURE?demoSp (IN inputParam VARCHAR(255) ,INOUT??inOutParam varchar(255) )
?
???? BEGIN
?
???????????? SELECT?CONCAT ( 'ZYXW---- ',?inputParam?) into? inOutParam ;
?
???? END
?
?
?
在Java中,JDBC对存储过程的调用 (这才是本文重点):
?
????(1) 得到CallableStatement,并调用存储过程:
?
????????????CallableStatement cStmt = conn.prepareCall("{call demoSp(?, ?)}");?????
?
????(2) 设置参数,注册返回值,得到输出
?
????????????cStmt.setString(1, "abcdefg");
?
????????????cStmt.registerOutParameter(2, Types.VARCHAR);????//类型参数值参见JDK中:java.sql.Types
?
????????????cStmt.execute();
?
????????????System.out.println(cStmt.getString(2));
?
小常识:“存储过程”在金融证券 行业中应用的非常广泛,并且将会保密其表结构和字段,完全使用公开的存储过程来实现表数据的调用。
?
Demo样例:
?
????public?class?Demo5?{?
?
??????????????/**
?
???????????????调用存储过程
?
???????????????*?@throws?SQLException?
?
???????????????*/
?
??????????????public?static?void?main(String[]?args)?throws?SQLException?{
?
?????????????
?
????????????????????Connection?conn?=?null;
?
????????????????????CallableStatement??st?=?null;
?
????????????????????ResultSet?rs?=?null;
?
?????????????????
?
????????????????????try{
?
??????????????????????????conn?=?JdbcUtils.getConnection();
?
??????????????????????????st?=?conn.prepareCall("{call?demoSp(?,?)}");
?
??????????????????????????st.setString(1,?"aaaaa");
?
??????????????????????????st.registerOutParameter(2,?Types.VARCHAR);
?
??????????????????????????st.execute();
?
?????????????????????
?
??????????????????????????System.out.println(st.getString(2));?
?
????????????????????}finally{
?
??????????????????????????JdbcUtils.release(conn,?st,?rs);
?
????????????????????}
?
??????????????}?
?
????}
?
?
?
?
?
??
?
11、事务的概念
?
????????事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部都成功,要么全部不成功。
?
????????例如:A——B转帐,对应于如下两条sql语句
?
?????????????????update from account set money=money+100 where name=‘b’;
?
?????????????????update from account set money=money-100 where name=‘a’;
?
????????数据库默认事务是自动提交的,也就是发一条sql它就执行一条。如果想多条sql放在一个事务中执行,则需要使用如下语句。
?
????????数据库开启事务命令
?
????????start transaction ?开启事务
?
????????Rollback ?回滚事务
?
????????Commit ? 提交事务
?
?????????
?
????????Demo:
?
????????????Start transaction
????????????????…
????????????????…
????????????commit
?
?
?
Demo样例:
?
public?static?void?main(String[]?args)?throws?SQLException?{?
?
????????Connection?conn?=?null;
?
????????PreparedStatement?st?=?null;
?
????????ResultSet?rs?=?null;
?
????????try{
?
??????????????conn?=?JdbcUtils.getConnection();
?
??????????????conn.setAutoCommit(false);????//start?transaction;? 开启事务
?
?????????
?
??????????????String?sql1?=?"update?account?set?money=money-100?where?name='aaa'";
?
??????????????String?sql2?=?"update?account?set?money=money+100?where?name='bbb'";
?
?????????
?
??????????????st?=?conn.prepareStatement(sql1);
?
??????????????st.executeUpdate();
?
?????????
?
??????????????int?x?=?1/0;????// 人为制造异常,验证事务
?
?????????
?
??????????????st?=?conn.prepareStatement(sql2);
?
??????????????st.executeUpdate();
?
?????????
?
??????????????conn.commit();????// Commit ? 提交事务
?
????????}finally{
?
??????????????JdbcUtils.release(conn,?st,?rs);
?
????????}?
?
??}
?
?
?
?
?
?
12、JDBC控制事务语句
?
????当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句。若想关闭这种默认提交方式,让多条SQL在一个事务中执行,可使用下列语句:
?
????(1) JDBC控制事务语句
?
????????Connection.setAutoCommit(false); ????//相当于 start transaction
?
????????Connection.rollback();?????//相当于??rollback
?
????????Connection.commit();?????//相当于? ?commit
?
?
?
????(2) 设置事务回滚点
?
????????Savepoint sp = conn.setSavepoint();
?
????????Conn.rollback(sp);????// 会滚到指定的位置,该位置之前的操作任然有效执行,因为需要被提交
?
????????Conn.commit(); ? //回滚后必须要提交
?
?
?
Demo样例:事务回滚点——演示银行转帐案例
?
??public?static?void?main(String[]?args)?throws?SQLException?{?
?
????????Connection?conn?=?null;
?
????????PreparedStatement?st?=?null;
?
????????ResultSet?rs?=?null;
?
????????Savepoint?sp?=?null;
?
????????try{
?
??????????????conn?=?JdbcUtils.getConnection();???//mysql?repeatable?read
?
??????????????conn.setAutoCommit(false);????//start?transaction;
?
?????????
?
??????????????String?sql1?=?"update?account?set?money=money-100?where?name='aaa'";
?
??????????????String?sql2?=?"update?account?set?money=money+100?where?name='bbb'";
?
??????????????String?sql3?=?"update?account?set?money=money+100?where?name='ccc'";
?
?????????
?
??????????????st?=?conn.prepareStatement(sql1);
?
??????????????st.executeUpdate();
?
?????????
?
??????????????sp?=?conn.setSavepoint();
?
?????????
?
??????????????st?=?conn.prepareStatement(sql2);
?
??????????????st.executeUpdate();
?
?????????
?
??????????????int?x?=?1/0;
?
?????????
?
??????????????st?=?conn.prepareStatement(sql3);
?
??????????????st.executeUpdate();
?
?????????
?
??????????????conn.commit();
?
????????}catch?(Exception?e)?{
?
??????????????e.printStackTrace();
?
??????????????conn.rollback(sp);
?
??????????????conn.commit();??//手动回滚后,一定要记得提交事务
?
????????}finally{
?
??????????????JdbcUtils.release(conn,?st,?rs);
?
????????}
?
??}
?
?
?
?
?
?
13、事务的四大特性(ACID)
?
????(1) 原子性(Atomicity) 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
?
????(2) 一致性(Consistency) 事务前后,数据的完整性必须保持一致。
?
????(3) 隔离性(Isolation) 事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
?
????(4) 持久性(Durability) 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
?
?
?
?
?
?
14、事务的隔离级别
?
????????多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。
?
????????如果不考虑隔离性,可能会引发问题:脏读、不可重复读、虚读(幻读) 。
?
?????????
?
?
?
?
15、事务的隔离性可避免问题—— 脏读:
?
????脏读: 指一个事务读取了另外一个事务未提交的数据。【针对同一个业务操作】
?
?????这是非常危险的,假设A向B转帐货款100元,对应sql语句如下所示
?
?????(1) update account set money=money+100 while name=‘b’; ?
?
?????(2) update account set money=money-100 while name=‘a’;
?
?????
?
?????当第1条sql执行完,第2条还没执行(A未提交时),如果此时B查询自己的帐户,就会发现自己多了100元钱。如果A等B走后再回滚,B就会损失100元。
?
?
?
?
?
?
16、事务的隔离性可避免问题—— 不可重复读
?
不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同。【针对同一行数据】
?
不可重复读和脏读的区别:脏读是读取前一事务未提交的脏数据,不可重复读是重新读取了前一事务已提交的数据。
?
?????????例如银行想查询A帐户余额,第一次查询A帐户为200元,此时A向帐户存了100元并提交了,银行接着又进行了一次查询,此时A帐户为300元了。银行两次查询不一致,可能就会很困惑,不知道哪次查询是准的。
?
?????????
?
????????很多人认为这种情况就对了,无须困惑,当然是后面的为准。我们可以考虑这样一种情况,比如银行程序需要将查询结果分别输出到电脑屏幕和写到文件中,结果在一个事务中针对输出的目的地,进行的两次查询不一致,导致文件和屏幕中的结果不一致,银行工作人员就不知道以哪个为准了。
?
?
?
?
?
?
17、事务的隔离性可避免问题—— 虚读(幻读)
?
虚读(幻读) :是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。【针对整张表】
?
如丙存款100元未提交,这时银行做报表统计account表中所有用户的总额为500元,然后丙提交了,这时银行再统计发现帐户为600元了,造成虚读同样会使银行不知所措,到底以哪个为准。
?
?
?
?
?
?
18、事务隔离性的设置语句
?
????数据库共定义了四种隔离级别:
?
????????????(1) Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化,近似于单线程操作。)
?
????????????(2) Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)
?
????????????(3) Read committed:可避免脏读情况发生(读已提交)。
?
????????????(4) Read uncommitted:最低级别,以上情况均无法保证。(读未提交)
?
?????????set ? transaction isolation level 设置事务隔离级别
?
?????????select @@tx_isolation ?查询当前事务隔离级别
?
?
?
Demo样例:Java中设定事务的隔离级别。
?
public?static?void?main(String[]?args)?throws?SQLException,?InterruptedException?{
?
????????Connection?conn?=?null;
?
????????PreparedStatement?st?=?null;
?
????????ResultSet?rs?=?null;
?
????????Savepoint?sp?=?null;
?
????????try{
?
??????????????conn?=?JdbcUtils.getConnection();???//mysql?repeatable?read
?
??????????????conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
?
??????????????conn.setAutoCommit(false);????//start?transaction;
?
?????????
?
??????????????String?sql?=?"select?*?from?account";
?
??????????????conn.prepareStatement(sql).executeQuery();
?
?????????
?
??????????????Thread.sleep(1000*20);?????// 等待另一个程序运行同样的代码以校验事务
?
?????????
?
??????????????conn.commit();
?
????????}finally{
?
??????????????JdbcUtils.release(conn,?st,?rs);
?
????????}
?
??}
?
?
?
?
?
?
?19、在MySQL客户端窗口界面掩饰事务的四种隔离级别。
?
(1) 演示脏读发生?
?
a窗口
?
set?transaction?isolation?level?read?uncommitted;
?
start?transaction;
?
select?*?from?account;
?
-------------发现a帐户是1000元,转到b窗口
?
select?*?from?account;
?
-------------发现a帐户是1100元,发生了脏读(这个事务读取到了别的事务未提交的数据)?
?
b窗口
?
start?transaction;
?
update?account?set?money=money+100?where?name='aaa';
?
-------------事务在不提交的情况下,转到a窗口进行查询?
?
?
?
(2) 避免脏读,并演示不可重复读问题的发生?
?
a窗口
?
set?transaction?isolation?level?read?committed;
?
start?transaction;
?
select?*?from?account;
?
-------------发现a帐户是1000元,转到b窗口
?
select?*?from?account;
?
-------------发现a帐户是1000元,这时我们发现read?committed这种级别可以避免脏读
?
-------------转到b窗口
?
select?*?from?account;
?
-------------发现a帐户是1100元,这时就发生不可重复读(指这个事务读取到了别的事务提交的数据)
?
?
?
b窗口
?
start?transaction;
?
update?account?set?money=money+100?where?name='aaa';
?
-------------事务在不提交的情况下,转到a窗口进行查询
?
commit;
?
-------------转到a窗口
?
?
?
(3)避免脏读、不可重复读,并演示虚读问题的发生?
?
a窗口
?
set?transaction?isolation?level?repeatable?read;
?
start?transaction;
?
select?*?from?account;
?
--------------------发现a帐户是1000元,并且表的总纪录数是3条,这时转到b窗口
?
select?*?from?account
?
--------------------发现a帐户是1000元,这说明repeatable?read这种级别可避免脏读
?
-------------------转到b窗口
?
select?*?from?account
?
---------------------发现a帐户是1000元,这说明repeatable?read这种级别还可以避免不可重复读
?
---------------------转到b窗口
?
select?*?from?account
?
---------------------发现表中可能会多出一条ddd的记录,这就发生了虚读,也就是在这个事务内读取了别的事务插入的数据(幻影数据)
?
??
?
b窗口
?
start?transaction;
?
update?account?set?money=money+100?where?name='aaa';
?
---------------------转到a窗口
?
commit;
?
---------------------转到a窗口
?
start?transaction;
?
insert?into?account(name,money)?values('ddd','1000');
?
commit;
?
--------------------转到a窗口?
?
??
?
?
?
?