Java 数据库操作


JDBC基础

JDBC是Java应用与数据库管理系统进行交互的标准API,包括两个包:核心API—java.sql和扩展的API—javax.sql。应用程序通过核心API的接口实现数据库连接和数据处理,其主要接口如下:

接口名称 功能
java.sql.Driver 驱动程序,连接应用程序和数据库,用于读取数据库驱动器的信息,提供连接方法,建立访问数据库所用的Connection对象。在加载某一Driver类时,它应该创建自己的实例并向DriverManager注册该实例。
java.sql.DriverManager 驱动程序管理器,管理一组Driver对象,对程序中用到的驱动程序进行管理,包括加载驱动程序、获得连接对象、向数据库发送信息。在调用getConnection方法时,DriverManager会试着从初始化时加载的那些驱动程序以及使用与当前 applet 或应用程序相同的类加载器显式加载的那些驱动程序中查找合适的驱动程序。
java.sql.Connection 连接Java数据库和Java应用程序之间的主要对象并创建所有的Statement对象。 不管对数据库进行什么样的操作,都需要创建一个连接,然后通过这个连接来完成操作。
java.sql.Statement 语句对象,代表了一个特定的容器,对一个特定的数据库执行SQL语句。用于执行静态SQL语句并返回它所生成结果的对象。在默认情况下,同一时间每个 Statement 对象在只能打开一个ResultSet对象。因此,如果读取一个 ResultSet 对象与读取另一个交叉,则这两个对象必须是由不同的Statement对象生成的。如果存在某个语句的打开的当前ResultSet对象,则Statement接口中的所有执行方法都会隐式关闭它。
PreparedStatement 表示预编译的SQL语句的对象。SQL语句被预编译并存储在PreparedStatement对象中。然后可以使用此对象多次高效地执行该语句。
java.sql.ResultSet 用于控制对一个特定语句的行数据的存取,也就是数据库中记录或行组成的集合。

结果集及其常见方法

  1. 结果集有三种类型,它的类型决定了能否对结果集中的游标进行操作,以及并发的数据源的改变能否反映到结果集中。具体描述如下:
类型 描述
TYPE_FORWARD_ONLY 默认的结果集类型,这种类型的结果集对象的游标只能向前移动,从第1行的前面到最后一行的后面。
TYPE_SCROLL_INSENSITIVE 这种类型的结果集对象的游标可以向前移动,也可以直接定位到某一行上,但是对结果集中对应数据的变化是不敏感的。
TYPE_SCROLL_SENSITIVE 这种类型的结果集对象的游标可以向前移动,也可以直接定位到某一行上,并且对结果集中对应数据的变化是敏感的(如果)。

2. 结果集的并发性决定了结果集所支持的更新操作的层次,有两种并发性层次:

类型 描述
CONCUR_READ_ONL 默认的结果集并发类型,这种情况下的结果集对象不支持更新操作。
CONCUR_UPDATABLE 这种情况下的结果集对象支持更新操作。

可以通过调用DatabaseMetaData的supportsResultSetConcurrency方法来看驱动是否支持结果集上的更新操作,该方法定义如下:

boolean supportsResultSetConcurrency(int type,int concurrency) throws SQLException

第一个数据表示结果集类型,第二个参数表示并发类型。

  1. 结果集的延续性是指当事务提交时,在当前事务中创建的结果集是否关闭。默认情况下会关闭这个结果集对象。延续性可以通过如下两个静态属性来指定:
类型 描述
HOLD_CURSORS_OVER_COMMIT 当提交事务时不关闭该结果集对象。
CLOSE_CURSORS_AT_COMMIT 提交事务时关闭结果集对象,有时候会提高性能。

4. 结果集的类型、并发性和延续性可以通过Connection.createStatement、Connection.prepareStatement和Connection.prepareCall等方法指定,同时Statement、PreparedStatement和CallableStatement接口也提供了相应的setter方法和getter方法。如下代码是创建语句对象的时候指定结果集的类型、并发性和延续性。

