你的位置:首页 > Java教程

[Java教程]Java 连接、操控数据库总结(JDBC)


看到数据库连接不由得想起了大一末参加团队考核时的悲催经历~~,还记得当初傻傻地按照书本的代码打到 Eclipse 上,然后一运行就各种报错。。。报错后还傻傻地和书本的代码一遍又一遍地进行核对,发现无误后,还特别纠结——代码和书本一样,怎么就报错了呢? 最后通过 Google 才得知要添加驱动包,就这样好多个小时就白白浪费掉了 ~~ 当初连 JDBC 与 JDBC Driver 还没区分好,往事不堪回首。。。

大二末,抱着“自己被抗了,也坑坑师弟,让他们体验下爬坑的经历”的心态,考核师弟时故意没发驱动包给他们,事后听他们说就因为这坑了他们几天的时间。。。

大三末,轮到大二师弟考核大一的师弟,然后大二的师弟和大一的说,“数据库连接时,需要一个驱动包,你们自己去了解、下载,具体的就不说了,要学会解决问题。当年带我的师兄也没直接提供给我,还害我花了几天的时间。所以今年我算好了,还提醒你们要注意。。。”,听到这我背后不由的一凉,这得有多大的怨气。。。(^_^,这不怪我,叫了你们遇到解决不了的问题可以问师兄,你们没问而已,逃~~)

好了,回忆结束~~。为了怀念以前的“懵懂青春”,总结下 Java的数据库连接以及 JDBC、JDBC Driver。

JDBC 与 JDBC Driver

