本文目录:

      1、使用JDBC处理大数据 

      2、使用JDBC处理大文本 

      3、使用JDBC处理二进制数据 

      4、Oracle中大数据处理 

      5、使用JDBC进行批处理 

      6、采用Statement.addBatch(sql)方式实现批处理的优缺点

      7、实现批处理的第二种方式:PreparedStatement.addBatch() 

      8、采用PreparedStatement.addBatch()实现批处理的优缺点

      9、获得MySQL数据库自动生成的主键 

      10、JDBC调用存储过程 

      11、事务的概念

      12、JDBC控制事务语句 

      13、事务的四大特性(ACID) 

      14、事务的隔离级别 

      15、事务的隔离性可避免问题—— 脏读: 

      16、事务的隔离性可避免问题—— 不可重复读 

      17、事务的隔离性可避免问题—— 虚读(幻读) 

      18、事务隔离性的设置语句 

      19、在MySQL客户端窗口界面演示事务的四种隔离级别

1、使用JDBC处理大数据

在实际开发中,程序需要把大文本或二进制数据保存到数据库。

基本概念:大数据也称之为LOB(Large Objects),LOB又分为:clob和blob

(a)clob用于存储大文本。(mysql 中采用Text)

(b)blob用于存储二进制数据,例如图像、声音、二进制文等。

对MySQL而言只有blob,而没有clob,mysql存储大文本采用的是Text,其体系中的Text和blob分别又分为:

(a)Text ——TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT

(b)blob ——TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB

2、使用JDBC处理大文本

(1) 保存数据—— 对于MySQL中的Text类型,可调用如下方法设置:

PreparedStatement.setCharacterStream(i, reader, length);

//注意length长度须设置,并且设置为int型

(2) 获取数据—— 对于MySQL中的Text类型,可调用如下方法获取:

(a) reader = resultSet. getCharacterStream(i);

(b) reader = resultSet.getClob(i).getCharacterStream();

(c) string s = resultSet.getString(i);

 Demo样例:

 public class Demo1 {
/**读写大文本
create table testclob
(
id varchar(40) primary key,
resume text
); */ @Test
public void insert() throws SQLException, FileNotFoundException{
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "insert into testclob(id,resume) values(?,?)";
st = conn.prepareStatement(sql);
st.setString(, "");
File file = new File("src/1.txt");
FileReader reader = new FileReader(file);
st.setCharacterStream(, reader, (int) file.length());
int num = st.executeUpdate();
if(num>){
System.out.println("插入成功!!");
}
}finally{
JdbcUtils.release(conn, st, rs);
}
}

3、使用JDBC处理二进制数据

(1) 保存数据—— 对于MySQL中的BLOB类型,可调用如下方法设置:

PreparedStatement. setBinaryStream(i , inputStream, length);

(2) 获取数据—— 对MySQL中的BLOB类型,可调用如下方法获取:

(a)  InputStream in  = resultSet.getBinaryStream(i);

