讲真,你真的懂JDBC吗?

本文详细介绍了DBCP数据库连接池的使用方法、配置参数及其在Java应用中的重要性。同时,文章还探讨了JNDI、JDBC等相关概念,解析了DBCP的核心类和时序图,以及超时机制的工作原理。

讲真,你真的懂JDBC吗?

2017-10-19 07:12 王新栋  0   0  阅读 4


作者介绍

王新栋从事京麦平台的架构设计与开发工作。熟悉各种开源软件架构,在web开发,架构优化上有较丰富的实战经历。有多年在NIO领域的设计、开发经验,对HTTP、TCP长连接技术有深入研究与领悟,目前主要致力于移动与PC平台网关技术的优化与实现。


数据库连接池在J2EE领域是一个不可缺失的组件,尽管Druid越来越流行,但是DBCP作为一个老牌的数据库连接池,一直在企业系统中默默奉献着自己。比如大部分系统还都是使用DBCP,那么我们还是很有必要对DBCP有一个熟悉的认识。


鉴于这样的需求,我根据相关资料,并结合自己的认知重新做了一个梳理,一来自己可以在以后的工作中回过头来温习,二来也希望能够帮助其它同学对DBCP以及涉及到的相关概念和知识(像超时机制、连接池原理等)有一个了解。众所周知,操作一个数据库的流程为:创建数据源,获得链接,构造statement,执行请求,下面我们逐步梳理总结。


一、相关概念复习 


在谈DBCP之前我们先来复习一下几个相关的概念:


  1   JNDI 


Java Naming and Directory Interface (JNDI)JNDI API被用于执行名字和目录服务。它提供了一致的模型来存取和操作企业级的资源。JNDI的api位于javax.naming包中,它的作用是可以把对象放到一个容器中(JNDI容器),Java对象在这个容器中都有一个名称,程序则可以通过这个名称来获取对象,例如下面这样:



  2   JDBC 



Java数据库连接(Java Database Connectivity,简称JDBC),是Java语言中用来规范客户端应用程序(比如Web应用程序等如何访问关系型数据库的应用程序接口),提供了诸如查询和更新数据库中数据的方法。


JDBC驱动程序一共有四种类型:

  • 类型1-JDBC-ODBC桥

  • 类型2-本地API驱动

  • 类型3-网络协议驱动

  • 类型4-本地协议驱动


我们常用的是类型4-本地协议驱动,这种类型的驱动使用socket链接,直接在客户端和数据库之间进行通信。


优点是1-访问速度快 2-最直接,最纯粹的JAVA实现。
缺点是1-需要每个数据库厂商提供自己的JDBC驱动。2-需要针对不同的数据库使用不同的驱动程序。

JDBC的API在jdk的java.sql包中,扩展的内容在javax.sql包中。主要包括(斜体代表接口,需驱动程序提供者来具体实现):


  • DriverManager:负责加载各种不同驱动程序(Driver),并根据不同的请求,向调用者返回相应的数据库连接(Connection)。

  • Driver:驱动程序,会将自身加载到DriverManager中去,并处理相应的请求并返回相应的数据库连接(Connection)。

  • Connection:数据库连接,负责进行与数据库间的通讯,SQL执行以及事务处理都是在某个特定Connection环境中进行的。可以产生用以执行SQL的Statement。

  • Statement:用以执行SQL查询和更新(针对静态SQL语句和单次执行)。

  • PreparedStatement:用以执行包含动态参数的SQL查询和更新(在服务器端编译,允许重复执行以提高效率)。

  • CallableStatement:用以调用数据库中的存储过程。

  • SQLException:代表在数据库连接的创建和关闭和SQL语句的执行过程中发生了例外情况(即错误)。


  3   DBCP数据库连接池和JDBC之间的关系 


数据库连接池负责创建(通过JDBC API)、管理、销毁数据库的连接。应用程序可以从数据库连接池中重复使用一个现有的连接,而不是重新创建一个。连接池,common-pool中的GenericObjectPool它负责缓存和管理连接;连接,这是是指PoolableConnection;连接池和连接一对多的关系。池化技术是通过commons-pool来实现的,每个连接是一个对象,换言之,是对象池的使用与管理。DBCP连接池是基于commons-pool这种对象池来实现的。


