`
duoerbasilu
  • 浏览: 1483276 次
文章分类
社区版块
存档分类
最新评论

Mysql Java驱动代码阅读笔记及JDBC规范笔记

 
阅读更多

一前言:

以前刚开始用java连接mysql时,都是连猜带蒙的。比如:

一个Statement,Connection何时关闭?

Connection能不能先于Statement关闭?

ResultSet是怎样存放数据的?怎样才能高效操纵ResultSet?

PrepareStatement到底是怎样回事?

连接池是怎样工作的?

二、从JDBC driver代码分析:

在性能要求高的地方,应当使用ResultSet.get**(int)系列函数

如ResultSet.getBytes(String columnName),

则会先会调用findColumn(columnName)去查找到columnName对应的index是什么,而在findColumn(columnName)函数中,

会检查索引有没有构建好了,如果还没有则要构建columnName对应的索引。

所以,如果对性能要求,则应该使用ResultSet.getBytes(int column)函数。


PreparedStatement的缓存及重用

对于PreparedStatement,会有一个LRUCache来存放,会先到里面去取,拿不到再创建一个新的。

这个LRUCache的默认大小是25(太小了吧。。)。对于sql长度,如果大于256的,貌似则不缓存这个PreparedStatement。

LRUCache很简单,代码:

    public class LRUCache extends LinkedHashMap {
    protected int maxElements;
    public LRUCache(int maxSize) {
        super(maxSize);
        this.maxElements = maxSize;
    }
    protected boolean removeEldestEntry(Entry eldest) {
        return (size() > this.maxElements);
    }
}

LinkedHashMap在每次put和putAll后,会调用removeEldestEntry来判断是否要移除最老的Entry。

LinkedHashMap的实现也比较简单,里面用双向链表把所有的Entry串在一起,当调用get时,把get所在的key移到链表的最前面。

PreparedStatement是如何实现重用的:

    pStmt = (com.mysql.jdbc.ServerPreparedStatement)this.serverSideStatementCache.remove(sql);
                                                                                                                                                                                                                                                                                                                                                                                                                                                     
if (pStmt != null) {
    ((com.mysql.jdbc.ServerPreparedStatement)pStmt).setClosed(false);
    pStmt.clearParameters();
}

可见只是设置未关闭,再清除Parameters。所以从代码上来说,我们得到一个PreparedStatement在使用后,可以调用clearParameters,再接着使用。但是最好不要这样做。

如果是想要执行多次,可以用addBatch和executeBatch函数来多次执行。


关于CallableStatement和ServerPreparedStatement

CallableStatement,ServerPreparedStatement继承自PreparedStatement,实际上prepareStatement(String sql)函数返回的就是ServerPreparedStatement,LRUCache中放的也是。

CallableStatement也有一个LRUcache。

实际上当PreparedStatement调用close时,并没有真正释放掉资源,


Statement、Connection、ResultSet何时close

查看Statement的close函数代码,可以发现当close时,这个Statement中所有的ResultSet也会被close。

查看Connection的close函数,当close时,这个Connection的所有Statement都会被close。

但是据JDBC4的规范,有可能当Statement关闭时,ResultSet中的资源未被完全释放,当GC再次运行时才会回收。

所以最好就是顺序关闭ResultSet,Statement,Connection。


异常处理

SQLException是可以迭代的,应该用以下的代码来处理所有的异常:

    catch(SQLException ex) {
   while(ex != null) {
      System.out.println("SQLState:" + ex.getSQLState());
      System.out.println("Error Code:" + ex.getErrorCode());
      System.out.println("Message:" + ex.getMessage());
      Throwable t = ex.getCause();
      while(t != null) {
         System.out.println("Cause:" + t);
         t = t.getCause();
      }
      ex = ex.getNextException();
   }
}
//或者
catch(SQLException ex) {
   for(Throwable e : ex ) {
      System.out.println("Error encountered: " + e);
   }
}


在代码Connection类的很多地方,比如void closeAllOpenStatements()函数,可以看到这样的代码:

    for (int i = 0; i < numStmts; i++) {
                Statement stmt = (Statement) currentlyOpenStatements.get(i);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
                try {
                    stmt.realClose(false, true);
                } catch (SQLException sqlEx) {
                    postponedException = sqlEx; // throw it later, cleanup all
                    // statements first
                }
            }

感觉这个是有问题的,因为把一些异常信息给丢掉了,实际上是可以迭代的,应该调用setNextException函数把异常都加到一起。


统计数量count:

    stmt.execute(sql);
ResultSet resultSet = stmt.getResultSet();
resultSet.next();
int count = resultSet.getInt("count(*)");//or  getInt(1);


代码中其它一些有意思的地方:

在代码中有大量的StringBuffer,而没有用StringBuilder,可能是要兼容JDK5的原因

配置都用一个ConnectionProperty类来表示,从这里派生出子类,IntegerConnectionProperty,BooleanConnectionProperty什么的。

每一个配置有都默认值,描述信息,版本等。貌似大部分在ConnectionProperties类中实现。

发现了代码中的一个bug,没有找到找交的地方,不是很重要就算了:)

com.mysql.jdbc.ConnectionProperties类initializeFrom(String extractedValue)函数中:

    if (extractedValue.endsWith("k")
        || extractedValue.endsWith("K")
        || extractedValue.endsWith("kb")
        || extractedValue.endsWith("Kb")
        || extractedValue.endsWith("kB")) {
    multiplier = 1024;
    int indexOfK = StringUtils.indexOfIgnoreCase(
            extractedValue, "k");
    extractedValue = extractedValue.substring(0, indexOfK);
} else if (extractedValue.endsWith("m")
        || extractedValue.endsWith("M")
        || extractedValue.endsWith("G")  //这行明显是多出来的
        || extractedValue.endsWith("mb")
        || extractedValue.endsWith("Mb")
        || extractedValue.endsWith("mB")) {
    multiplier = 1024 * 1024;


三、从JDBC规范来看:

类型对照表:

Java类型

SQL类型

boolean

BIT

byte

TINYINT

short

SMALLINT

int

INTEGER

long

BIGINT

float

FLOAT

double

DOUBLE

byte[]

BINARYVARBINARYLONGBINARY

java.lang.String

CHARVARCHARLONGVARCHAR

java.math.BigDecimal

NUMERICDECIMAL

java.sql.Date

DATE

java.sql.Time

TIME

java.sql.Timestamp

TIMESTAMP

注意:

在JDBC中要表示日期,是使用java.sql.Date,其日期格式是"年、月、日";

要表示时间的话则是使用java.sql.Time,其时间格式为"时、分、秒";

如果要表示"时、分、秒、微秒"的格式,则是使用java.sql.Timestamp


连接池可能会自动关闭之前的connection!

要注意使用连接池时,据JDBC规范:

A single physical PooledConnection object may generate many logical

Connection objects during its lifetime. For a given PooledConnection object,

only the most recently produced logical Connection object will be valid. Any

previously existing Connection object is automatically closed when the associated

PooledConnection.getConnection method is called. Listeners (connection

pool managers) are not notified in this case.

This gives the application server a way to take a connection away from a client.

This is an unlikely scenario but may be useful if the application server is trying

to force an orderly shutdown.

所以之前得到的Connection有可能会失效!!

但是实际上我估计没人会按这个方案来实现,因为太不友好,怎么能别人用着你就把它悄悄地关闭掉了。

测试了proxool,当设置最大Connection数为1时,在获取第二个Connection时,会抛出个异常。


物理连接和逻辑连接

连接池中分为物理连接和逻辑连接,对应PooledConnection类和Connection类。

PooledConnection不对用户暴露,当PooledConnection调用close时才关闭物理连接。

Connection调用close时,并不真正关闭物理连接,只是把它放入池中。


Statement

Statement也分两种logical statement和physical statement。

可以实现PreparedStatement池,当Connection调用close时,并不把PreparedStatement关闭,有可能是放入到池中。

A pool of statements is associated with a PooledConnection object.

所以说一个物理连接都有一个Statement池。至于池的大小,可以通过ConnectionPoolDataSource的Properties来设置。


ConnectionPoolDataSource的属性

ConnectionPoolDataSource 有以下的标准属性:

maxStatements,initialPoolSize,minPoolSize,maxPoolSize,maxIdleTime,propertyCycle。



JDBC连接池的架构图和PreparedStatement池的架构图:








分享到:
评论

相关推荐

    JDBC连接MySQL数据库8.0.26的驱动包

    就是java连接MySQL需要的那个jar包,5积分意思一下。里面有一个笔记(笔记版权归原作者所有仅用来学习,侵删)不想花积分可以去官网下:https://dev.mysql.com/downloads/file/?id=506032

    jdbc 之 mysql 笔记

    此资源是我从网上下载的视频附赠的,特分享出来,与广大学习者共睹

    狂神说java笔记 MySQL和JDBC.zip

    狂神说java笔记 MySQL和JDBC

    jdbc笔记(自写)

    JDBC的目标是使Java程序员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统, 这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。 ​ 总结: JDBC本身是java连接数据库...

    JDBC学习笔记(笔记+包含详细注释的代码)

    JDBC学习笔记(笔记+包含详细注释的代码)

    韩顺平j2ee-JDBC与MySQL学习笔记

    韩顺平j2ee-JDBC与MySQL学习笔记

    MySQL专栏中JDBC编程那一节的笔记代码

    MySQL专栏中JDBC编程那一节的笔记代码。

    JDBC课堂笔记.md

    mysql的jar包及学习方法, 1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar 1.复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下 2.右键--&gt;Add As Library 2. 注册驱动 3. 获取数据库连接...

    配套学习资料:Java开发 - 尚硅谷JDBC学习笔记

    学习笔记请看我写的文章: Java开发 - 尚硅谷JDBC学习笔记 https://blog.csdn.net/qq_63317769/article/details/136693437

    达内jdbc学习笔记

    达内jdbc学习笔记,内有三大数据(oracle,MySql,SQLServer)的不同连接方式

    【狂神】JAVA学习全套笔记(完整版)

    2、MySQL + JDBC 3、JAVA WEB 4、Mybatis 5、SSM 6、大前端 7、Vue 8、SpringBoot 9、Spring Security 10、Shiro 11、Spring Cloud 12、JVM 13、JUC 14、Git 15、Linux 16、Redis 17、ElasticSearch 18、Docker

    MySQL JDBC部分 mysql8.0 笔记(详细)

    Mysql中JDBC部分,内容详细,知识点清晰便于理解,值得上手

    MySQL数据库文件jdbc.sql

    MySQL数据库文件jdbc.sql:用于MySQL学习笔记(二)的测试数据库,下载后用导入MySQL即可使用,学习时配套使用。

    JDBC笔记.docx

    所属包:java.sql Javax.sql 1. 创建数据库连接 a) Dirver(驱动) 数据库连接接口,该接口由数据库驱动来实现。 //jdbc协议名:(子协议)数据库协议名://服务器IP地址:端口号/数据库名 private String url = ...

    Java JDK 7学习笔记(国内第一本Java 7,前期版本累计销量5万册)

     《java jdk 7学习笔记》是作者多年来教学实践经验的总结,汇集了教学过程中学生在学习java时遇到的概念、操作、应用或认证考试等问题及解决方案。  《java jdk 7学习笔记》针对java se 7新功能全面改版,无论是...

    JDBC学习笔记2017.11.29写

    这是我2017.11.29写的JDBC学习笔记里面详细介绍JDBC的连接到Mysql的代码以及截图步骤,里面覆盖了封装的代码,包括增删查改等基本数据操作,jdbc驱动的下载连接

    狂神笔记,b站狂神说课程笔记大全(最新)

    15、MySQL和JDBC 16、前端:Html5 17、前端:CSS3 18、前端:JavaScript、jQuery 19、JavaWeb 20、MyBatis 21、Spring 22、SpringMVC 23、大前端串讲 24、Vue精讲 25、SpringBoot入门及原理 26、SpringBoot操作...

    Java 从入门到精通全程笔记(JavaSE+JavaEE :史上最全笔记)

    JavaSE+JavaEE企业级开发 全程笔记免费下载,内容详尽,doc文档,共173页,六号字体,...*、数据库(Mysql Oracle jdbc Hibernate hql/sql), *、JSP, *、Ajax *、Struts, *、Spring, *、Ejb, *、Unix *、java和模式

    jdbc笔记整理 sql

    将数据库的 JDBC 驱动加载到 classpath 中,在基于 JAVAEE 的 WEB 应用库产品的 JDBC 驱动复制到 WEB-INF/lib 下 . 加载 JDBC 驱动,并将其注册到 DriverManager 中,下面是一些主流数 //Oracle8/8i/9iO 数据库 ...

    java基础笔记

    JAVA 基础讲解包括面向对象,javaSE,mysql,jdbc,web,笔记,练习等

Global site tag (gtag.js) - Google Analytics