Java学习之JDBC

Posted 喝水天天

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java学习之JDBC相关的知识,希望对你有一定的参考价值。

Java学习之JDBC

想写这类文章一经很久了,一直没时间,其实也不是真的没时间,只是感觉太累了,而且自己学的不够深,就一直搁浅。算下来,从真的开始学习写代码到现在已经四年了。

到了新公司这半年,确实每天都跟打了鸡血一样(每晚11点30分左右下班,回家还要看会书,早上七点起床到公司),没感觉到累,只是感觉压力太大,现在算是适应了,对Java算一个全新的认识,排名第一的语言总是有它的独特之处,强大的社区支持,Java EE其实真的博大精深,虽然大家都不采用,但是设计理念真的很先进,不过稍微重量级一点,而且强依赖了Java EE容器,预期说容器,我觉得不如说框架。强大归强大,大家都不领情,随着spring等轻量级工具(说工具,其实是spring的设计理念都是非入侵式的,然而我个人感觉整个spring 的建设都是围绕着Java EE,并试图简化Java EE)。
说了这么多,并不是觉得自己牛逼,牛逼的人大有人在,我在公司现在估计连螺丝钉都算不上…讲这些只是想说说自己的经历以及对自己学习过程中的一些想法的纪录。
进入正题,瞎扯了这么多,哈哈哈~~

持久化

目前广泛使用的应该还是mysql,nosql。对于NoSQL目前我还没有太多的认识,长路漫漫,慢慢学习中…
在Java中,JDBC应该是被大家摒弃的,因为如果真的采用JDBC去写代码,太多模版代码,有点洁癖的程序猿估计都不想看到自己的代码冗余,会想方设法的去减少模版代码,这个时候就有太多的工具了,意图减少此类模板代码(其实主要的模版代码是建立connection,组装sql,获取结果集,组装POJO,顺便说明一下,有人主张划分VO,DTO,POJO,经过我自己写代码,有好处有不好的地方,看自己的偏好吧),spring jdbctemplate(技术应该已经过时)、hibernate、mybatis、JSR-220、spring data…东西太多了,我知道的太有限,但是估计大家都知道,这些框架,或者工具的简历都离不开JDBC,由此可见JDBC其实还是很重要的,虽然这些工具简化了数据库的操作,但是如果想知道为什么这么设计,了解JDBC你就知道了~~~

数据库框架选择

这个问题其实我在网上查了很久,翻了一下我的第一条查询纪录,大概是四月份,在两个月之前我就开始查了, 一直没找到合适的解释,不过我还是想要说说我自己的看法.

  • JDBC 大家都知道它是啥玩意,又各个厂商开发提供实现,Java SE制定了相关的规范
  • JPA JSR-220,在众多框架的发展之后,JCP意识到应该有一个规范了,于是JPA诞生了
  • hibernate 这个不用多说了,大家都懂
  • mybatis 好用,简单,学习成本低
  • spring data 目标在于对JPA再做一次减法

对于以上内容,估计大部分公司都会做出自己的选择,我也在网上咨询了很多扣扣群,问了群里的大神们自己对数据库框架的选型,其实貌似也没告诉我为什么。一下就是我对框架的看法:

对于这么多框架,其实主要使用的就是hibernate、mybatis。spring data的使用应该还比较少。我问了我很多同事,大家都说hibernate太重,其实功能强大为什么不重呢…mybatis大家可以定制sql,扪心自问,我们有多少数据库查询需要这么care数据库性能。如果我来制定数据库技术方案,我肯定优先使用spring data + JPA + hibernate,实在不行再采用spring + mybatis,原因如下:我自己做事情比较喜欢标准化,因为JPA是标准,而hibernate则很好的支持了JPA,spring data有效的减少了模版代码的产生,加上spring data 可以与批处理框架结合使用,因此我觉得使用它真的是不二之选

JDBC

大部分网上的资料对JDBC的介绍过于简单,只提及了JDBC都不算主要功能的功能。在我看来,JDBC分为以下几部分:

  • 数据库驱动,大家熟悉的DriverManager
  • Connection
  • Statement(包含很多种,下文会依次介绍)
  • ResultSet
  • Transactions
  • 异常
  • RowSet

