澳门金沙vip 12

澳门金沙vipJDBC 环境搭建与开发

默认情况下,MySQL的JDBC驱动会一下子把所有row都读取下来,这在一般情况下是最优
的,因为可以减少Client-Server的通信开销。但是这样也有一个问题,当数据库查询结
果很大时,特别当不能全部放进内存时,就会产生性能问题。 本来,JDBC
api里在Connection、Statement和ResultSet上都有设置fetchSize的方法,
但是MySQL的JDBC驱动都不支持,无论你怎么设fetchSize,ResultSet都会一次性从Serv
er读取数据。在MySQL的官方论坛上也有多个这样的问题,总结一下解决办法如下:

澳门金沙vip 1

澳门金沙vip 2JDBC
游标 与 fetch size

 

public void enableStreamingResults() throws SQLException {
    synchronized (checkClosed().getConnectionMutex()) {
        this.originalResultSetType = this.resultSetType;
        this.originalFetchSize = this.fetchSize;

对庞大的数据记录,为了避免内存溢出,我们可以分批来读取数据库中的数据记录,这每一批的数据即为一个
fetch size。支持这种数据读取方式的技术,即为 JDBC 游标。如下图示:

mysql fetch size相关问题

        setFetchSize(Integer.MIN_VALUE);
        setResultSetType(ResultSet.TYPE_FORWARD_ONLY);
    }
}
    
    在网上查了下这种Client Side Cursor的大概实现,
其实mysql本身并没有FetchSize方法,
它是通过使用CS阻塞方式的网络流控制实现服务端不会一下发送大量数据到客户端撑爆客户端内存,
我认为这种方式非常LOW! 是很明显的”补丁”策略;
这样就会造成一个必然的问题就是如果没有全部读取完ResultSet的结果再执行其他sql,
那么将会影响该连接的缓存,
所以这种方式要求要么读取完ResultSet中的全部数据要么需要自己调用ResultSet.close()方法,
也就是得用try {} finally{ rs.close();
}或者jdk7下的try-with-resources语法, 例如:
try (ResultSet rs = pstmt.executeQuery()) {
    Role role = new Role();
    int i = 0;
    while (rs.next()) {
        try {
            role.setRoleId(rs.getString(“roleId”));
            role.setState(rs.getInt(“state”));
            role.setMiscData(rs.getString(“miscData”));

一个 batch 就是一组需要同时发送到数据库的多条 SQL 语句。

 

  参考代码如下:

澳门金沙vip 3Connection

1).statement.setFetchSize(Integer.MIN_VALUE);

2. 使用Server Side Cursor
    MySQL JDBC Driver文档中有这样参数的说明:

16、关于中文乱码的问题

2.Statement一定是TYPE_FORWARD_ONLY的,并发级别是CONCUR_READ_ONLY(即创建Statem
ent的默认参数)

Since version: 5.0.0

遍历 ResultSet 对象,即可读取结果集中的每一行记录,相关方法如下表:

1.MySQL版本在5.0以上,MySQL的JDBC驱动更新到最新版本(至少5.0以上)

If connected to MySQL > 5.0.2, and setFetchSize() > 0 on a
statement, should that statement use cursor-based fetching to retrieve
rows?

情景描述:如果有大量数据记录需要插入到数据库,如果使用通常的一条一条的
SQL
插入语句去实现,这将导致插入速度非常慢。慢的原因是,每插入一条数据都需要向数据库发送一条
SQL 语句并执行一次 SQL
语句,如果插入成千上万条数据,那么就需要向数据库发送成千上万次
SQL,因此这样做的效率是很低的。

官方
的说法是不支持fetchSize不是MySQL的JDBC驱动的问题,而是MySQL本身就不支持。而商
用数据库Oracle或DB2都是支持fetchSize的,从这里也可以看出两者的考量不同。

  对DB来说Stream其实也就是我们说的游标(Cursor),
MySQL的Stream方式有2种, Client Side Cursor和Server Side Cursor.
JDBC默认的方式Client Side Cursor,
没有任何设置的默认情况下JDBC驱动会将select的全部结果都读取到Client
Side后再处理,
这样的话当select返回的结果集非常大时将会撑爆Client端的内存,
JDBC下就是普通的OOM; 当然用MyBatis之类的ORM也有同样的问题,
因为这些东西都是架构在JDBC之上的.

上述代码的核心做法时,使用循环把多条 SQL 语句打包至一个 batch
中去,然后把 batch 发送至数据库一起执行,执行完 batch
后记得清理掉它即可

2).((com.mysql.jdbc.Statement)stat).enableStreamingResults();
这样会一行一行地从Server读取数据,因此通信开销很大,但内存问题可以解决。

    在MyBatis中位置为:
<property name=”url”
value=”jdbc:mysql://localhost:3008/mybatislearn?autoReconnect=true&useCursorFetch=true”/>

澳门金沙vip 4
JDBC 设置字符编码

 

  这样就可以放心的在MyBatis下使用Client Side Cursor了.

不同的数据库,所支持的连接协议也是不相同的,数据库驱动程序所提供的 API
接口也不相同,这给“跨数据库迁移”带来了诸多不便。JDBC
存在的意义,就是统一了 API
接口,从而可以方便地对任意数据库进行访问。JDBC
帮助我们屏蔽了应用程序与数据库之间交互协议的实现细节,我们只需要使用
JDBC 提供的标准 API
,即可实现对任意数据库的访问,且无需关心底层的实现方式。

对 Java 程序来讲,JDBC 就是一个普通的 Java 类库,在应用程序中引用 JDBC
JAR包所提供的类和方法,通过操作
Java对象的方式就可以操作数据库中的数据。

对 数据库厂商来讲,JDBC
就是一套接口规范,每个厂商的数据库驱动都必须实现 JDBC
中所定义的数据库接口,如此才能在 Java程序中实现数据库连接与访问。

 

 

编译.java 代码,并放置至指定的 ./classes/cn/geekxia 文件目录:

3.以下两句语句选一即可: 

情景: 遍历并处理一个大表中的所有数据,
这个表中的数据可能会是千万条或者上亿条,
很多人可能会说用分页limit……但需求本身一次性遍历更加方便,
且Oracle/DB2都有方便的游标机制.

1、访问数据库最常见的几种方式(以 MySQL 为例)

 

  再次也发现MySQL相比其他大型RDBMS的弱点, 这种查询游标遍历本该是标配!
而MySQL用这么LOW的实现, 还需要用户掌握这么多黑魔法……F***

澳门金沙vip 5JDBC
的体系架构

fetch size相关问题
默认情况下,MySQL的JDBC驱动会一下子把所有row都读取下来,这在一般情况下是最优
的,因为可以减少Client-Server的通信…

useCursorFetch

12、JDBC 开发的五个基本步骤

“批处理”解决大量数据插入数据库的问题,示例代码如下:

    实测这种Server Side Cursor执行sql后要等很久才开始返回结果,
而Client Side Cursor几乎是瞬间就开始返回结果; 网上查询后的结果是Server
Side Cursor使用MySQL Server端的资源(内存/CPU……)处理Cursor,
这个可能是其原因, 但一旦开始返回结果目测两者差别不大.
    
    两者各有优缺点, 尤其是Client Side
Cursor必须自己记得ResultSet.close()否则整个连接将不再可用, 此为大坑,
尤其是有连接池的情况.

JDBC 的体系架构分为两层,分别是上层的 JDBC API 层,下层的 JDBC
驱动层。API 层
主要用于与数据库进行通信;驱动层主要用于与具体的数据库建立连接,这些驱动层
Driver 一般都是由数据库厂商提供的。

            selectHandler.action(role);
        } catch (Exception ex) {
            logger.error(“selectAllRoles error!”, ex);
        }
    }
}
    
    常用的ORM MyBatis下, 默认select的结果是一个List<XXXObject>,
