#JDBC进阶 ##业务场景(一) 在企业级业务场景中,我们会经常遇到我们编写的SQL语句,查询条件过弱,区分性不强,SQL所查询的内容过多。一方面,我们可以通过修改过滤条件来减少搜索内容,在一些特定的业务需求,缺需要这些内容。
过滤条件比较弱,一次可能不去较多记录!
##业务场景(二) 数据统计需求,例如,在首页展示某些信息的排行榜。我们会扫描所有内容。
读取数据库表中所有的记录。
##海量数据读取 读取千万条记录,(读取内容过多)会导致Java内存异常, 异常产生的原因,Java程序是运行在Java虚拟机JVM当中,JVM是有内存大小限制的。当我们把数据库当中的内容一次性读入到内存中,如果我们读取的数据一次性超过了内存大小的限制,将会得到内存溢出的异常。
##游标 内存容量是有限的,我们每次只读取一部分数据进行处理,当处理完成后在读取下一部分的数据内容,这样我们就避免一次性载入太多的记录导致内存溢出。
游标是提供一种客户端读取部分服务器端结果集的机制。
我们把一个批次读取出来的记录称为Fetch Size
##使用游标 两个关键步骤 ###在DB_URL中增加支持游标的参数 MySQL中,我们在DB_URL之后设置userCursorFetch=true
开启游标功能。告诉JDBC开启游标
##PreparedStatement接口 PreparedStatement
接口,继承于Statement
接口。可以直接使用PreparedStatement
接口替换Statement
接口。PreparedStatement
接口相比Statement
接口,要求程序员在生成Statement的时候生成参数格式化的SQL语句。也就是说where的过滤条件都是通过?
的形式来表示的。后续,通过PreparedStatement
的setString或者setInt等方法设置值。然后进行执行。
PreparedStatement
接口,有setFetchSize
函数,可以设置客户端每次取回的数量。
##游标实例
String DB_URL_fetch = DB_URL + "?userCursorFetch=true";
System.out.println("[DB_URL_fetch]:" + DB_URL_fetch);
// connection = DriverManager.getConnection(DB_URL,USER,PASSWORD);
connection = DriverManager.getConnection(DB_URL_fetch,USER,PASSWORD);
打印输出
[DB_URL_fetch]:jdbc:mysql://192.168.1.200/test?userCursorFetch=true
###修改运行SQL部分
// statement = connection.createStatement();
preparedStatement = connection.prepareStatement("SELECT userName FROM user");
preparedStatement.setFetchSize(1);
resultSet = preparedStatement.executeQuery("SELECT userName FROM user");
#业务场景(三) 读取数据库当中,某字段可能会出现大字段内容,比如说文章、博客。在数据库当中某一列,保存了非常大的字段。存储文章、博客内容。有的系统会在数据库当中存储图片。尽管我们不推荐在数据库中存储图片资源,等大对象。
##大对象读取 某行的某列信息内容,太过巨大,读取有可能造成内存溢出问题。
#流方式 我们通过一种叫流的方式,解决列内容过多的问题。将大字段的内容,以二进制流的方式,按照区间进行划分,划分多个区间,每次读取一个区间,进行处理。当一个区间处理结束,在读取下一个区间内容。
##使用流方式 由原来resultSet
对象的getString()
等方式,使用getBinaryStream()
的方式读取列信息。之后操作inputStream
对象,进行数据内容读取。
#业务场景(四) - 批处理 数据录入的业务场景,我们需要把大量输入录入到系统当中。
如果我们使用for循环的方式执行SQL,我们会发现速度非常慢。主要我们执行update操作时,都需要发送SQL+执行SQL。
##批处理 更加高效的方式-批处理,一次发送多条SQL,插入多条数据内容。
##Statement的批处理
- Statement
preparedStatement继承于Statement,所以同样能使用以下方法。 -
addBatch()
把SQL打包成执行单元,把SQL加入到Batch中
-
executeBatch()
执行Batch中的多条SQL
-
clearBatch()
当执行结束后,清空Batch,为下次Batch做准备。
##批处理实例 ###for循环方式提交
for(String user : users) {
String sql = "INSERT INTO user (userName) VALUE (" + user + ")";
statement.executeUpdate(sql);
}
###批处理方式提交
Iterator iterator = users.iterator();
int batchsize = 10;
for(int index=0;index < users.size();index += batchsize)
{
for(int jndex=0;jndex < batchsize;jndex++)
{
if(iterator.hasNext())
{
String user = iterator.next().toString();
String sql = "INSERT INTO user (userName) VALUE (" + user + ")";
statement.addBatch(sql);
}
}
//execute batch
statement.executeBatch();
//clear for the next batch
statement.clearBatch();
}
###时间监控代码与循环一千次的执行时间差
Long begin = System.currentTimeMillis();
// Dosomething
Long end = System.currentTimeMillis();
System.out.println("[end - begin]:" + (end - begin));
执行后的时间差值
[end - begin]:5321
[end - begin]:3136
#业务场景(五) - 中文 如果碰到JDBC中的字符集和存储当中的字符集不统一,会获取乱码。 ##字符集设置 ###数据库字符编码 MySQL允许用户设置server级别的数据库编码,也可以设置数据库级的编码,允许设置表级别的编码
优先级 列 > 表 > 数据库 > 服务
##JDBC设置 知道如上编码,我们JDBC的编码需要设置成数据库内部编码一致,则我们就不会获取乱码结果。
在DB_URL后面添加编码参数,网易建议添加UTF8作为统一的编码格式。
#附录 ##FetchSize & preparedStatement的HelloJDBC
package com.hava.jdbc;
import java.sql.*;
/**
* Created by zhanpeng on 2016/9/21.
*/
public class HelloJDBC {
// static final String JDBC_DRIVER = "org.postgresql.Driver";
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
// static final String DB_URL = "jdbc:postgresql://192.168.1.200/test";
static final String DB_URL = "jdbc:mysql://192.168.1.200/test";
// static final String USER = "postgres";
static final String USER = "root";
// static final String PASSWORD = "";
static final String PASSWORD = "dVHJtG0T:pf*";
public static void helloworld() throws ClassNotFoundException {
Connection connection = null;
Statement statement = null;
//Fetch Statement
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
// 1. add Driver
Class.forName(JDBC_DRIVER);
// 2. create db connnection
try {
// add the fetch value for db_url
String DB_URL_fetch = DB_URL + "?userCursorFetch=true";
System.out.println("[DB_URL_fetch]:" + DB_URL_fetch);
// connection = DriverManager.getConnection(DB_URL,USER,PASSWORD);
connection = DriverManager.getConnection(DB_URL_fetch,USER,PASSWORD);
System.out.println("getConnection");
// 3.run SQL
// statement = connection.createStatement();
preparedStatement = connection.prepareStatement("SELECT userName FROM user");
preparedStatement.setFetchSize(1);
// resultSet = statement.executeQuery("SELECT \"userName\" FROM \"public\".\"user\"");
resultSet = preparedStatement.executeQuery("SELECT userName FROM user");
// 4.get userName
while(resultSet.next())
{
Integer index = resultSet.getRow();
String value = resultSet.getString("userName");
System.out.println("resultSet [row]:" + index + " [value]:" + value);
}
} catch (SQLException e) {
// Exception
e.printStackTrace();
} finally {
try {
// 5. close connection
if(connection != null)
connection.close();
if(statement != null)
statement.close();
if(resultSet != null)
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
##批处理
public static void batch(Set<String> users) throws ClassNotFoundException {
Connection connection = null;
Statement statement = null;
//Fetch Statement
// PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
// 1. add Driver
Class.forName(JDBC_DRIVER);
// 2. create db connnection
try {
// add the fetch value for db_url
// String DB_URL_fetch = DB_URL + "?userCursorFetch=true";
// System.out.println("[DB_URL_fetch]:" + DB_URL_fetch);
connection = DriverManager.getConnection(DB_URL,USER,PASSWORD);
// connection = DriverManager.getConnection(DB_URL_fetch,USER,PASSWORD);
System.out.println("getConnection");
// 3.run SQL
statement = connection.createStatement();
// preparedStatement = connection.prepareStatement("SELECT userName FROM user");
// preparedStatement.setFetchSize(1);
// resultSet = statement.executeQuery("SELECT \"userName\" FROM \"public\".\"user\"");
// resultSet = preparedStatement.executeQuery("SELECT userName FROM user");
//
// // 4.get userName
// while(resultSet.next())
// {
// Integer index = resultSet.getRow();
// String value = resultSet.getString("userName");
// System.out.println("resultSet [row]:" + index + " [value]:" + value);
// }
Long begin = System.currentTimeMillis();
for(String user : users) {
String sql = "INSERT INTO user (userName) VALUE (" + user + ")";
statement.executeUpdate(sql);
}
Long end = System.currentTimeMillis();
System.out.println("[end - begin]:" + (end - begin));
begin = System.currentTimeMillis();
Iterator iterator = users.iterator();
int batchsize = 10;
for(int index=0;index < users.size();index += batchsize)
{
for(int jndex=0;jndex < batchsize;jndex++)
{
if(iterator.hasNext())
{
String user = iterator.next().toString();
String sql = "INSERT INTO user (userName) VALUE (" + user + ")";
statement.addBatch(sql);
}
}
//execute batch
statement.executeBatch();
//clear for the next batch
statement.clearBatch();
}
end = System.currentTimeMillis();
System.out.println("[end - begin]:" + (end - begin));
//
} catch (SQLException e) {
// Exception
e.printStackTrace();
} finally {
try {
// 5. close connection
if(connection != null)
connection.close();
if(statement != null)
statement.close();
if(resultSet != null)
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}