驱动

为了获取链接,我们必须要有驱动,驱动其实就是数据库厂商为了适配JDBC而对自己的数据库客户端做的初始化而已,在JDBC 4.0之前我们需要这样初始化:

 Class.forName("com.mysql.jdbc.Driver"); 

在JDBC 4.0以后我们将不在需要这行代码。
按照oracle的划分,数据库驱动分为四大类:

  • 做接口映射的驱动,这种驱动把JDBC API映射到其他类型的数据库API
  • 采用Java语言和JNI编写的驱动
  • 采用一个中间的代理服务器,类似于阿里的中间件一样的东东
  • JDBC 直接跟数据库交互,最直接的方式

与其说oracle划分了四种类型,不如说oracle提供了四种方案,让我们可以选择。

Connection

对于数据库与业务之间,Connection是我们的主体部分,大部分异常处理其实都是这货给的。这里不得不说一句,Golang的多返回值是多么的好用呀,让异常都消失吧。在JDBC中获取链接的方式有两种,分别如下:

  • DriverManager: 相信众多初学者跟我一样,这个是我们第一个接触到的类,对于另一个我觉得可能大部分人应该不会太熟悉,这里就不再说他了。
  • DataSource: 其实这个接口是oracle提倡使用的接口,因为他提供了更多细节
1. 使用DriverManager

没什么好说的,我直接贴代码了~

private static final String DB_URL = "jdbc:mysql://localhost:3308/database";
private static final String DB_USERNAME = "admin";
private static final String DB_PASSWORD = "admin";

public Connection getConnection() throws SQLException{
    Connection conn = null;

    Properties connectionProps = new Properties();
    connectionProps.put("user", DB_USERNAME);
    connectionProps.put("password", DB_PASSWORD);

    conn = DriverManager.getConnection(DB_URL, connectionProps);

    return conn;
}

2.采用DataSource获取Connection

DataSource接口由各个驱动器厂商实现,oracle把他们划分如下:

  • 基本的DataSource,它产生的每一个Connection都互不干扰,没有池以及分布式事务的概念
  • 支持连接池的DataSource
  • 支持分布式事务的DataSource

oracle 规定,一个驱动至少实现一个基本的DataSource,在学习数据库的时候我相信大家肯定见过BaseicDataSouce这个东西,当时我看到的时候我也不明白,不过现在明白了,哈哈哈,在使用DataSource中其实比DriverManager更复杂,但是更实.

2.1 部署Baseic DataSource
  • 创建一个DataSource 类实例
  • 设置属性
  • 使用JNDI注册DataSource实例
创建数据源并且设置属性
BasicDatabaseSource  ds = BasicDatabaseSource();
ds.setServerName("server");
ds.setDatabaseName("database");
ds.setDescription("description");
使用JNDI
Context ctx = new InitialContext();
ctx.bind("jdbc/sty", ds);   

在学习之前,觉得JNDI到底是什么,其实花点时间,就知道了,道理很简单,反正Java EE容器做了。

获取数据源
Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("jdbc/sty");

Connection conn = ds.getConnection("server", "database");

使用DatabaseSource 解决了数据库url的硬编码,当然有很多方式可以解除硬编码,除此之外,DatabaseSource一般会和ConnectionPoolDataSrouce配合食用。类似的,Datasource还和XADataSource一起使用,实现分布式事务。

2.2 部署Pooled DataSource

部署Pooled DataSource比Basic DataSource稍微复杂一些(分布式也是类似),不过都是模版话的配置,学习成本并不高。
之前提到过,Polled DataSource与基本的DataSource是配合使用的,意味着我们需要部署两个类。下面假设我们有两个类,第一个类是我们的池实现类:

class PooledDataSource implements javax.sql.ConnectionPoolDataSource{
    // implements here
}

第二个类如下:

class FastDataSource implements javax.sql.DataSource{
    // adapt ConnectionPoolDataSource here
}