二、commons-pool的理解 


Apache commons-pool是一种对象池技术,我们使用的很多涉及池的场景一般都是基于该组件来实现的,DBCP数据库连接池也是基于commons-pool来实现的,因此我们先来了解下这种对象池技术。下图是对象池的对象生命周期流程图。



三、DBCP核心类图及序列图 


 BasicDataSource.java



 ConnectionFactory.java



 PoolingDataSource.java



 PoolingConnection.java



 Delegating.java



 AbandonedObjectPool.java



 创建数据源createDataSource



 创建连接getConnection



 创建statement prepareStatement




四、DBCP配置及使用 


DBCP是Apache下的一个开源数据库连接池,我们在使用时需要两个JAR文件,分别是commons-dbcp.jar(连接池的实现)和commons-pool.jar(连接池实现的依赖库),不过我们在使用的时候只需要引入下面mvaen坐标,commons-pool是在commons-dbcp里面隐含引用了。


注意一点,就是从1.x升级到2.x的时候,由于DBCP的包路径已经变了,需要升级者修改局部代码。


<dependency>

    <groupId>commons-dbcp</groupId>

    <artifactId>commons-dbcp</artifactId>

    <version>2.2</version>

</dependency>


DBCP可以在应用程序中独立的使用,也可以与web应用服务器整合使用。


  1   Tomcat中使用 


比如Tomcat的连接池就是采用该连接池来实现的。如下:



  2   应用程序中独立使用 



更多配置请参照

https://commons.apache.org/proper/commons-dbcp/configuration.html


五、连接超时机制 


对网络资源访问时,超时设置是必须的。没有超时的保护,一旦依赖资源发生故障或者网络故障,就会引起线程堆积,甚至发生雪崩。


  1   超时层级 



从这张图中我们也能够看出DBCP并不参与数据库超时的处理,它只负责管理连接。根据上图我们可以看到超时有一个依赖层级,上层超时依赖下层超时。依次为:事务超时->Statement超时->JDBC Driver socket超时->操作系统超时。


  事务超时


事务是应用层级的概念,我们知道事务是有一组SQL执行单元组成的。那么事务超时的时间阈值,实际是Statement超时N个需要执行的Statement数量。比如一个事务里面有3条Statement,每条Statement的执行时间是50MS,其它业务上的逻辑执行时间+框架执行时间未100ms,那么最终事务的超时时间为:350+100=250ms。


  statement超时


用来限制SQL语句的执行时间,通过setQueryTimeout(int timeout)来设置,不过现在大都是ibatis了,可以通过 SqlMapConfig.xml 中的 setting 属性defaultStatementTimeout 来设置全局的 statement 超时缺省值<settings defaultStatementTimeout="15"/>,还可以在每个sql.xml文件中,根据业务实际需要来设置<select timeout="10"/> <insert timeout="10"/> <update timeout="10"/>,这样就会覆盖掉全局的值,而采用具体的阈值。


  Socket超时


这是底层的一种超时,因为我们使用的JDBC驱动类型是TYPE4,它是基于socket来通信的。MySQL的JDBC驱动中的connectTimeout 和 socketTimeout 的默认值是 0 ,这意味着不会发生超时。所以我们必须在DBCP配置中设置这两个值。

<property name="connectionProperties" value="connectTimeout=2000;socketTimeout=15000"/>


connectTimeout为建立连接的超时时间,socketTimeout为JDBC客户端和数据库服务器之间数据交互的时间。注意这里配置的socketTimeout的值必须要大于Statement的超时时间值。否则Statement超时就没有意义,也不能生效。


  操作系统的socket超时


linux操作系统也会设置socket超时,比如我们这边的服务器一般配置的是20分钟,因为公司的Linux服务器的KeepAlive检查周期为20分钟。这样即使上面的socketTimeout值为0永不超时,也还是要收到Linux服务器的超时限制,也就是由于网络原因引起的数据库网络连接问题也不会超过20分钟。


  2   MySQL处理超时的机制及原理 