(b) InputStream in  = resultSet.getBlob(i).getBinaryStream();

 Demo样例:

 public class Demo2 {
/*
create table testblob
(
id varchar(40) primary key,
image blob
);
*/
@Test
public void insert() throws SQLException, FileNotFoundException{
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "insert into testblob(id,image) values(?,?)";
st = conn.prepareStatement(sql);
st.setString(, "");
File file = new File("src/1.jpg");
FileInputStream in = new FileInputStream(file);
st.setBinaryStream(, in, (int) file.length());
st.executeUpdate();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
@Test
public void read() throws SQLException, IOException{
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "select id,image from testblob where id='1'";
rs = conn.prepareStatement(sql).executeQuery();
if(rs.next()){
InputStream in = rs.getBinaryStream("image");
OutputStream out = new FileOutputStream("c:\\1.jpg");;
try {
int len = ;
byte buffer[] = new byte[];
while ((len = in.read(buffer)) > ) {
out.write(buffer, , len);
}
} finally {
if (in != null)
in.close();
if (out != null)
out.close();
}
}
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}

4、Oracle中大数据处理

Oracle定义了一个BLOB字段用于保存二进制数据,但这个字段并不能存放真正的二进制数据,只能向这个字段存一个指针,然后把数据放到指针所指向的Oracle的LOB段中, LOB段是在数据库内部表的一部分。

因而在操作Oracle的Blob之前,必须获得指针(定位器)才能进行Blob数据的读取和写入。

如何获得表中的Blob指针呢? 可以先使用insert语句向表中插入一个空的blob(调用oracle的函数empty_blob()  ),这将创建一个blob的指针,然后再把这个empty的blob的指针查询出来,这样就可得到BLOB对象,从而读写blob数据了。

Oracle中LOB类型的处理步骤

(1)  插入空blob ——  insert into test(id,image) values(?,empty_blob());

(2)  获得blob的cursor ——  select image from test where id= ? for update;

Blob b = rs.getBlob(“image”);

注意:  须加for update,锁定该行,直至该行被修改完毕,保证不产生并发冲突。

(3)  利用 io,和获取到的cursor往数据库读写数据

注意:以上操作需开启事务。

备注:本文关于Oracle中LOB类型数据处理的操作仅供参考,详细内容参见 有关Oracle的博文。

5、使用JDBC进行批处理

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

实现批处理有两种方式,

第一种方式: Statement.addBatch(sql) (其实是将sql语句 放在了一个 list 集合中。)

第二种方式:  PreparedStatement.addBatch() (其实是将sql语句 放在了一个 list 集合中。)

执行批处理SQL语句

executeBatch()方法:执行批处理命令

clearBatch()方法:清除批处理命令(实际上是清除 List集合中的SQL语句,否则会造成内存溢出。)

 Demo样例:第一种方式:Statement.addBatch(sql)

  @Test
public void test1() throws SQLException{
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnection();
String sql1 = "insert into user(name,password,email,birthday) values('kkk','123','abc@sina.com','1978-08-08')";
String sql2 = "update user set password='123456' where id=3";
st = conn.createStatement();
st.addBatch(sql1); //把SQL语句加入到批命令中
st.addBatch(sql2); //把SQL语句加入到批命令中
st.executeBatch();
st.clearBatch();
} finally{
JdbcUtil.free(conn, st, rs);
}
}

6、采用Statement.addBatch(sql)方式实现批处理的优缺点

优点:可以向数据库发送多条不同的SQL语句。

缺点:SQL语句没有预编译。当向数据库发送多条语句相同,但仅参数不同的SQL语句时,需重复写上很多条SQL语句。

例如:

Insert into user(name,password) values(‘aa’,’111’);

Insert into user(name,password) values(‘bb’,’222’);

Insert into user(name,password) values(‘cc’,’333’);

Insert into user(name,password) values(‘dd’,’444’);

7、实现批处理的第二种方式:PreparedStatement.addBatch() 

 Demo样例:第二种 方式

  @Test
public void test2() throws SQLException{
conn = JdbcUtil.getConnection();
String sql = "insert into user(name,password,email,birthday) values(?,?,?,?)";
st = conn.prepareStatement(sql);
for(int i=;i<;i++){
st.setString(, "aaa" + i);
st.setString(, "" + i);
st.setString(, "aaa" + i + "@sina.com");
st.setDate(,new Date(, , ));
st.addBatch();
if(i%==){ //为防止(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());
}
}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(, "aaaaa");
st.registerOutParameter(, Types.VARCHAR);
st.execute();
System.out.println(st.getString());
}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 = /; // 人为制造异常,验证事务
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窗口

JDBC 学习笔记(二)—— 大数据+存储过程+批处理+事务管理的更多相关文章

  1. JDBC学习笔记二

    JDBC学习笔记二 4.execute()方法执行SQL语句 execute几乎可以执行任何SQL语句,当execute执行过SQL语句之后会返回一个布尔类型的值,代表是否返回了ResultSet对象 ...

  2. Java框架spring 学习笔记(十八):事务管理(xml配置文件管理)

    在Java框架spring 学习笔记(十八):事务操作中,有一个问题: package cn.service; import cn.dao.OrderDao; public class OrderSe ...

  3. 【学习笔记】大数据技术原理与应用(MOOC视频、厦门大学林子雨)

    1 大数据概述 大数据特性:4v volume velocity variety value 即大量化.快速化.多样化.价值密度低 数据量大:大数据摩尔定律 快速化:从数据的生成到消耗,时间窗口小,可 ...

  4. 学习笔记之大数据(Big Data)

    300 秒带你吃透大数据! https://mp.weixin.qq.com/s/VWaqRig6_JBNYC1NX7NQ-Q 手把手教你入门Hadoop(附代码&资源) https://mp ...

  5. Java框架spring 学习笔记(十九):事务管理(注解管理)

    注解管理的方式要比xml配置方式要简单很多 只需在配置文件中添加事务注解 <?xml version="1.0" encoding="UTF-8"?> ...

  6. Spring 4 官方文档学习(九)数据访问之事务管理

    说明:未整理版,未完待续,请绕行 本部分的重点是数据访问以及数据访问层与业务层之间的交互. 1.Spring框架的事务管理 介绍 http://docs.spring.io/spring/docs/c ...

  7. Spring学习笔记(五):JDBCTemplate+事务管理

    1 概述 Spring为开发者提供了JDBCTemplate,可以简化很多数据库操作相关的代码,本文主要介绍JDBCTemplate的使用以及事务管理功能. 2 JDBC Template 2.1 配 ...

  8. JDBC学习笔记(10)——调用函数&存储过程

    如何使用JDBC调用存储在数据库中的函数或存储过程: * 1.通过COnnection对象的prepareCall()方法创建一个CallableStatement *    对象的实例,在使用Con ...

  9. 【转】JDBC学习笔记(10)——调用函数&存储过程

    转自:http://www.cnblogs.com/ysw-go/ 如何使用JDBC调用存储在数据库中的函数或存储过程: * 1.通过COnnection对象的prepareCall()方法创建一个C ...

随机推荐

  1. 关于Css选择器优先级

    今天练习css的时候,重叠后的style发现不起作用,原来css选择器优先级大有文章. 声明: yi下内容选自 51cto.com  --加以自己的理解 以备日后参照使用,毕竟自己理解的才是自己的. ...

  2. CSS Sprite 图标

    HTML <body> <!-- ul.sprite>li*5>s.s-icon+a{CSS Sprite} --> <!-- 以上是Sublime Text ...

  3. [转载]JSON序列化与反序列化

    转载:http://www.cnblogs.com/ejiyuan/archive/2010/04/09/1708084.html 方法一:引入System.Web.Script.Serializat ...

  4. HDU4277 USACO ORZ(dfs+set)

    Problem Description Like everyone, cows enjoy variety. Their current fancy is new shapes for pasture ...

  5. cmd 命令收集

    window类   1.命令打开系统设置页面 1.control keymgr.dll 打开凭据管理器 2.gpedit.msc 打开管理面板 3.mspaint--------画图板  4.msts ...

  6. Photoshop CS6的安装

    Photoshop CS6的安装 文件的现在可以从百度软件中下载 安装方法见参考链接,window版的与之类似,亲测成功 参考链接 Mac版Adobe Photoshop CS6 破解过程 Adobe ...

  7. GridView自带分页 1总页数 首页 下一页 上一页 尾页 X 页 go 实现方法 .

    在前台GRIDVIEW中添加如下代码 <PagerTemplate> <table> <tr> <td style="text-align: rig ...

  8. 基于jquery的inputlimiter 实现字数限制功能

    html代码: <td>内容摘要:</td> <td> <textarea id="content_summary" rows=5 nam ...

  9. Typical sentences in SCI papers

       Beginning  1. In this paper, we focus on the need for   2. This paper proceeds as follow.   3. Th ...

  10. Windows Phone 8.1开发:如何让ListView下拉加载更多?

    Windows Phone 8.1开发中使用ListView作为数据呈现载体时,经常需要一个下拉(拇指向上滑动)加载更多的交互操作.如何完成这一操作呢?下面为您阐述. 思路是这样的: 1.在ListV ...