为什么采用这样的方式,其实去看看JDBC的接口就可以了。
下面就是使用它们的时候了,首先我们注册我们的连接池类:

 PooledDataSource pds = new PooledDataSource();
 pds.setServerName("pds");
 pds.setDatabaseName("database");
 pds.setPortNumber(3800);
 pds.setDescription("Polled DataSource");

 Context ctx = new InitialContext();
 ctx.bind("jdbc/pool/datasource", pds);

现在设置我们的另一个类:

 FastDataSource fds = new FastDataSource();
 fds.setDescription("produces pooled connections to COFFEEBREAK");
 fds.setDataSourceName("jdbc/pool/fastCoffeeDB");
 Context ctx = new InitialContext();
 ctx.bind("jdbc/fastDatasource", fds);   

使用数据源:

ctx = new InitialContext();
ds = (DataSource)ctx.lookup("jdbc/fastCoffeeDB");       
2.3 部署分布式事务 DataSource

与池比较类似,我们有两个类:

class XATransactionalDataSource implements javax.sql.XADataSource{
    // implements here
}

class TransactionalDataSource implements javax.sql.DataSource{
    // adapt here 
}

部署如下:

XATransactionalDataSource xads = new XATransactionalDataSource();
xads.setServerName("xads");
xads.setDatabaseName("database");
xads.setPortNumber(9040);
xads.setDescription("Distributed transactions DataSource");

Context ctx = new InitialContext();
ctx.bind("jdbc/xa/xads", xads);


TransactionalDataSource tds = new TransactionalDataSource();
tds.setDescription("suibianla");
tds.setDataSourceName("jdbc/xa/xads");

Context ctx = new InitialContext();
ctx.bind("jdbc/ds", ds);

使用如下:

Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("jdbc/ds");
Connection con = ds.getConnection();    

Statement

按照API的划分,Statement分为三大类:

  • Statement
  • PreparedStatement
  • CallableStatement

这部分比较简单,再次就不在介绍了,以后有时间的话再来完善。

ResultSet

ResultSet 按照功能被划分为三类,这里直接使用它们的定义介绍了:

  • TYPE_FORWARD_ONLY:最简单,功能最单一,类似于c++里面的强项迭代器
  • TYPE_SCROLL_INSENSITIVE:次之,类似于c++里面的双向迭代器
  • TYPE_SCROLL_SENSITIVE:有双向迭代器的功能,并且和底层数据库保持连接,如果的层数据库发生变化,他会更新内存

按照是否能修改的层数据库划分为两类:

  • CONCUR_READ_ONLY
  • CONCUR_UPDATABLE

读取ResultSet的时候有有一个概念需要介绍,那就是Cursor,翻译成光标感觉不太好,下文就直接沿用Cursor这个单词,当调用Collection.commit的时候ResultSet对象会自动关闭,有时候我们的需求可能并不是这样,这时候我们需要设置另一个属性,ResultSet提供了这个属性,描述如下:

  • HOLD_CURSORS_OVER_COMMIT:ResultSet 的cursor不会被关闭。
  • CLOSE_CURSORS_AT_COMMIT:如果追求性能的话,建议采用这种方式。

Transactions

事务的主要功能是把数据库操作划分成一个单元,这个单元可以看作是原子的,其中事务也有不同的等级划分,这里直接摘抄一个表格:

事务隔离等级 是否会话 读取的脏数据 幂等读取 幻觉的读取
TRANSACTION_NONE 不适用 不适用 不适用 不适用
TRANSACTION_READ_COMMITTED 支持 不允许 允许 允许
TRANSACTION_READ_UNCOMMITTED 支持 允许 允许 允许
TRANSACTION_REPEATABLE_READ 支持 不允许 不允许 允许
TRANSACTION_SERIALIZABLE 支持 不允许 不允许 不允许

以上是关于Java学习之JDBC的主要内容,如果未能解决你的问题,请参考以下文章

java学习之jdbc的封装

Java学习之JDBC

Java学习之JDBC

MySQL学习之第七章-JDBC

JavaEE学习之jdbc基础认识

Mybatis3源码学习之二JDBC