上图是一MySQL在执行一个命令的过程中的步骤以及发生超时现象后的处理机制:


  1. 通过Connection的createStatement()方法去创建一个Statement,以便后续进行读写操作

  2. 执行第1步创建的Statement的executeQuery()方法

  3. 将查询请求命令发送到MySQL数据库服务器

  4. 创建一个超时线程(从5.1版本以后,在创建每个连接的时候,会随之创建一个处理超时的线程timeout-execution)

  5. 把当前的statement对象注册到超时线程timeout-execution中

  6. 发生了超时(阈值是你在Statement执行前候配置的setQueryTimeout(int timeout),如果是mybatis则是在配置文件里面配置的值defaultStatementTimeout="15",单位s)

  7. 超时线程会重新创建一个新的Connection,这个Connection的属性配置都跟先前的一样

  8. 用新创建的这个Connection去发送取消查询请求


六、各种数据库连接池性能对比 


测试环境,OS: OS X 10.8.2 CPU:intel i7 2GHz 4 core JVM:java version "1.7.0_05"


测试执行申请归还连接1,000,000(一百万)次总耗时性能对比,Java7 的基准测试结果如下:


Druid是性能最好的数据库连接池,DBCP性能属于中上,我们在以后的业务场景中可以根据实际需要做选择。这个是Druid的官方测试,不过可以利用测试代码自己进行验证一遍,测试代码如下:


https://github.com/alibaba/druid/blob/master/src/test/java/com/alibaba/druid/benckmark/pool/Case1.java


七、总结 


本文梳理了数据库连接相关的概念,JNDI、JDBC、连接池,DBCP的核心类和时序图,另外还梳理了commons的池化组件,最后描述了JDBC连接过程中的超时机制,包括超时依赖和超时的机制原理,超时的知识点对与我们非常重要,超时是一种防护措施,当依赖DB性能变慢或者网络故障的情况下,可以快速失败,以便保护应用程序不会导致线程堆积而发生雪崩这类致命事故。


DBCP经历了各种互联网应用的验证,稳定可靠的性能会一直服务于我们的系统中。当今比较流行的Spring Boot内部集成的Tomcat应用依然在使用DBCP连接池。


参考资料

https://www.cubrid.org/blog/understanding-jdbc-internals-and-timeout-configuration

https://zh.wikipedia.org/wiki/Java%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5

http://shift-alt-ctrl.iteye.com/blog/1917782

https://commons.apache.org/proper/commons-pool/index.html

https://commons.apache.org/proper/commons-dbcp/index.html


-END-

2017 Gdevops广州站高调启动!

数据库工具发布、大数据与数据库课程

大咖专题、深度交流、原创颁奖等大招

11月24日,邀你共同见证~

