说了这么多年的JDBC,你了解它么?

JDBC主要功能及职责

官方文档对JDBC的解释:Java数据库连接 (JDBC,Java Database Connectivity) ,是针对Java编程语言与各种数据库、SQL数据库和其他表格数据源(如电子表格或平面文件)之间独立于数据库的连接的行业标准。其中,JDBC API 是基于 SQL 的数据库访问提供调用级 API

JDBC是Java语言中提供的访问关系型数据库的接口。

JDBC的对数据源的操作基本如下图:


1631243487(1).png

建立数据源连接(Connection)

  1. DataSource (官方推荐方式):调用JDBC 2.0提供的DataSource的getConnection()方法后,DataSource实例会返回一个与数据源建立连接的Connection对象。

    UnpooledDataSource dataSource = new UnpooledDataSource(driverClassLoader, "com.mysql.jdbc.Driver", "jdbc:mysql://127.0.0.1/test", "root", "123");
    Connection connection = dataSource.getConnection();
    

    JDBC API中只提供了DataSource接口,没有DataSource的具体实现。DataSource具体的实现由JDBC驱动程序提供,如JDBCDataSource,MysqlDataSource等。目前主流的数据库连接池(例如DBCP、C3P0、Druid等)也都实现了DataSource接口

  2. DriverManager: JDBC 1.0使用DriverManager类生成一个与数据源连接的Connection对象,通过重载的getConnection()方法,用来获取Connection对象。当应用程序第一次通过URL连接数据源时,DriverManager会自动加载CLASSPATH下所有的JDBC驱动。

    Connection conn = DriverManager.getConnection("jdbc:hsqldb:mem:association_nested", "SA", "");
    

    DriverManager类会尝试加载“jdbc.drivers”系统属性中引用的驱动程序类。

执行sql语句

数据源Connection建立后,通过Connection接口提供的方法来创建Statement、PreparedStatement或者CallableStatement对象,就可以对数据源进行查询和修改操作。

 Connection realConn = conn.getRealConnection();
 Statement statement = realConn.createStatement());
 statement.executeQuery("select * from t");

Statement接口实际是JDBC API中提供的SQL语句的执行器,Statement接口定义了executeQuery()查询操作、executeUpdate()更新操作、executeBatch()批量操作、execute()查询/更新操作、getResultSet()查询结果集、getUpdateCount()更新操作影响的行数等等执行和返回方法。

a. 如果数据库返回的更新数量大于Integer.MAX_VALUE,则需要调用executeLargeXXX()方法

b.int executeXXX(String sql, int autoGeneratedKeys)等方法,第二个参数(autoGeneratedKeys)可以为int,int[],String[]等类型,通过autoGeneratedKeys/columnIndexes/columnNames参数告诉驱动程序哪些列是自动生成的键可以用于检索。如果SQL语句不是INSERT语句,columnNames参数就会被忽略。

处理SQL执行结果

通过Statement接口执行了sql语句,根据返回的ResultSet获取sql执行后的结果,遍历获取最终的查询结果。ResultSet提供相关的getString(int columnIndex),getBoolean(int columnIndex)等各种方法,根据JDBCType等各种枚举最终将每列查询结果的数据类型转换为Java对应的类型。

关闭连接

当所有Sql语句执行完成,并获取结果集后,通过对应的close()方法,正常关闭Statement和Connection。

JDBC 主要内容

Wrapper

Wrapper接口为使用JDBC的应用程序提供访问原始类型的功能,从而使用JDBC驱动中一些非标准的特性。
Connection,DataSource,Statement,ResultSet,DatabaseMetaData等基础接口继承Wrapper,Wrapper包含unwrap()和isWrapperFor()两个方法:

  • unwrap()方法用于返回未经过包装的JDBC驱动原始类型实例,可以通过该实例调用JDBC驱动中提供的非标准的方法。
  • isWrapperFor()方法用于判断当前实例是否是JDBC驱动中某一类型的包装类型。

DataSource

DataSource优点:

  1. 可以通过JNDI注册数据源对象,然后在程序中用一个逻辑名称来引用,JNDI会自动根据这个名称找到与这个名称绑定的DataSource对象。这样就可以使用这个DataSource对象来建立和具体数据库的连接。
  2. DataSource接口支持连接池和分布式事务上。连接池通过对连接的复用,不需要每次操作数据源时都新建一个物理连接,可以显著地提高程序的效率。

PooledConnection

当应用程序调用方法DataSource.getConnection时,返回一个Connection对象。但是当使用数据库连接池时(例如Druid),Connection对象实际上是到PooledConnection对象的句柄,是一个物理连接。

