Java数据库连接(Java Database Connectivity)是用来规范客户端如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。通过JDBC,可以使用Java通过数据库驱动连接到数据库,编写SQL语句进行操作。
JDBC实现了开放式数据库连接(Open Database Connectivity)标准——一种早期使用C语言实现的数据库连接标准。ODBC提供了一套用来访问数据库的标准API,同时提供了驱动管理器,当数据库驱动注册到驱动管理器后,能够通过该驱动连接特定的数据库,对数据库进行访问。而JDBC就是Java版本的ODBC。
数据库驱动主要由数据库的提供者开发。对于JDBC,不同的数据库驱动能够操作相对应的数据库。JDBC的驱动类型主要分为以下4种:
- JDBC-ODBC桥:此类驱动程序将JDBC调用转换成ODBC调用 , 然后使用ODBC与数据库进行通信。早期Java包含了驱动程序:sun.jdbc.odbc.JdbcOdbcDriver,但使用之前需要对ODBC数据源进行配置。JDK1.8不再提供JDBC/ODBC桥的支持。
- 本地API驱动:用来和数据库的客户端API进行通信。使用前,需要安装Java类库和一些平台相关的代码(特定的驱动程序,类似ODBC)。通过驱动程序的转换,把Java程序中使用的JDBC API转换成NativeAPI,进而存取数据库。
- 网络协议驱动:使用与具体数据库无关的协议,将数据库请求发送给服务器中间件。服务器再将数据库请求翻译成符合数据库规范的调用,再把这种调用传给数据库服务器。执行效率受网络带宽的影响。
- 本地协议驱动:将JDBC请求直接转换成符合相关数据库系统规范的请求,可以直接和数据库通信。这种类型的驱动完全由Java实现,因此实现了平台独立性。执行效率相对于其他三种类型,不需要将jdbc调用转换成odbc/本地数据库接口/中间层服务器,效率高。最常用的驱动方式。
连接步骤
```java
- 注册驱动
- 获取连接对象
- 获取Statement对象
- 执行SQL语句
- 处理查询结果集
- 关闭连接
```
注册驱动
因为JDBC就是Java版本的ODBC,和ODBC一样,也需要将数据库驱动注册到驱动管理器后才能和数据库进行通信。
- java.sql.Driver是JDBC中提供的驱动接口,每一种数据库的驱动类都要实现这个接口。
- 对于Oracle,驱动可以使用oracle.jdbc.driver.OracleDriver或oracle.jdbc.OracleDriver,前者是后者的子类。它们都是java.sql.Driver的实现类。
- 在底层,当加载了一个驱动类后便能够将驱动注册到驱动管理器中。
- 加载驱动类的方法包括:创建驱动对象、Class.forName(驱动类全类名)、设置环境变量,共三种方法。 ```java DriverManager.registerDriver( new oracle.jdbc.driver.OracleDriver()); // 在启动管理器注册驱动对象 new oracle.jdbc.driver.OracleDriver(); // 当驱动对象创建时也会自动注册
Class.forName(“oracle.jdbc.driver.OracleDriver”); // 使用反射可以加载驱动类,从而注册驱动对象,最推荐
System.setProperty(“jdbc.drivers”, “oracle.jdbc.driver.OracleDriver”); // 设置System环境变量,将jdbc.drivers的值设置为驱动类的全类名
<a name="GM2dn"></a>
### 获取连接对象
java.sql.DriverManager是JDBC的驱动管理器,注册驱动后,可通过它获取到数据库连接对象。
- 告诉驱动管理器要连接到哪个数据库,管理器就会使用该数据库的驱动,并返回一个连接对象。该操作通过DriverManager.getConnection(url,user,password)方法实现。
- 要连接的数据库通过url确定,并通过用户名和密码连接到数据库。
```java
String url = "jdbc:oracle:thin:@localhost:1521:XE";
// 格式:jdbc/odbc:数据库厂商:连接方式(thin/fat):@ip:端口:数据库名
String user = "jy"; // 用户名
String password = "jy"; // 密码
Connection connection = DriverManager.getConnection(url, user, password);
Properties properties = new Properties(); // 也可以使用Properties对象
properties.setProperty("user", "jy");
properties.setProperty("password", "jy");
Connection connection = DriverManager.getConnection(url, properties);
执行SQL语句并处理
通过连接对象,可以向数据库传输SQL语句,对数据库进行操作。如果是查询操作,还会返回一个查询结果。
- SQL语句使用Statement进行包装。使用connection.createStatement()方法获取SQL语句对象。语句对象可以执行一条SQL,也可以批量执行。
- 返回的查询结果使用java.sql.ResultSet进行包装。ResultSet的数据按数据类型和字段进行存储,不同的数据类型可以使用不同的方法获取。使用迭代器获取每一行的数据。
- 获取结果集时,列索引从1开始;列名为查询时指定的列名(别名)。
```java
Statement statement = connection.createStatement();
String sql = “create table “
- “kk(id number primary key,”
- “name varchar2(50))”; // 这里的SQL语句不能有分号 statement.execute(sql); // 执行SQL,返回一个boolean,表示有没有查询结果 statement.executeUpdate(sql); // 执行SQL,获取影响的行数,查询不影响行数
ResultSet result = statement.executeQuery(sql); // 执行SQL,获取查询结果集 while (set.next()) { // 有下一个记录时 long id = set.getLong(1); // 获取第1列,是Long类型的 String name = set.getString(2); // 获取第2列,是String类型的 long id2 = set.getLong(“id”); // 获取Long类型的id列 String name2 = set.getString(“name”); // 获取String类型的name列 System.out.println(id + “: “ + name); }
String sql2 = “delete from kk where id = 1”; String sql3 = “update kk set name = ‘12222’ where id = 2”; statement.addBatch(sql); // 添加语句 statement.addBatch(sql2); statement.addBatch(sql3); int[] is = statement.executeBatch(); // 批量执行SQL语句,返回的数组表示每条SQL影响的行数
<a name="SY2Te"></a>
### 关闭连接
数据库操作完毕后,使用close()方法关闭ResultSet、Statement和Connection连接,释放资源。
<a name="ynBw5"></a>
## 注意事项
<a name="1315r"></a>
### 配置文件
在前两个步骤中,因为不同的应用程序要连接的数据库是不一样的,故需要指定不同的驱动,设置不同的url、用户名和密码。这就需要对该步骤进行封装,并从外部文件获取数据库配置。这也是为什么推荐使用反射的forName()方法来注册驱动的原因。数据库配置可以从[Properties](https://www.yuque.com/linja2020/saise0/qz71q7)配置文件中读取。<br />在对项目进行打包后,配置文件的路径可以从类加载路径获取。当配置文件放在src目录下时,配置文件的路径就是类加载器的根路径:
```java
InputStream is = Test.class.getClassLoader().getResourceAsStream("datasource.properties");
properties.load(is);
Statement
除了普通的SQL语句接口Statement,该接口还有两个子接口:PreparedStatement和CallableStatement。其中后者又是前者的子接口。
PreparedStatement(简称PS)是预处理的SQL语句接口,它具有以下特殊功能:
- 预处理:能够对SQL进行预处理,在处理过程中将语句传递给数据库进行验证,避免错误的SQL。同时可以在此过程中对SQL语句进行检查,避免SQL语句注入等安全问题。
- 占位符:PS具有占位符功能,对SQL检查完毕后,此后的数据只需填补占位符(?)即可,不用发送完整的SQL语句,避免连接字符串。对于大量重构的SQL语句(即结构相同数据不同的语句),速度更快。注意,表名等元数据不能使用占位符,只有数据可以。
- 批处理:PS只能批处理同构的SQL语句。
CallableStatement能够调用数据库中的存储过程。String sql = "insert into t_user(id, name, age) "
+ "values(user_seq.nextval, ?, ?)";
PreparedStatement ps = conn.prepareStatement(sql);
for (int i = 1; i <= 10000; i++) {
ps.setString(1, "tom" + i); // 1是占位符序号,后面的是数据
ps.setInt(2, 20);
ps.executeUpdate(); // 执行
// ps.addBatch(); 添加到批处理
}
// ps.executeBatch(); 执行批处理
事务处理
JDBC默认自动提交事务,但这样破坏了事务的原子性。可以改为手动提交。也可以回滚和设置记录点。connection.setAutoCommit(false); // 设置不自动提交事务
connection.commit(); // 提交一次事务
connection.rollback(); // 回滚事务
Savepoint savepointC = connection.setSavepoint("c"); // 设置记录点
connection.rollback(savepointC); // 回滚到记录点