Connection conn = ds.getConnection(user,passwd);
Statement stmt = conn.createStatement(ResultSet. TYPE_SCROLL_INSENSITIVE,
ResultSet. CONCUR_READ_ONLY, ResultSet. CLOSE_CURSORS_AT_COMMIT);
  1. 结果集常用的游标操作方法如下表所示:
返回值 方法名 功能描述
boolean next() 将游标从当前位置向前移一行,如果指向某行,返回true,如果指向最后一行的后面,返回false
boolean previous() 将游标从当前位置向后移一行,如果指向某行,返回true,如果指向第一行的前面,返回false
boolean first() 将游标移到此结果集的第一行。
boolean last() 将游标移到些结果集的最后一行。
void beforeFirst() 将游标移动到此结果集的开始处,正好位于第一行之前
void afterLast() 将游标移动到此结果集的末尾,正好位于最后一行之后
boolean relative(int rows) 按相对行数移动游标。如果参数为0,游标无变化。如果参数是正的,游标向前移动rows行,如果rows太大,游标指向最后1条记录的后面。如果参数是负数,游标向后移动rows行,如果rows太小,游标指向第1条记录的前面。如果游标指向一个有效行,方法返回true,否则返回false
boolean absolute(int row) 将游标移动到参数所指定的行

6. 以下是示例代码:

//获取结果集中列的类型  
ResultSet rs = stmt.executeQuery(sqlString);
ResultSetMetaData rsmd = rs.getMetaData();
int [] colType = new int[ rsmd.getColumnCount() ];
for(int idx=0,int col=1;idx<colType.length;idx++,col++)
colType[idx] = rsmd.getColumnType(col);
//如果结果集支持更新操作,可以通过结果集完成更新、删除和插入操作。
Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stmt.executeQuery(“select author from booklist where isbn=111”);
rs.next();
rs.updateString(“author”,”JerryChoi”);
rs.updateRow();
//插入记录
ResultSet rs = stmt.executeQuery(“select author,title,isbn from booklist”);
rs.moveToInsertRow();
//设置每一列的值
rs.updateString(1,”aaa”);
rs.updateString(2,”bbb”);
rs.updateLong(3,1234);
//插入一行
rs.insertRow();
rs.moveToCurrentRow();
//删除记录
rs.absolute(4);
rs.deleteRow();

连接数据库的基本过程

基本过程如下:

  • 建立数据源
    • 假设已经存在名为bookstore的MySQL数据库。
  • 导入用到的包
    • import java.sql.*;
  • 加载驱动程序
    • 代码
//如果驱动程序和名字是MyDriver,加载驱动程序的语句是:
Class.forName(“MyDriver”);
//或者
Class.forName(“MyDriver”).newInstance();
//本例中使用MySQL,刚加载的语句为:
Class.forName(“com.mysql.jdbc.Driver”);
  • 创建与数据库的连接
    • 连接数据库需要知道如下信息:
      • 数据库所在的主机,所使用的端口等
      • 数据库的名字
      • 用户信息,包括用户名和口令
    • 代码

//连接数据库的格式如下:
//constr:连接字符串;user:用户名;passwd:口令
Connection con = DriverManager.getConnection(constr,user,passwd);
//MySQL数据库的连接示例如下:
Connection con = DriverManager.getConnection(“jdbc:mysql://localhost:3306/bookstore”,”root”,”root”);

以下表格是常用的数据库驱动程序名字和URL格式:

这里写图片描述这里写图片描述这里写图片描述

  • 创建语句对象

    • 语句对象的作业就是执行SQL语句。语句对象的创建是通过连接对象创建的,所以在创建语句对象之前应该确保连接对象已经创建。以下代码是创建语句对象的代码,不管连接什么数据库,不管执行什么样的操作,这句代码不用改:
Statement stmt = con.createStatement();
  • 编写SQL语句
    • 示例代码如下:
String sql = “select * from usertable”;
  • 执行SQL语句
    • 常用的执行SQL语句的方法有以下两个:
返回值 方法 说明
ResultSet executeQuery(String sql) 主要用于执行有结果集返回的SQL语句,典型的就是select查询语句。
int executeUpdate(String sql) 主要用于执行没有结果集返回的SQL语句,如果要在数据库中添加、修改或删除信息,它们都没有返回值,应该使用本方法。该方法的返回值是一个整数,表示影响数据库中记录的个数。

例如,要执行上面的查询语句,可以使用如下代码:

ResultSet rs = stmt.executeQuery(sql);
  • 处理数据
    可参照上面的“结果集及其常见方法”
    注:对行的遍历,使用结果集的next()方法。要获取某一列,可以使用getXXX(int columnIndex)或getXXX(String columnLabel)方法。以下是示例代码:
//以字符串的形式获取第1列或字段名为username的列
String userid = rs.getString(1);
String userName = rs.getString(“username”);
  • 关闭相关对象
    代码如下:
//应该按以下顺序关闭相关对象,该顺序与创建对象的顺序相反
//关闭结果集
rs.close();
//关闭语句
stmt.close();
//关闭连接
con.close();
  • 处理异常

使用以下框架对异常进行处理:

try{
……//要执行的可能出错的代码
}catch(Exception e){
……//出错后的处理代码
}finally{
……//不管是否出错都要执行的代码
}

一个小的例子

/**
* Created by Phoebe on 15/12/29.
*/

String className = “com.mysql.jdbc.Driver”;
String url = “jdbc:mysql://localhost:3306/bookstore”;
String username = “root”;
String userpass = “root”;
String tableName = “usertable”
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
Class.forName(className);
con = DriverManager.getConnection(url,username,userpass);
pstmt = con.prepareStatement(“select*from “ +tableName);
rs = pstmt.executeQuery();
while(rs.next())
{
for(int i=0;i<rs.getMetaDate().getColumnCount();++i)
System.out.println(rs.getString(i+1));
System.out.println();
}
} catch(Exception e) {
System.out.println(e.toString);
} finally {
try {
rs.close();
} catch(Exception e) {

}
try {
pstmt.close();
}catch(Exception e) {

}
try {
con.close();
}catch(Exception e) {

}
}

最后附一段比较经典的代码吧:

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Enumeration;
import java.util.Vector;
public class ConnectionPool {
private String jdbcDriver = ""; // 数据库驱动
private String dbUrl = ""; // 数据 URL
private String dbUsername = ""; // 数据库用户名
private String dbPassword = ""; // 数据库用户密码
private String testTable = ""; // 测试连接是否可用的测试表名,默认没有测试表
private int initialConnections = 10; // 连接池的初始大小
private int incrementalConnections = 5;// 连接池自动增加的大小
private int maxConnections = 50; // 连接池最大的大小
private Vector connections = null; // 存放连接池中数据库连接的向量 , 初始时为 null

// 它中存放的对象为 PooledConnection 型

/**
* 构造函数
*
* @param jdbcDriver String JDBC 驱动类串
* @param dbUrl String 数据库 URL
* @param dbUsername String 连接数据库用户名
* @param dbPassword String 连接数据库用户的密码
*
*/


public ConnectionPool(String jdbcDriver,String dbUrl,String dbUsername,String dbPassword) {
this.jdbcDriver = jdbcDriver;
this.dbUrl = dbUrl;
this.dbUsername = dbUsername;
this.dbPassword = dbPassword;
}

/**

* 返回连接池的初始大小
*
* @return 初始连接池中可获得的连接数量
*/

public int getInitialConnections() {

return this.initialConnections;
}

/**

* 设置连接池的初始大小

*

* @param 用于设置初始连接池中连接的数量

*/


public void setInitialConnections(int initialConnections) {
this.initialConnections = initialConnections;
}

/**

* 返回连接池自动增加的大小 、
*
* @return 连接池自动增加的大小
*/

public int getIncrementalConnections() {

return this.incrementalConnections;

}

/**
* 设置连接池自动增加的大小
* @param 连接池自动增加的大小
*/


public void setIncrementalConnections(int incrementalConnections) {

this.incrementalConnections = incrementalConnections;

}

/**
* 返回连接池中最大的可用连接数量
* @return 连接池中最大的可用连接数量
*/


public int getMaxConnections() {
return this.maxConnections;
}

/**
* 设置连接池中最大可用的连接数量
*
* @param 设置连接池中最大可用的连接数量值
*/


public void setMaxConnections(int maxConnections) {

this.maxConnections = maxConnections;

}

/**

* 获取测试数据库表的名字
*
* @return 测试数据库表的名字
*/

public String getTestTable() {

return this.testTable;

}

/**
* 设置测试表的名字
* @param testTable String 测试表的名字
*/

public void setTestTable(String testTable) {
this.testTable = testTable;
}

/**

*
* 创建一个数据库连接池,连接池中的可用连接的数量采用类成员
* initialConnections 中设置的值
*/

public synchronized void createPool() throws Exception {

// 确保连接池没有创建

// 如果连接池己经创建了,保存连接的向量 connections 不会为空

if (connections != null) {

return; // 如果己经创建,则返回

}

// 实例化 JDBC Driver 中指定的驱动类实例

Driver driver = (Driver) (Class.forName(this.jdbcDriver).newInstance());

DriverManager.registerDriver(driver); // 注册 JDBC 驱动程序

// 创建保存连接的向量 , 初始时有 0 个元素

connections = new Vector();

// 根据 initialConnections 中设置的值,创建连接。

createConnections(this.initialConnections);

System.out.println(" 数据库连接池创建成功! ");

}

/**

* 创建由 numConnections 指定数目的数据库连接 , 并把这些连接

* 放入 connections 向量中
*
* @param numConnections 要创建的数据库连接的数目
*/

@SuppressWarnings("unchecked")
private void createConnections(int numConnections) throws SQLException {

// 循环创建指定数目的数据库连接

for (int x = 0; x < numConnections; x++) {

// 是否连接池中的数据库连接的数量己经达到最大?最大值由类成员 maxConnections

// 指出,如果 maxConnections 为 0 或负数,表示连接数量没有限制。

// 如果连接数己经达到最大,即退出。

if (this.maxConnections > 0 && this.connections.size() >= this.maxConnections) {

break;

}

//add a new PooledConnection object to connections vector

// 增加一个连接到连接池中(向量 connections 中)

try{

connections.addElement(new PooledConnection(newConnection()));

}catch(SQLException e){

System.out.println(" 创建数据库连接失败! "+e.getMessage());

throw new SQLException();

}

System.out.println(" 数据库连接己创建 ......");

}
}

/**

* 创建一个新的数据库连接并返回它
*
* @return 返回一个新创建的数据库连接
*/

private Connection newConnection() throws SQLException {

// 创建一个数据库连接

Connection conn = DriverManager.getConnection(dbUrl, dbUsername, dbPassword);

// 如果这是第一次创建数据库连接,即检查数据库,获得此数据库允许支持的

// 最大客户连接数目

//connections.size()==0 表示目前没有连接己被创建

if (connections.size() == 0) {

DatabaseMetaData metaData = conn.getMetaData();

int driverMaxConnections = metaData.getMaxConnections();

// 数据库返回的 driverMaxConnections 若为 0 ,表示此数据库没有最大

// 连接限制,或数据库的最大连接限制不知道

//driverMaxConnections 为返回的一个整数,表示此数据库允许客户连接的数目

// 如果连接池中设置的最大连接数量大于数据库允许的连接数目 , 则置连接池的最大

// 连接数目为数据库允许的最大数目

if (driverMaxConnections > 0 && this.maxConnections > driverMaxConnections) {

this.maxConnections = driverMaxConnections;

}
}
return conn; // 返回创建的新的数据库连接

}

/**

* 通过调用 getFreeConnection() 函数返回一个可用的数据库连接 ,

* 如果当前没有可用的数据库连接,并且更多的数据库连接不能创

* 建(如连接池大小的限制),此函数等待一会再尝试获取。

*

* @return 返回一个可用的数据库连接对象

*/


public synchronized Connection getConnection() throws SQLException {

// 确保连接池己被创建

if (connections == null) {

return null; // 连接池还没创建,则返回 null

}

Connection conn = getFreeConnection(); // 获得一个可用的数据库连接

// 如果目前没有可以使用的连接,即所有的连接都在使用中

while (conn == null){

// 等一会再试

wait(250);

conn = getFreeConnection(); // 重新再试,直到获得可用的连接,如果

//getFreeConnection() 返回的为 null

// 则表明创建一批连接后也不可获得可用连接

}

return conn;// 返回获得的可用的连接
}

/**

* 本函数从连接池向量 connections 中返回一个可用的的数据库连接,如果

* 当前没有可用的数据库连接,本函数则根据 incrementalConnections 设置

* 的值创建几个数据库连接,并放入连接池中。

* 如果创建后,所有的连接仍都在使用中,则返回 null

* @return 返回一个可用的数据库连接

*/


private Connection getFreeConnection() throws SQLException {

// 从连接池中获得一个可用的数据库连接

Connection conn = findFreeConnection();

if (conn == null) {

// 如果目前连接池中没有可用的连接

// 创建一些连接

createConnections(incrementalConnections);

// 重新从池中查找是否有可用连接

conn = findFreeConnection();

if (conn == null) {

// 如果创建连接后仍获得不到可用的连接,则返回 null

return null;

}

}

return conn;

}

/**

* 查找连接池中所有的连接,查找一个可用的数据库连接,

* 如果没有可用的连接,返回 null

*

* @return 返回一个可用的数据库连接

*/


private Connection findFreeConnection() throws SQLException {

Connection conn = null;

PooledConnection pConn = null;

// 获得连接池向量中所有的对象

Enumeration enumerate = connections.elements();

// 遍历所有的对象,看是否有可用的连接

while (enumerate.hasMoreElements()) {

pConn = (PooledConnection) enumerate.nextElement();

if (!pConn.isBusy()) {

// 如果此对象不忙,则获得它的数据库连接并把它设为忙

conn = pConn.getConnection();

pConn.setBusy(true);

// 测试此连接是否可用

if (!testConnection(conn)) {

// 如果此连接不可再用了,则创建一个新的连接,

// 并替换此不可用的连接对象,如果创建失败,返回 null

try{

conn = newConnection();

}catch(SQLException e){

System.out.println(" 创建数据库连接失败! "+e.getMessage());

return null;

}

pConn.setConnection(conn);

}

break; // 己经找到一个可用的连接,退出

}

}

return conn;// 返回找到到的可用连接

}

/**

* 测试一个连接是否可用,如果不可用,关掉它并返回 false

* 否则可用返回 true

*

* @param conn 需要测试的数据库连接

* @return 返回 true 表示此连接可用, false 表示不可用

*/


private boolean testConnection(Connection conn) {

try {

// 判断测试表是否存在

if (testTable.equals("")) {

// 如果测试表为空,试着使用此连接的 setAutoCommit() 方法

// 来判断连接否可用(此方法只在部分数据库可用,如果不可用 ,

// 抛出异常)。注意:使用测试表的方法更可靠

conn.setAutoCommit(true);

} else {// 有测试表的时候使用测试表测试

//check if this connection is valid

Statement stmt = conn.createStatement();

stmt.execute("select count(*) from " + testTable);

}

} catch (SQLException e) {

// 上面抛出异常,此连接己不可用,关闭它,并返回 false;

closeConnection(conn);

return false;

}

// 连接可用,返回 true

return true;

}

/**
* 此函数返回一个数据库连接到连接池中,并把此连接置为空闲。
* 所有使用连接池获得的数据库连接均应在不使用此连接时返回它。
*
* @param 需返回到连接池中的连接对象
*/


public void returnConnection(Connection conn) {

// 确保连接池存在,如果连接没有创建(不存在),直接返回

if (connections == null) {

System.out.println(" 连接池不存在,无法返回此连接到连接池中 !");

return;

}

PooledConnection pConn = null;

Enumeration enumerate = connections.elements();

// 遍历连接池中的所有连接,找到这个要返回的连接对象

while (enumerate.hasMoreElements()) {

pConn = (PooledConnection) enumerate.nextElement();

// 先找到连接池中的要返回的连接对象

if (conn == pConn.getConnection()) {

// 找到了 , 设置此连接为空闲状态

pConn.setBusy(false);

break;

}

}

}

/**

* 刷新连接池中所有的连接对象

*

*/


public synchronized void refreshConnections() throws SQLException {

// 确保连接池己创新存在

if (connections == null) {

System.out.println(" 连接池不存在,无法刷新 !");

return;

}

PooledConnection pConn = null;

Enumeration enumerate = connections.elements();

while (enumerate.hasMoreElements()) {

// 获得一个连接对象

pConn = (PooledConnection) enumerate.nextElement();

// 如果对象忙则等 5 秒 ,5 秒后直接刷新

if (pConn.isBusy()) {

wait(5000); // 等 5 秒

}

// 关闭此连接,用一个新的连接代替它。

closeConnection(pConn.getConnection());

pConn.setConnection(newConnection());

pConn.setBusy(false);

}

}

/**

* 关闭连接池中所有的连接,并清空连接池。

*/


public synchronized void closeConnectionPool() throws SQLException {

// 确保连接池存在,如果不存在,返回

if (connections == null) {

System.out.println(" 连接池不存在,无法关闭 !");

return;

}

PooledConnection pConn = null;

Enumeration enumerate = connections.elements();

while (enumerate.hasMoreElements()) {

pConn = (PooledConnection) enumerate.nextElement();

// 如果忙,等 5 秒

if (pConn.isBusy()) {

wait(5000); // 等 5 秒

}

//5 秒后直接关闭它

closeConnection(pConn.getConnection());

// 从连接池向量中删除它

connections.removeElement(pConn);

}

// 置连接池为空

connections = null;

}

/**
* 关闭一个数据库连接
*
* @param 需要关闭的数据库连接
*/


private void closeConnection(Connection conn) {

try {

conn.close();

}catch (SQLException e) {

System.out.println(" 关闭数据库连接出错: "+e.getMessage());

}

}

/**
* 使程序等待给定的毫秒数
*
* @param 给定的毫秒数
*/


private void wait(int mSeconds) {

try {

Thread.sleep(mSeconds);

} catch (InterruptedException e) {

}

}

/**

*

* 内部使用的用于保存连接池中连接对象的类

* 此类中有两个成员,一个是数据库的连接,另一个是指示此连接是否

* 正在使用的标志。

*/


class PooledConnection {

Connection connection = null;// 数据库连接

boolean busy = false; // 此连接是否正在使用的标志,默认没有正在使用

// 构造函数,根据一个 Connection 构告一个 PooledConnection 对象

public PooledConnection(Connection connection) {

this.connection = connection;

}

// 返回此对象中的连接

public Connection getConnection() {

return connection;

}

// 设置此对象的,连接

public void setConnection(Connection connection) {

this.connection = connection;

}

// 获得对象连接是否忙

public boolean isBusy() {

return busy;

}

// 设置对象的连接正在忙

public void setBusy(boolean busy) {

this.busy = busy;

}

}

}
/*
=======================================

这个例子是根据POSTGRESQL数据库写的,
请用的时候根据实际的数据库调整。

调用方法如下:

① ConnectionPool connPool
= new ConnectionPool("org.postgresql.Driver"
,"jdbc:postgresql://dbURI:5432/DBName"
,"postgre"
,"postgre");

② connPool .createPool();
  Connection conn = connPool .getConnection();
*/
智能推荐

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2019 ITdaan.com 粤ICP备14056181号  

赞助商广告