首先要搞清楚的是,什么是 JDBC,什么是 JDBC Driver

  1. JDBC,JDBC 的全称为 Java Database Connectivity,它定义了一套访问数据库的 API。使用这些 API 你就可以使用 Java 来操控数据库,执行 selectupdatedeleteinsert 等常用的操作。(具体定义请看维基百科

  2. JDBC Driver,JDBC Driver 就是 Java 与数据库之间的一层软件组件——驱动。就像我们的鼠标驱动、键盘驱动等驱动,它担任了一个中间人、一名翻译者。因此,要想使用 Java(JDBC API)来访问操控数据库,我们还需要 JDBC Driver 来负责翻译。(维基百科上有具体介绍)

JDBC Driver

JDBC Driver 一般是由相应的数据库提供的,比如 MySQL 提供了 Connector/J 驱动 (根据官方说明,Connector/J 属于第 4 种类型的驱动。想了解更详细、更多的驱动类型可以参加维基百科)。

JDBC Driver 的安装

JDBC Driver 的安装很简单,只需要去官网下载它提供的驱动,并把相应的 Jar 包(通常叫作 mysql-connector-java-version-bin.jar)放在 Java Classpath 下就 OK,如果你和我一样是搞 J2EE 的,你也可以直接把它放在 WEB-INF/lib 目录下,或者用类似 Maven 之类的工具来添加。详细请看 官方文档 。

使用

JDBC Driver 的使用在 这 有很多例子,因此不再累赘了。毕竟它只是一个驱动,我们更多的是使用 JDBC API 调用这个驱动与数据库打交道。更多的使用总结请看下部分。

JDBC API

JDBC 只是 Java 中定义的一些接口,它也属于 JDK 的一部分,就像文件等普通接口一样,我们只需要调用它来完成目的就 OK 。既然是 API 我们就要去看文档熟悉它才能更好地使用它,这些类分别处于 java.sql 和 javax.sql。

连接

要想使用 JDBC API 来操控数据库,首先要连上数据库。连接数据库有两种方法:

  • 使用 DriverManager 。第一次使用 DriverManager 来建立与数据库的连接时,它会自动在 class path 中寻找并加载 JDBC 4.0 驱动。要注意的是,如果是 4.0 之前的版本,需要手动去加载。
  • 使用 DataSource 。根据官方推荐,我们应该优先使用 DataSource 。相对于比较简单的 DriverManager ,它比较复杂,也比较全面、详细。

使用 DriverManager 连接数据库:
要使用 DriverManager ,我们先要把该类加载进来,使用最简单的方法(在要加载的类里添加以下代码)

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

加载了 DriverManager 后,我们就可以使用它来获取与数据库的连接(假设我们使用本地数据,默认路径为 localhost ;数据库名为 test ;用户名为 root;密码为 123):

// DriverManager、Connection 类在 java.sql 里都有定义String URL = "jdbc:mysql://localhost:3306/test?user=root&password=123";Connection conn = DriverManager.getConnection(URL); //或者String URL = "jdbc:mysql://localhost:3306/test";String user = "root";String password = "123";Connection conn = DriverManager.getConnection(URL, user, password); //或者String URL = "jdbc:mysql://localhost:3306/test";Properties connectionProps = new Properties();connectionProps.put("user", this.userName);connectionProps.put("password", this.password);Connection conn = DriverManager.getConnection(URL, connectionProps); 

(个人觉得,第一种看起来简单、方便,但是修改麻烦、可读性不高,不太建议使用。如果你只想简单地传递用户名与密码可以选用第二种。如果你有很多参数要传递,比如字符编码、用户名密码等,优先选择第三种。)

连接数据库,我们只需调用 getConnection() 方法并且返回与数据库的连接(Connection)就 OK ,有了此连接我们就可以操作数据了。

在这要注意的是 getConnection 方法里的字符串参数 URL 。该字符串指定了数据库的路径、数据库名、数据库配置(用户名密码等)。 MySQL 的 URL 语法如下:

jdbc:mysql://[host][,failoverhost...][:port]/[database] [?propertyName1][=propertyValue1][&propertyName2][=propertyValue2]...
详细说明

使用 DataSource 连接:
DataSource 连接涉及的东西比较多,找时间另开一篇来总结。 ^_^

创建语句

连接上了数据库,我就可以操控数据库了。通常我们是编写 SQL 语句来操控数据库的。因此,在操控数据库之前,我们还要创建 SQL 语句。JDBC 定义了 3 种类型语句接口,用来运行 SQL 语句,并且返回执行结果:

  • Statement
  • PreparedStatement
  • CallableStatement

相应地,创建语句也有三种方法:

  • Connection.createStatement() ——创建普通的语句,它通常不需要提供参数。更多
  • Connection.prepareStatement(String stringSQL)——创建预编译语句,通常要提供一个带有占位符的字符串 SQL 语句。 更多
  • Connection.prepareCall()——创建存储过程。更多

这里要注意的是,Statement 与 PreparedStatement 的区别。它们的最主要区别就是:

  1. PreparedStatement 对 SQL 语句进行了预编译,在需要运行多次 SQL 语句是能得到显著的性能提升
  2. 可以有效地防止 SQL 注入攻击( SQL injection attacks )
  3. 可以轻松地在 SQL 字符串里使用非标准的 Java 对象,如DateTimeTimestampBigDecimal
  4. 相对于使用拼接方式的 Statement,它使用占位符(?) 很好地把查询语句以及变量值分开。
  5. 更多请看 stackoverflow 上的讨论

操控数据库

平时我们操控数据库,最常用的、最简单的就是selectupdatedeleteinsert 等常用的操作了。那么我们是怎么通过 JDBC API 来实现这些行为的呢?

通常,我们会使用 Statement.executeQuery(String sql) 来执行 select 查询操作,它会返回一个 ResultSet 的对象,这个对象包含了查询返回的数据。updatedeleteinsert 等更新操作使用 Statement.executeUpdate(String sql),它会返回一个整数,代表影响的行数。

ResultSet

ResultSet 是查询数据库时返回的数据集,我们可以把它想象为一张与数据表类似的数据表,就像我们使用命令行执行 select 语句时控制台返回的数据表。不同之处是它拥有一个一开始处于第一行数据的前一行的指针。然后我们就可以通过不停地调用 ResultSet.next()来移动指针获取每一行的数据,该函数在指针移动到最后一行的下一行时就返回 false ,我们可以利用这个特性来作为终止条件遍历整张表。(第一行的前一行与最后一行的下一行都是不存在的行,是虚拟的行)

此外,ResultSet 有 3 中类型(更多):

  1. TYPE_FORWARD_ONLY:这个是默认的类型,它只能不同地调用 next() 方法把指针向下一行移动,而不能往回走。
  2. TYPE_SCROLL_INSENSITIVE:这个类型除了可以调用 next() 方法把指针向下一行移动外,还可以调用previous() 方法把指针指向前一行,使用 first 方法把指针移动到第一行等。
  3. TYPE_SCROLL_SENSITIVE:此方法与第 2 个类型一样,可以任意移动指针。但它是敏感型的。也就是说,当你使用Statement.executeQuery(String sql) 产生了 ResultSet 后,数据库中响应的数据发生了改变,它会更新 ResultSet 并保持数据与数据库一致。而第 2 个类型是非敏感型的,它不会更新 ResultSet。

要指定 ResultSet 的类型,我们只需在创建 Statement 时指定:

 Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE);

OK,我已经可以在获取数据表中的任意行了,我们现在来获取一行数据中的特定列的数据。ResultSet 提供了 getter 方法(getBoolean、getLong、getInt等方法)来获取当前行的列。这些方法的参数既可以是列的索引号(从 1 开始的整型)也可以是列的别名或者列名(字符串)。比如 ResultSet.getInt(1) 获取第一列的数据,ResultSet.getFloat("score") 获取列名为 score 的列