【持久层】java程序猿的JDBC知识点

41 篇文章 0 订阅
26 篇文章 0 订阅

声明:本文只是JDBC和数据库的一个知识路线图,只是给出了一个大概的学习结构,很多东西提到了但是没有细节,需要自行丰富。


一、基本操作

    JDBC的基本操作,总结起来就是“注册驱动、获取连接、获取执行对象并执行SQL、获取结果集并处理、关闭资源”,每一个步骤都有一些东西值得沉淀。

1. 注册驱动

    a. DriverManager.registerDriver(Driver driver)会导致驱动重复注册问题;

    b. Class.forName进行驱动注册:类加载知识、静态代码块知识,需要care和ClassLoader.load的区别;

    c. 注册驱动流程;

2. 获取连接

    a. CopyOnWriteArrayList知识(作为MVCC中的特例,只存在一个都版本的MVCC,两者在任何时刻都只存在一个加锁写);

    b. DriverManager.getConnection细节:遍历CopyOnWriteArrayList,然后从头到尾的尝试driver.connect方法,直到取到第一个不抛异常不为null的连接,返回并break循环;connect方法实际上是通过反射com.MySQL.jdbc.ConnectionImpl,在ConnectionImpl的构造函数中,会常见com.mysql.jdbc.MysqlIO实例,创建成功后会基于socket(jdbc:mysql://localhost:3306/database,主协议jdbc,子协议mysql)与服务器进行doHandshake握手,不抛异常则表示连接成功;否则失败,继续遍历CopyOnWriteArrayList直到结束;这里也是驱动重复注册浪费性能的原因,每一次都是找第一个可用的,但是实际上只有当获取连接对象的时候才会分配端口、网络等资源,因此这个性能浪费可控,并不是那么昂贵;

    c. JDBC核心对象:Connection的实现类是整个JDBC的核心对象,基于socket连接应用程序和数据库服务器;

    d. 获取数据库元信息(conn.getMetaData);

    e. 事务API,需要关注隔离级别(隔离级别和MVCC放在一起讨论,这两个技术的目的是为了什么,弄清楚这个问题,就知道为什么要放在一起讨论了)和持久性(例如mysql的undo、redo);

    f. readonly的含义;

3. 获取执行对象并执行SQL

3.1 ResultSet的设定

    a. Statement createStatement(int resultSetType, int resultSetConcurrency, nt resultSetHoldability) throws SQLException;

    b. PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException;

    c. CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException;

    其中:

    a. resultSetType:只读、滚动但是对数据变化不敏感、滚动但是对数据变化敏感,ResultSet.TYPE_FORWARD_ONLY、ResultSet.TYPE_SCROLL_INSENSITIVE、ResultSet.TYPE_SCROLL_SENSITIVE;

    b. resultSetConcurrency:结果集并发类型,只读和可通过ResultSet对象更新,ResultSet.CONCUR_READ_ONLY、ResultSet.CONCUR_UPDATABLE;

    c. resultSetHoldability:连接对象关闭之后结果集是否能继续使用,ResultSet.HOLD_CURSORS_OVER_COMMIT、ResultSet.CLOSE_CURSORS_AT_COMMIT;

3.2 插入自增主键

    a. int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException;

    b. PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException;

    其中,autoGeneratedKeys分成返回自增主键Statement.RETURN_GENERATED_KEYS和无自增主键Statement.NO_GENERATED_KEYS;

3.3 查询超时原理

    见下文。

3.4 大数据量防止OOM的配置;

    见下文。

3.5 PreparedStatement

    PreparedStatement正确使用才能提高效率,否则其效率是比Statement还要低,至于怎么用,还需要考虑防止SQL注入等安全性。

3.6 CallableStatement

    这个使用只需要关注数据库中procedure的参数input/output类型的对应即可。

4. 获取结果集并处理

    a. 根据设定的ResultSet类型进行查询处理;

    b. 根据设定的ResultSet的并发类型进行更新;

    c. 获取数据库表的元信息(getMetaData);

    d. fetch size问题,下文中大数据量查询防止OOM中介绍;

5. 关闭资源

    关闭资源没有太多需要注意的地方,只不过需要更好的容错,先判断null再关闭,至于资源的分配时机,则是在DriverManager.getConnection的时候分配。


二、高级操作

1. 批处理

    a. 批处理是容易导致死锁的,因为需要自己控制事务,否则mysql JDBC中的批处理和一个一个的提交操作没有区别,才考mysql最新版本的驱动源码,ConnectorJ,本文源码摘自最新版本5.1.38;

    b. 再次重申一遍,批处理需要自己控制事务,否则和单独提交没有区别,当然有优化,在连接的时候url添加服务器端参数rewriteBatchedStatements=true,这样就会把批处理的SQL一次性发送到服务端,而不是一次一次的发送;

    c. api:addBatch、clearBatch、executeBatch;

    c. mysql批处理原理及源码参考:源码截图如下,实际上批处理就是在addBatch的时候把sql放入一个ArrayList,然后在executeBatch调用的时候for循环出来一个一个执行,最后清空这个ArrayList,因此要想使用真正的批处理,则JDBC获取连接的时候就需要和服务器进行沟通,因此需要在URL中添加服务器端参数rewriteBatchedStatements=true。

    源码参见com.mysql.jdbc.StatementImpl.executeBatchInternal(),关键部分截图如下所示:


2. Blob/Clob

    至于Blob和Clob,就是字节流和字符流的操作,重点在数据库要不要给全文索引这些东西;

3. 连接池

    至于连接池,常用的DBCP、C3P0、JNDI、HikariCP等等,其使用的关键和设计的关键:

    a. 基本配置,譬如什么空闲连接数、总连接数啊等等;

    b. 事务配置,隔离级别、自动提交等;

    c. 健康性检查,取时测试、还回时测试、mysql8小时问题等等;


三、关键知识点

1. 连接和事务管理

    做持久层的东西,必然要关注事务,否则数据一致性无法保证。至于JDBC的事务,需要从几个方向来了解:

    a. 数据库锁基本操作和原理;

    b. 连接管理的应用设计:基于方法栈、基于线程局部变量、基于同步加锁,要提高并发,采用MVCC、copy on write技术等等;

    在事务的ACID中(当然,spring基于线程局部变量的传播特性在这里就不讨论了,Spring这部分的源码并不晦涩,多看几次就懂了),AID的提出都是为了保证C(数据一致性),至于A原子性通过加锁来保证,需要跟深入的讨论的是则是I和D,隔离性和持久性。

    隔离性描述的是并发事务中数据一致性问题,不同的隔离级别能看到其他事务中数据的中间状态的程度不一样,当然隔离级别是基于数据库锁的,因此对读写并发性会照成很大的影响,因此引入了MVCC;不同数据库对MVCC的支持不一样,从mysql的角度来讨论,只有RC和RR才支持MVCC,而从RC的角度看MVCC和RR的角度来实现MVCC是不一样的,需要分开来讨论,否则不可能掌握隔离级别和MVCC。

    至于持久性,既要追求性能又要保证数据安全,因此mysql插入缓存和redo\undo log配套出现,通过内存缓冲和磁盘日志的顺序写异步了磁盘的随机写,从而提高性能又保证数据持久安全。

2. 查询超时原理

    之前简写了一篇博客,基于connectorJ 5.1.37,和最新版的jar包并无变化。

    mysql查询超时原理:点击打开链接

3. 大数据量查询时防止OOM

3.1. 服务器端fetch_size(不建议)

    这种方式会在服务器生成临时表,因此性能上表现并不好。

3.2. 客户端stream

    参见实现源码截图。


    参见博客:点击打开链接

4. ReadOnly原理

    a. 参见mysql开发手册:点击打开链接

    b. 参见源码实现:


5. 批操作原理

    见上文。


附注:

    本文是自己总结的持久层知识,当然不包括一些持久层框架,譬如DButils、Spring JDBC和Spring Transaction、mybatis、hibernate,但是这些持久层框架的实现,必然离不开这些原理。

    没有提及的内容,后续工作用发现将会持续补充,谢谢!

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值