PooledConnection提供了连接池管理的句柄。PooledConnection对象表示到数据源的物理连接。当应用程序完成连接时,连接可以被回收而不是关闭,从而减少了需要建立的连接数。开发人员一般不直接使用PooledConnection接口,它由管理连接池的中间层基础设施使用,即通过一个管理连接池的中间层基础设施使用。

  1. 建立连接

连接池管理器(通常是应用程序服务器)维护一个 PooledConnection的对象池。如果池中有可用的 PooledConnection 对象,则连接池管理器返回一个Connection对象,该对象是该物理连接的句柄。如果没有PooledConnection对象可用,连接池管理器调用ConnectionPoolDataSource的(PooledConnection对象工厂)getPoolConnection方法来创建新的物理连接。一般对应实现 ConnectionPoolDataSource 的 JDBC 驱动程序,创建一个新的 PooledConnection 对象并返回一个句柄给它。如MysqlConnectionPoolDataSource实现如下:

public synchronized PooledConnection getPooledConnection() throws SQLException {
        try {
            Connection connection = this.getConnection();
            MysqlPooledConnection mysqlPooledConnection = MysqlPooledConnection.getInstance((JdbcConnection)connection);
            return mysqlPooledConnection;
        } catch (CJException var4) {
            throw SQLExceptionsMapping.translateException(var4);
        }
    }
  1. 关闭连接

当应用程序关闭连接时,调用Connection的close方法。当连接池完成时,连接池管理器会收到通知(使用ConnectionPool的addConnectionEventListener方法,将自己注册为ConnectionEventListener对象)。连接池管理器停用PooledConnection对象的句柄并将PooledConnection对象返回到连接池,以便它可以再次使用。因此,当应用程序关闭其连接时,底层物理连接将被回收而不是被关闭。 在连接池管理器调用PooledConnection的close方法之前,物理连接不会关闭。通常调用close方法来有序关闭服务器。

调用Connection对象的commit()方法能够关闭当前事务中创建的ResultSet对象。

  1. 分布式连接
  • XAConnection继承PooledConnection,为分布式事务提供支持的对象。XAConnection 对象可以通过XAResource对象加入分布式事务。事务管理器,通常是中间层服务器的一部分,通过XAResource对象管理XAConnection。应用不直接使用这个接口;它由在中间层服务器中工作的事务管理器使用。
  • XAConnection接口继承了PooledConnection接口,具有所有PooledConnection的特性,我们可以调用XAConnection实例的getConnection()方法获取java.sql.Connection对象

RowSet 和 ResultSet

  1. RowSet接口继承java.sql包下的ResultSet接口,提供了一组 JavaBeans 属性,允许将RowSet实例配置为连接到 JDBC 数据源并从数据源读取一些数据(RowSet用于为数据源和应用程序在内容中建立一个映射)。一组 setter 方法(setInt、setBytes、setString等)提供了一种将输入参数传递给行集的命令属性的方法。此命令是行集在从关系数据库获取数据时使用的 SQL 查询。RowSet接口支持 JavaBeans 事件,允许在行集上发生事件时通知应用程序中的其他组件,例如其值的更改。
  • RowSet对象可以建立一个与数据源的连接并在其整个生命周期中维持该连接,在这种情况下,该对象被称为连接的RowSet

  • RowSet对象还可以建立一个与数据源的连接,从其获取数据,然后关闭它,这种RowSet被称为非连接RowSet。非连接Rowset可以在断开时更改其数据,然后将这些更改写回底层数据源,不过它必须重新建立连接才能完成此操作。

相较于java.sql.ResultSet,RowSet的离线操作能够有效地利用计算机内存减轻数据库的负担。由于数据操作都是在内存中进行,然后批量提交到数据源,因此灵活性和性能有很大的提高。RowSet默认是一个可滚动、可更新、可序列化的结果集,而且它作为一个JavaBean组件,可以方便地在网络间传输,用于两端的数据同步。通俗来讲,RowSet就相当于数据库表数据在应用程序内存中的映射,我们所有的操作都可以直接与RowSet对象交互。RowSet与数据库之间的数据同步,开发人员不需要关心。

  1. ResultSet的类型、并行性和可保持性等属性可以在调用Connection对象的createStatement()、prepareStatement()或prepareCall()方法创建Statement对象时设置,例如:
Connection connection = DriverManager.getConnection("abc");
Statement statement = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE, ResultSet.CLOSE_CURSORS_AT_COMMIT);

ResultSet类型

TYPE_FORWARD_ONLY(默认):游标只能向前移动,从第一行到最后一行

TYPE_SCROLL_INSENSITIVE:游标可以向前/向后滚动(相对于当前位置),也可以滚动到相对位置,ResultSet对象的修改不会影响对应的数据库中的记录