<think> 首先,用户的问题是关于使用Java和Oracle实现图书管理系统的详细步骤,适合新手小白理解。我需要参考系统提供的引用信息。 引用内容: - 引用[1]:讨论了Oracle JDK和OpenJDK的区别,以及Java MVC架构模式。它提到Oracle JDK更适合企业级应用,而OpenJDK更适合开源项目和个人开发者。 - 引用[2]:描述了一个档案管理系统的项目,强调了信息量大、安全性高、操作便捷的特点,以及它对学院管理的规范化、科学化的意义。 用户的问题是基于这些引用的,但引用的内容似乎不太直接相关。引用[1]提到了Oracle JDK和Java MVC,这与Java和Oracle的实现相关;引用[2]是关于档案管理系统的,类似于图书管理系统。所以我可以从中提取有用的部分。 用户正的需求是:获取使用Java和Oracle实现图书管理系统的详细步骤,适合新手小白理解。 我的回答需要: - 使用中文。 - 结构清晰,帮助用户逐步解决问题。 - 尽量保证回答实可靠。 - 按照系统级指令:行内数学表达式用$...$,独立公式用$$...$$单独成段,LaTeX语法正确。 - 在回答中引用的段落末尾自然地添加引用标识,例如[^1]。 - 生成相关问题。 系统级指令还指定了示例格式,包括生成相关问题部分。 组织回答结构: 1. **引言**:简要介绍图书管理系统,并解释为什么使用Java和Oracle。 2. **详细步骤**:分步骤描述实现过程,从环境设置到部署。 - 确保步骤简单易,适合新手。 - 包括设置开发环境、设计数据库、编写代码等。 - 参考引用:引用[1]提到Oracle JDK和MVC模式,我可以建议使用这些。 3. **代码示例**:提供简单的Java代码片段,使用$...$或$$...$$格式如果需要数学表达式,但图书管理系统可能不需要复杂的数学,所以可能少用。 4. **结束部分**:总结并生成相关问题。 5. **添加引用标识**:在引用段落末尾添加[^1]或[^2]。既然引用是提供的,我需要在回答中自然地引用它们。例如,当提到数据库设计时,可以引用[^2]关于信息管理和安全性的部分;当提到Java实现时,引用[^1]。 详细步骤: - 步骤1:环境准备 - 安装Java JDK:建议Oracle JDK,因为它适合企业应用[^1]。 - 安装Oracle Database:设置数据库。 - 步骤2:数据库设计 - 设计图书表:例如,books表,包含id, title, author等字段。 - 参考[^2]关于信息量大、安全性高的要求。 - 步骤3:Java项目设置 - 使用MVC架构:如引用[1]中提到的。 - 创建项目结构:model, view, controller。 - 步骤4:实现功能 - 连接数据库:使用JDBC。 - 实现CRUD操作:创建、读取、更新、删除图书。 - 步骤5:用户界面 - 简单的命令行或Swing GUI。 - 步骤6:测试和部署 确保步骤适合新手:使用简单语言,避免高级概念。 生成相关问题:在回答末尾,添加§§相关问题§§部分,列出3-4个相关问题。 添加引用: - 当讨论Oracle JDK的好处时:[^1] - 当讨论系统安全性或管理时:[^2] 完整回答结构: - 开头:解释主题。 - 详细步骤(编号或分点)。 - 代码示例(如果需要)。 - 结尾:总结。 - 相关问题。 现在,草拟回答。 </think> 以下是使用Java和Oracle实现图书管理系统的详细步骤,针对新手小白设计。我将以清晰、逐步的方式解释整个过程,确保易于理解和操作。实现基于Java(推荐使用**Oracle JDK**,因为它提供更好的企业级支持和稳定性,适合管理系统的开发[^1])和Oracle数据库(强调信息量大、安全性高和操作便捷的特点,类似引用[2]中的档案管理系统)。系统采用MVC(Model-View-Controller)架构模式,使代码结构模块化、易于维护。 整个过程分为6个主要步骤,从环境设置到部署测试。我会提供关键代码示例(使用简单Java和JDBC),并避免高级概念,适合新手入门。 ### 步骤1: 环境准备(约10-15分钟) 确保开发环境正确设置: - **安装Java JDK**:下载并安装Oracle JDK(如JDK 17)。Oracle JDK提供更好的商业支持和性能优化,适合新手学习企业应用开发[^1]。 下载地址:[Oracle JDK官网](https://www.oracle.com/java/technologies/downloads/) - **安装Oracle Database**:下载Oracle Database Express Edition(XE),它是免费、轻量级版本,适合新手。安装时设置管理员账号(如sys/password)和端口(默认1521)。 下载地址:[Oracle Database XE](https://www.oracle.com/database/technologies/xe-downloads.html) - **IDE设置**:推荐使用IntelliJ IDEA或Eclipse。创建新Java项目,并添加JDBC驱动(下载ojdbc8.jar并导入到项目库中)。 ### 步骤2: 数据库设计(约20-30分钟) 设计数据库表结构,确保信息量大且安全性高,类似引用[2]中的规范化管理需求。使用Oracle SQL*Plus或SQL Developer工具执行以下SQL: - **创建图书表(BOOKS)**:存储图书基本信息,包含主键约束防止数据重复。 ```sql CREATE TABLE BOOKS ( BOOK_ID NUMBER PRIMARY KEY, -- 图书ID,主键确保唯一性 TITLE VARCHAR2(100) NOT NULL, -- 书名 AUTHOR VARCHAR2(50) NOT NULL, -- 作者 ISBN VARCHAR2(20) UNIQUE, -- ISBN号,唯一约束 PUBLISH_DATE DATE, -- 出版日期 STATUS VARCHAR2(10) DEFAULT 'AVAILABLE' -- 状态,如可借阅或已借出 ); ``` - **添加示例数据**: ```sql INSERT INTO BOOKS VALUES (1, 'Java入门教程', '张三', '978-7-121-12345-6', DATE '2023-01-01', 'AVAILABLE'); INSERT INTO BOOKS VALUES (2, '数据库基础', '李四', '978-7-122-67890-1', DATE '2022-05-15', 'BORROWED'); ``` 安全性提示:Oracle提供用户权限管理,创建专门用户(如LIBRARY_USER)并授权仅访问BOOKS表,减少风险[^2]。 ### 步骤3: Java项目设置(约15-20分钟) 采用MVC架构模式(如引用[1]所述),使代码分层清晰: - **项目结构**: - `src/main/java` - `model`: 定义数据模型(如Book类) - `view`: 处理用户界面(简单命令行) - `controller`: 业务逻辑和数据库交互 - `util`: 工具类(如数据库连接) - **创建模型类(Book.java)**:表示图书对象。 ```java package model; public class Book { private int id; private String title; private String author; private String isbn; private String status; // 构造方法、getter和setter省略(新手可自动生成) } ``` ### 步骤4: 实现数据库连接和CRUD操作(约30-40分钟) 使用JDBC(Java Database Connectivity)连接Oracle数据库。确保在`util`包中创建数据库工具类。 - **数据库连接类(DBUtil.java)**:处理连接池,提高效率。 ```java package util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBUtil { private static final String URL = "jdbc:oracle:thin:@localhost:1521:XE"; // Oracle连接URL private static final String USER = "LIBRARY_USER"; // 数据库用户 private static final String PASSWORD = "password"; // 密码 public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } } ``` - **控制器类(BookController.java)**:实现CRUD(增删改查)操作。例如,添加图书方法: ```java package controller; import model.Book; import util.DBUtil; import java.sql.*; public class BookController { public void addBook(Book book) { String sql = "INSERT INTO BOOKS (BOOK_ID, TITLE, AUTHOR, ISBN, PUBLISH_DATE, STATUS) VALUES (?, ?, ?, ?, ?, ?)"; try (Connection conn = DBUtil.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, book.getId()); pstmt.setString(2, book.getTitle()); pstmt.setString(3, book.getAuthor()); pstmt.setString(4, book.getIsbn()); pstmt.setDate(5, new java.sql.Date(book.getPublishDate().getTime())); pstmt.setString(6, book.getStatus()); pstmt.executeUpdate(); System.out.println("图书添加成功!"); } catch (SQLException e) { e.printStackTrace(); } } // 其他方法如deleteBook, updateBook等类似 } ``` ### 步骤5: 用户界面实现(约20-30分钟) 使用简单命令行界面(避免GUI复杂度),适合新手。在`view`包创建主类。 - **主类(LibraryApp.java)**:启动应用并调用控制器。 ```java package view; import controller.BookController; import model.Book; import java.util.Scanner; public class LibraryApp { public static void main(String[] args) { BookController controller = new BookController(); Scanner scanner = new Scanner(System.in); System.out.println("欢迎使用图书管理系统!"); System.out.print("输入图书ID: "); int id = scanner.nextInt(); scanner.nextLine(); // 消耗换行符 System.out.print("输入书名: "); String title = scanner.nextLine(); // 类似获取其他输入 Book book = new Book(id, title, "作者", "ISBN", new Date(), "AVAILABLE"); controller.addBook(book); // 调用添加方法 } } ``` ### 步骤6: 测试和部署(约15-20分钟) - **测试**:运行`LibraryApp`,输入数据检查Oracle表是否更新。使用JUnit添加简单测试。 - **部署**:将项目打包为JAR文件,在命令行运行。确保Oracle服务已启动。 - **安全性优化**:参考引用[2]中的安全性要求,使用Oracle的加密功能(如对密码哈希处理)。 ### 总结 这个图书管理系统实现简单,但覆盖了核心功能:图书增删改查、数据库连接和命令行交互。总时间约2-3小时完成。关键点: - 使用Oracle JDK和Oracle数据库,确保企业级可靠性和性能[^1]。 - MVC架构使代码易于扩展,类似引用[1]中的模式。 - 数据库设计注重信息管理和安全性,符合规范化需求[^2]。 新手建议:从命令行版本开始,熟练后再添加Web界面(如Spring Boot)。遇到问题,查阅Oracle和Java官方文档。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值