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[]


BINARY、VARBINARY、LONGBINARY


java.lang.String


CHAR、VARCHAR、LONGVARCHAR


java.math.BigDecimal


NUMERIC、DECIMAL


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池的架构图:

时间: 2024-08-31 13:20:23

Mysql Java驱动代码阅读笔记及JDBC规范笔记的相关文章

jdbc的mysql安装问题-求教java连接MySQL的驱动包怎么安装不起来

问题描述 求教java连接MySQL的驱动包怎么安装不起来 官网下的是.mis的文件,点开进度条都读不完就跳了,安装界面都打不开,怎么破 解决方案 安装MySQL 的 Java驱动Mysql Java 驱动安装 解决方案二: 你去找mysql-connector的jar包,加入到你的项目的lib下 解决方案三: 你是安装mysql呢还是找 java链接mysql驱动啊 解决方案四: 你要把问题说清楚,安装环境说明白,贴个截图也行啊, 解决方案五: 驱动包,你是说jar包么,不是安装用的吧,下载j

MongoDB快速入门笔记(八)之MongoDB的java驱动操作代码讲解_mariadb

MongoDB的Java驱动是线程安全的,对于一般的应用,只要一个Mongo实例即可,Mongo有个内置的连接池(池大小默认为10个). 下面代码给大家介绍MongoDB的java驱动操作,具体代码如下所示: import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import org.bson.Document; import com.mongodb.MongoClient; impo

java连接MySQL数据库实现代码_java

本文实例为大家分享了java连接MySQL数据库代码,供大家参考,具体内容如下 //java连接MySQl数据库实例代码 package com.abc.dao; /*dkplus专业搜集和编写实用电脑软件教程, *搜集各种软件资源和计算机周边,独立制作视频和ppt和音频微信公众号, *点击进入 dkplus官方博客 (java网络编程http://dkplus.iteye.com), *微信搜索dkplus关注公众号可获取海量计算机周边资源.*/ import java.sql.Connect

Kryo简介及代码阅读笔记

更新:2012-08-01 版本 2.16长时间运行可能会导致OOM,版本2.18有bug,不能正确序列化map和collection. 真是悲剧,所用的每一个版本都有bug.不过从代码来看,作者有时的确比较随便..测试用例也少..(比起msgpack少多了) ======================================== Kryo官方网站:https://code.google.com/p/kryo/ 优点:     速度快!见https://github.com/eisha

jdbc连接mysql数据库驱动加载失败

问题描述 jdbc连接mysql数据库驱动加载失败 各位大神帮我看看这是啥意思,加载了驱动但还是报错啊 解决方案 右键build-path->add jars将lib下的驱动jar添加到类路径下,试试. 解决方案二: 已经解决了谢谢,好像是将jar包放在system jar里面 解决方案三: http://download.csdn.net/detail/be_proud/9301443 代码http://download.csdn.net/detail/be_proud/9301631 jar

java swing mysql 下面的代码无法连接到数据库,插入数据

问题描述 java swing mysql 下面的代码无法连接到数据库,插入数据 import java.awt.*; import javax.swing.*; import java.awt.event.*; import java.sql.*; class Test { /*private String driver="com.mysql.jdbc.Driver"; private String url="jdbc:mysql://localhost:3306/tech

用java编写代码,实现将多套试题,读入mysql数据库中

问题描述 用java编写代码,实现将多套试题,读入mysql数据库中 我想做一个自动生成试卷的系统,供教师使用,想把多套试卷录入数据库,用java编写代码,实现将多套试题,读入mysql数据库中 解决方案 自动生成试卷,需要做一个题库吧.通过题库的随机算法生成新的试卷 解决方案二: 是将一套样题的卷子拆解成若干道题目吗?首先要实现拆分啊,好像有点难...不如上网上直接找题目来的快 解决方案三: 是的,需要做题库,试题录入题库.试卷从中随机取出.. 解决方案四: 嗯,我已经将几套试题按题型录入数据

Java学习笔记(JDBC数据库编程常用接口)

Path配置 通常问题有两方面,一方面是路径不对,另一方面是路径前后标点符号不对(英文半角): CLASSPATH   .;%JAVA_HOME%\lib;                 (注意:前面点号,分号,后面分号) JAVA_HOME   C:\Program Files\Java\jdk1.6.0_17   (注意:前后均无符号) Path        %JAVA_HOME%\bin;              (注意:写在原来变量值的最前面  前无符号,后为分号) JDBC的全称

很全面的Mysql数据库、数据库表、数据基础操作笔记(含代码)_Mysql

 Mysql数据库.数据库表.数据基础操作笔记分享给大家,供大家参考,具体内容如下 一.数据库操作 1.创建数据库 Create database db name[数据库选项]; tip:语句要求使用语句结束符";"来结束服务. 标识符(数据库名)命名规则: 大小写取决于当前操作系统. 见名知意,推荐使用下划线法. 标识符的字符: 使用任意字符,数字,甚至中文.但是一些特殊的组合,例如纯数字组合,特殊符号等需使用标识符限定符来包裹. 限定符:反引号`` 中文可以:但要求客户端编码 在m