TYPE_SCROLL_SENSITIVE:游标可以向前/向后移动(相对于当前位置),也可以移动到绝对位置。ResultSet对象的修改会直接影响数据库中的记录

ResultSet并行性

CONCUR_READ_ONLY(默认):为ResultSet对象设置这种属性后,只能从ResulSet对象中读取数据,但是不能更新ResultSet对象中的数据。

CONCUR_UPDATABLE:该属性表明,既可以从ResulSet对象中读取数据,又能更新ResultSet中的数据。

ResultSet可保持性

HOLD_CURSORS_OVER_COMMIT:当调用Connection对象的commit()方法时,不关闭当前事务创建的ResultSet对象。

CLOSE_CURSORS_AT_COMMIT:当前事务创建的ResultSet对象在事务提交后会被关闭,这样能够提升系统性能。

ResultSet对象关闭后,不会关闭由ResultSet对象创建的Blob、Clob、NClob或SQLXML对象,除非调用这些对象的free()方法进行清除

事务

Connection接口中提供了一个setTransactionIsolation()方法,允许JDBC客户端设置Connection对象的事务隔离级别。
Connection对象的autoCommit属性决定什么时候结束事务。启用自动提交后,会在每个SQL语句执行完毕后自动提交事务。当Connection对象创建时,默认情况下,事务自动提交是开启的。Connection接口中setAutoCommit()方法,可以禁用事务自动提交。这种情况下,需要调用Connection接口的commit()方法进行显式提交事务,或者调用rollback()方法回滚事务。禁用事务自动提交适用于需要将多个SQL语句作为一个事务提交或者事务由应用服务器管理。

“老生常谈”的事务隔离级别

  1. 脏读、幻读、不可重复读
  • 脏读,读取未提交的数据导致的。例如,A事务修改了一条数据,但是未提交修改,此时A事务对数据的修改对其他事务是可见的,B事务中能够读取A事务未提交的修改。如果A事务回滚,B事务中读取的就是不正确的数据。
  • 不可重复读,(1)A事务中读取一行数据。(2)B事务中修改了该行数据。(3)A事务中再次读取该行数据将得到不同的结果。
  • 幻读,(1)A事务中通过WHERE条件读取若干行。(2)B事务中插入了符合条件的若干条数据。(3)A事务中通过相同的条件再次读取数据时将会读取到B事务中插入的数据。
  1. 几种事务隔离级别如下:
  • TRANSACTION_NONE:表示驱动不支持事务,不兼容JDBC规范的驱动程序。
  • TRANSACTION_READ_UNCOMMITTED:允许事务读取未提交的数据,可能会出现脏读、不可重复读、幻读等现象。
  • TRANSACTION_READ_COMMITTED:在事务中进行的任何数据更改,在提交之前对其他事务是不可见的。可以防止脏读,不能解决不可重复读和幻读。
  • TRANSACTION_REPEATABLE_READ:能够解决脏读和不可重复读,但是不能解决幻读。
  • TRANSACTION_SERIALIZABLE:事务串行执行,能够有效解决脏读、不可重复读和幻读题,但是并发效率较低

参考资料

JDBC 4.2规范文档:https://download.oracle.com/otndocs/jcp/jdbc-4_2-mrel2-spec/index.html

JDBC在Java 8中相关的功能:https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/jdbc_42.html

JTA规范文档:http://download.oracle.com/otndocs/jcp/jta-1.1-spec-oth-JSpec/?submit=Download

©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,099评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,828评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,540评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,848评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,971评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,132评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,193评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,934评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,376评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,687评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,846评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,537评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,175评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,887评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,134评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,674评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,741评论 2 351

推荐阅读更多精彩内容

  • 以下我是归纳的JDBC知识点图: 图上的知识点都可以在我其他的文章内找到相应内容。 JDBC常见面试题 JDBC操...
    Java3y阅读 1,723评论 0 15
  • 1.定义 JDBC(Java DataBase Connectivity)是Java和数据库之间的一个桥梁,是一个...
    唐騦忆阅读 341评论 0 0
  • 什么是JDBC,在什么时候会用到它? JDBC的全称是Java DataBase Connection,也就是Ja...
    青青子衿zq阅读 403评论 0 0
  • JDBC API 给 Java 程序提供了一种访问一个或者多个数据源的途径,在大多数情况下,数据源是关系型数据库,...
    zlb阅读 819评论 0 3
  • 8 MyBatis数据源与连接池# 8.1 MyBatis数据源DataSource分类## MyBatis数据源...
    七寸知架构阅读 10,258评论 10 106