这样问题就更明显了, 要将select全部结果放到一个集合中再处理,
那么结果集一大OOM是必然; 经过查询MyBatis资料发现有ResultHandler机制,
就是这样handler:
   
sqlSession.select(“chenlong.mybatislearn.db.mapper.RoleMapper.findAllRoles”,
handler);
    但是和JDBC方式一样,
MyBatis即便用了ResultHandler也是将所有结果都读到Client Side,
内存一样爆掉, 最后总算发现xml mapper里可以配置select的fetchSize,
按照前面JDBC方式将其配置为Integer.MIN_VALUE即-2147483648就正常了,
如下:
    <select id=”findAllRoles” fetchSize=”-2147483648″
resultType=”chenlong.mybatislearn.db.struct.Role”>
        SELECT * FROM role
    </select>
    但还有一个问题就是这种方式必须自己ResultSet.close(),
通过扒MyBatis代码发现它已经帮我们做了, 如下

澳门金沙vip 6ResultSet

 

澳门金沙vip 7三种访问数据库的方式

解决办法:
1. 使用Client Side Cursor
PreparedStatement/Statement的setFetchSize方法设置为Integer.MIN_VALUE或者使用方法Statement.enableStreamingResults(),
其实这个方法和设置Integer.MIN_VALUE一样, 源码如下:

澳门金沙vip 8DriverManger

Default: false

下载 JDK 并配置两个环境变量:JAVA_HOME = D:\Java\JDK (JDK
所在的安装目录 )PATH = D:\Java\JDK\bin (JRE 所在的 bin 目录)

什么是 JDBC 游标?什么是 fetch size ?

这种情景下,我们的解决方案是使用“批处理”插入数据,即向数据库一次同时发送多条
SQL 语句 ,同时插入多条数据。这样做,可以大大地减少向数据库发送 SQL
语句的次数,因此能够提升向数据库插入数据的效率。

3、JDBC 有哪些优势?

javac -d ./classes/cn/geekxia User.java

14、如何解决 JDBC 读取大数据量 字段的问题?

查询数据库 Servler 的字符编码列表:show variables like '%character%';

澳门金沙vip 9JDBC
开发的 5 个基本步骤

澳门金沙vip 10字符编码的优先级如果数据库
Server、数据库、表、字段同时设置了字符编码格式,则为字段设置的编码格式优先起作用,依次类推

本节完!!

示例代码如下:

部署描述符 web.xml ,代码如下:

澳门金沙vip 11ResultSet

澳门金沙vip 12如何开启
JDBC 游标?

<?xml version="1.0" encoding="UTF-8"?><web-app version="3.0"> <display-name>GeekXia</display-name> <servlet> <servlet-name>User</servlet-name> <servlet-class> cn.geekxia.User </servlet-class> </servlet> <servlet-mapping> <servlet-name>User</servlet-name> <url-pattern>/User</url-pattern> </servlet-mapping></web-app>

它是数据库唯一的通信标识符,通过它可以确定唯一的数据库实例。DB_URL
的组成部分如下图所示:

public final static String DB_URL = "jdbc:mysql://localhost/world?characterEncoding=uft8";

如何使用 JDBC 游标?使用 PreparedStatement 类: