Posted on 2008-10-21 20:24
夢與橋 閱讀(2018)
評論(0) 編輯 收藏
1. JDBC驅動程序的類型
目前比較常見的JDBC驅動程序可分為以下四個種類:
(1)JDBC-ODBC橋加ODBC驅動程序
JavaSoft橋產品利用ODBC驅動程序提供JDBC訪問。注意,必須將ODBC二進制代碼(許多情況下還包括數據庫客戶機代碼)加載到使用該驅動程序的每個客戶機上。因此,這種類型的驅動程序最適合于企業網(這種網絡上客戶機的安裝不是主要問題),或者是用Java編寫的三層結構的應用程序服務器代碼。
(2)本地API
這種類型的驅動程序把客戶機API上的JDBC調用轉換為Oracle、Sybase、Informix、DB2或其它DBMS的調用。注意,象橋驅動程序一樣,這種類型的驅動程序要求將某些二進制代碼加載到每臺客戶機上。
(3)JDBC網絡純Java驅動程序
這種驅動程序將JDBC轉換為與DBMS無關的網絡協議,之后這種協議又被某個服務器轉換為一種DBMS協議。這種網絡服務器中間件能夠將它的純Java客戶機連接到多種不同的數據庫上。所用的具體協議取決于提供者。通常,這是最為靈活的JDBC驅動程序。有可能所有這種解決方案的提供者都提供適合于Intranet用的產品。為了使這些產品也支持Internet訪問,它們必須處理Web所提出的安全性、通過防火墻的訪問等方面的額外要求。幾家提供者正將JDBC驅動程序加到他們現有的數據庫中間件產品中。
(4)本地協議純Java驅動程序
這種類型的驅動程序將JDBC調用直接轉換為DBMS所使用的網絡協議。這將允許從客戶機機器上直接調用DBMS服務器,是Intranet訪問的一個很實用的解決方法。由于許多這樣的協議都是專用的,因此數據庫提供者自己將是主要來源,有幾家提供者已在著手做這件事了。
據專家預計第(3)、(4)類驅動程序將成為從JDBC訪問數據庫的首方法。第(1)、(2)類驅動程序在直接的純Java驅動程序還沒有上市前會作為過渡方案來使用。對第(1)、(2)類驅動程序可能會有一些變種,這些變種要求有連接器,但通常這些是更加不可取的解決方案。第(3)、(4)類驅動程序提供了Java的所有優點,包括自動安裝(例如,通過使用JDBC驅動程序的appletapplet來下載該驅動程序)。
2. JDBC編程的步驟
1)第一步:加載驅動程序
//jdbc-odbc驅動
try{
Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”);
}catch(ClassNotFoundException e){
e.printStackTrace();
}
加載其他各種不同驅動程序的方法如下:
//sqlserver
Class.forName(“com.microsoft.jdbc.sqlserver.SQLServerDriver”);
//mysql
Class.forName(“org.gjt.mm.mysql.Driver”);
//oracle
Class.forName(Oracle.jdbc.driver.OracleDriver);
//Informix
Class.forName(“com.informix.jdbc.IfxDriver”);
//Sybase
Class.forName(“com.sybase.jdbc2.jdbc.SybDriver”);
//AS400
Class.forName(“com.ibm.as400.access.AS400JDBCConnection”);
2)第二步:將“驅動程序”傳遞到DriverManager,然后獲得“連接”。
DriverManager類的getConnection(String rul,String user,String password)方法用于建立與某個數據庫的連接。每個JDBC驅動程序使用一個專門的JDBC URL作為自我標識的一種方法。
JDBC URL的格式為:jdbc:<子協議名>:<子名稱>,子協議與子名稱和JDBC驅動程序有關。
//建立JDBC-ODBC驅動的連接
try{
String url=”jdbc:odbc:myodbc”;
Connection con=DriverManager.getConnection(url);
//或者
//Connection con=DriverManager.getConnection(url,user,password);;
} catch(SQLException e){
e.printStackTrace();
}
以下是連接各種不同的數據庫的URL編寫方法:
//sqlserver
DriverManger.getConnection(“jdbc:Microsoft:sqlserver://主機:端口號;DatabaseName=數據庫名”,”用戶名”,”密碼”)
//mysql
DriverManger.getConnection(“jdbc:mysql://主機:端口號:數據庫名”,”用戶名”,”密碼”)
//oracle
DriverManger.getConnection(“jdbc:Oracle:thin:@主機:端口號:數據庫名”,”用戶名”,”密碼”)
//Informix
DriverManger.getConnection(“jdbc:informix-sqli://主機:端口號/數據庫名:INFORMIXSERVER=informix服務名”,”用戶名”,”密碼”)
//Sybase
DriverManger.getConnection(“jdbc:sybase:Tds:主機:端口號/數據庫名”,”用戶名”,””密碼)
//AS400
DriverManger.getConnection(“jdbc:as400://主機”,”用戶名”,”密碼”)
3)第三步:創建語句,Statement、PreparedStatement或CallableStatement,并將它們用于更新數據庫或執行查詢。
4)第四步:查詢并返回包含有已請求數據的ResultSet,該ResuletSet是按類檢索的。
5)顯示數據或根據得到的查詢結果完成業務邏輯處理。
6)第六步:最后關閉ResultSet(結果集)、Statement(語句對象)、Conection(數據庫連接)。
3. 數據庫各種連接方式實例分析
1)通過ODBC建立與數據庫的連接,創建數據源的過程略掉,數據源的名稱為myodbc,代碼:

<%
@ page language="java" contentType="text/html; charset=GBK"%>

<%
@ page import="java.sql.*" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>通過ODBC建立連接</title>
</head>
<body>

<%
Connection con = null;
try {
// 加載ODBC驅動
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
// 通過驅動管理器(DriverManager)獲得連接
con = DriverManager.getConnection("jdbc:odbc:myodbc",
"sa","");
// 如果連接不成功,就會出現異常,不會執行下面這個語句
out.println("<H1>");
out.println("通過ODBC數據源連接數據庫成功!");
out.println("</H1>");
} catch (Exception e) {// 如果出現異常,會打印堆棧里異常的信息
e.printStackTrace();
} finally {// 用完后,關閉連接,釋放資源
try {
if (con != null) // 防止出現內存泄露
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
%>
</body>
</html>
2)通過sqlserver提供驅動程序獲得連接,需要3個jar文件:msbase.jar、mssqlserver.jar、msutil.jar,設置class環境變量,或者配置到WEB中,jar文件部署,和所有的jar文件一樣,想在整個WEB服務器(我指Tomcat)中使用,就拷貝到Tomcat 6.0\lib目錄下,若在當前WEB應用中使用,拷貝到WEB應用的WEB-INF\lib目錄下。代碼:

<%
@ page language="java" contentType="text/html; charset=GBK"%>

<%
@ page import="java.sql.*" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>通過Sqlserver純驅動程序建立連接</title>
</head>
<body>

<%
Connection con = null;
try {
// 加載SQLSERVER的驅動程序
Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
// 通過驅動來獲得一個連接
con = DriverManager.getConnection(
"jdbc:microsoft:sqlserver://localhost:1433;"
+ "databasename=pubs", "sa", "");

// 如果連接不成功,就會出現異常,不會執行下面這個語句
out.println("<H1>");
out.println("通過SQLServer純驅動程序連接數據庫成功!<br> con="+con);
out.println("</H1>");
} catch (Exception e) {// 如果出現異常,會打印堆棧里異常的信息
e.printStackTrace();
} finally {// 用完后,關閉連接,釋放資源
try {
if (con != null) // 防止出現內存泄露
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
%>
</body>
</html>

3)通過Oracle提供驅動程序獲得連接,需要用到一個classes12.jar文件,配置同上。代碼:

<%
@ page language="java" contentType="text/html; charset=GBK"%>

<%
@ page import="java.sql.*" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>通過oracle純驅動程序建立連接</title>
</head>
<body>

<%
Connection con = null;
try {
// 加載ORACLE9i的驅動程序
Class.forName("oracle.jdbc.driver.OracleDriver");
// 獲得連接 oracle數據庫的端口號:1521 數據服務器的名字叫ora921
// 登陸的用戶名為system,密碼為:itjob (默認密碼為manager)
con = DriverManager.getConnection(
"jdbc:oracle:thin:@127.0.0.1:1521:ora921",
"system","itjob");
// 如果連接不成功,就會出現異常,不會執行下面這個語句
out.println("<H1>");
out.println("通過ora921純驅動程序連接數據庫成功!<br> con="+con);
out.println("</H1>");
} catch (Exception e) {// 如果出現異常,會打印堆棧里異常的信息
e.printStackTrace();
} finally {// 用完后,關閉連接,釋放資源
try {
if (con != null) // 防止出現內存泄露
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
%>
</body>
</html>
4)通過數據庫連接池獲得連接,連接池將通過連接的重用,降低開銷。
以oracle數據庫為例,配置Tomcat6.0的連接池。
§將classes12.jar拷到Tomcat 6.0\lib目錄下,并修改Tomcat 6.0\conf目錄下的context.xml文件,在文件的</Context>標記前面加上如下代碼:
<WatchedResource>WEB-INF/confweb.xml</WatchedResource>
<Resource name="jdbc/oracleds" auth="Container" type="javax.sql.DataSource" maxActive="100"
maxIdle="30" maxWait="10000" username="scott" password="tiger" driverClassName="oracle.jdbc.driver.OracleDriver"
url="jdbc:oracle:thin:@127.0.0.1:1521:wzz"/>
保存并重啟Tomcat。
§編輯jsp文件pool_connection.jsp,代碼如下:

<%
@ page language="java" contentType="text/html; charset=GBK"%>

<%
@ page import="java.sql.*,javax.naming.*,javax.sql.*" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>通過數據庫連接池建立連接</title>
</head>
<body>

<%
Connection con = null;
try {
Context context =new InitialContext();
DataSource ds=(DataSource)context.lookup("java:/comp/env/jdbc/oracleds");
con=ds.getConnection();
out.println("<H1>");
out.println("第一次通過數據庫連接池連接數據庫成功!<br> con="+con);
out.println("</H1>");
//con.close();
con = ds.getConnection();
out.println("<H1>");
out.println("第二次通過數據庫連接池連接數據庫成功!<br> con="+con);
out.println("</H1>");
con.close();
} catch (Exception e) {// 如果出現異常,會打印堆棧里異常的信息
e.printStackTrace();
}
%>
</body>
</html>
其中連接數據庫的代碼:
Context context =new InitialContext();
DataSource ds=(DataSource)context.lookup("java:/comp/env/jdbc/oracleds");
con=ds.getConnection();
可以修改為如下代碼:
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/oracleds");
con = ds.getConnection();
3. 使用DDL和DML語言對數據進行基本操作
1)創建表并插入、修改數據,代碼如下:

<%
@ page language="java" contentType="text/html; charset=GBK"%>

<%
@ page import="java.sql.*,javax.naming.*,javax.sql.*" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>創建表并插入及修改數據</title>
</head>
<body>

<%
Connection con = null;
try {
// 通過連接池來獲得一個連接
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/sqlserver");
con = ds.getConnection();
// 創建語句對象
Statement st = con.createStatement();
// 創建表的SQL語句
String sql = "create table student(id int,name char(30),age int)";
// 執行完SQL語句的結果
boolean b = st.execute(sql);
out.println("<h2>創建student表成功!</h2><br>");
// 插入數據到student表
sql = "insert into student values(1,'andy',47)"
+ "insert into student values(2,'jacky',53)"
+ "insert into student values(3,'周潤發',51)"
+ "insert into student values(4,'謝賢',60)";
// 執行完SQL語句的結果
b = st.execute(sql);
out.println("<h2>插入數據成功!</h2><br>");

// 更新表數據
sql = "update student set name='劉德華' where id=1";
int rows = st.executeUpdate(sql);

// 如果更新成功,rows肯定是大于1的值
if (rows > 0)
out.println("<h2>修改數據成功!</h2><br>");
else
out.println("<h2>修改數據失敗!</h2><br>");

} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (con != null)
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}

%>
</body>
</html>
2)查詢數據,代碼如下:

<%
@ page language="java" contentType="text/html; charset=GBK"%>

<%
@ page import="java.sql.*,javax.naming.*,javax.sql.*" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>查詢數據庫數據</title>
</head>
<body>

<%
Connection con = null;
try {
// 通過連接池來獲得一個連接
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/sqlserver");
con = ds.getConnection();
Statement st = con.createStatement();
String query = "select id,name from student";

// 獲得一個結果集
ResultSet rs = st.executeQuery(query);
// 獲得結果集的元數據(表及相關的信息)
ResultSetMetaData rsmt = rs.getMetaData();
// 得到結果集有幾列
int num = rsmt.getColumnCount();
String[] columns = new String[num];
// 列的序號是從1開始的
for (int i = 0; i < num; i++)
columns[i] = rsmt.getColumnName(i + 1);
out.println("<table width='600' border='1'>");
out.println("<tr>");
// 先輸出列名
for (int i = 0; i < num; i++)
out.print("<td>"+columns[i] + "</td>");
// 輸出列名之后換行
out.println("</tr>");
// 取出結果
while (rs.next()) {
// 輸出每一行的值
out.println("<tr>");
for (int i = 1; i <= num; i++) {
String temp = rs.getString(i);
out.print("<td>"+temp + "</td>");
}
out.println("</tr>");
}
out.println("</table>");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 用完后要關閉連接,釋放資源
if (con != null)
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
%>
</body>
</html>
3)使用預編譯語句,代碼如下:

<%
@ page language="java" contentType="text/html; charset=GBK"%>

<%
@ page import="java.sql.*,javax.naming.*,javax.sql.*" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>預編譯的SQL語句</title>
</head>
<body>

<%
Connection con = null;
try {
// 通過連接池來獲得一個連接
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/sqlserver");
con = ds.getConnection();
// 創建修改表的PrepareStatement SQL語句
String sql = "update student set name=? where id=?";
// 創建預編譯語句對象
PreparedStatement pst = con.prepareStatement(sql);
String[] names = new String[] { "梁朝偉", "貝殼汗母", "小羅", "霍元甲" };
for (int i = 0; i < names.length; i++) {
pst.setString(1, names[i]);
pst.setInt(2, i + 1);
pst.executeUpdate();
}
pst.close();
// 打印執行完SQL語句的結果
out.println("<h1>修改后的數據庫結果</h1>");
Statement st = con.createStatement();
String query = "select id,name from student";

// 獲得一個結果集
ResultSet rs = st.executeQuery(query);
// 獲得結果集的元數據(表及相關的信息)
ResultSetMetaData rsmt = rs.getMetaData();
// 得到結果集有幾列
int num = rsmt.getColumnCount();
String[] columns = new String[num];
// 列的序號是從1開始的
for (int i = 0; i < num; i++)
columns[i] = rsmt.getColumnName(i + 1);
out.println("<table width='600' border='1'>");
out.println("<tr>");
// 先輸出列名
for (int i = 0; i < num; i++)
out.print("<td>"+columns[i] + "</td>");
// 輸出列名之后換行
out.println("</tr>");
// 取出結果
while (rs.next()) {
// 輸出每一行的值
out.println("<tr>");
for (int i = 1; i <= num; i++) {
String temp = rs.getString(i);
out.print("<td>"+temp + "</td>");
}
out.println("</tr>");
}
out.println("</table>");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 用完后要關閉連接,釋放資源
if (con != null)
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
%>
</body>
</html>
4)使用存儲過程。存儲過程是用來封裝一段SQL語句完成一個完整的業務功能,這樣可以帶來更快的性能和改進的安全性。存儲過程可以支持3種類型的參數:IN、OUT、INOUT,這對于存儲過程在數據庫內部真正能做什么來說,帶來了很大的靈活性。不管存儲過程是用什么語言編寫的,它都能以一種標準的方式從java應用程序調用。首先需要創建一個CallableStatement對象,允許三種類型的調用,調用存儲過程StudentList為例:
§|call StudentList|:如果過程不需要參數。
§|call StudentList(?,?)|:如果過程需要兩個參數。
§|? =call StudentList(?,?)|:如果參數需要兩個參數并返回一個。
首先要創建存儲過程,代碼如下:

<%
@ page language="java" contentType="text/html; charset=GBK"%>

<%
@ page import="java.sql.*,javax.naming.*,javax.sql.*" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>創建SQLServer存儲過程</title>
</head>
<body>

<%
Connection con = null;
try {
// 通過連接池來獲得一個連接
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/sqlserver");
con = ds.getConnection();
Statement stmt = con.createStatement();
// 1.創建存儲過程show_students
String createProcedure1 = "create procedure show_students " + "as " + "select id, name,age " + "from student " + "order by id";
// 刪除數據庫中存在的同名過程
stmt.executeUpdate("if exists(select name from sysobjects "
+ "where name='show_students'and type='p') "
+ "drop procedure show_students");
stmt.executeUpdate(createProcedure1);
out.println("<h1>第一個存儲過程show_students創建成功</h1><br>");

// 2.創建儲存過程onestudent
String createProcedure2 = "create procedure onestudent "
+ "@stu_id int = null, " + "@name varchar(20) output, "
+ "@age int output " + "as " + "if @stu_id = null "
+ "BEGIN "
+ " PRINT 'ERROR: You must specify a stu_id value.' "
+ " RETURN "
+ "END "
+
// Get the sales for the specified cof_name and " +
// assign it to the output parameter. " +
"SELECT @name = name, @age = age " + "FROM student "
+ "WHERE id = @stu_id " + "RETURN ";
stmt.executeUpdate("if exists(select name from sysobjects "
+ "where name='onestudent'and type='p') "
+ "drop procedure onestudent");
stmt.executeUpdate(createProcedure2);
out.println("<h1>第二個存儲過程onestudent創建成功</h1><br>");

// 3.創建函數
String createProcedure3 = "CREATE FUNCTION ageofstu "
+
// Input cof_name
"(@stu_name varchar(20)) "
+ "RETURNS int "
+ // return sales
"AS " + "BEGIN " + " DECLARE @age int "
+ " SELECT @age = age " + " FROM student "
+ " WHERE name like @stu_name " + " RETURN @age "
+ "END ";
stmt.executeUpdate("if exists(select name from sysobjects "
+ "where name='ageofstu') "
+ "drop function ageofstu");
stmt.executeUpdate(createProcedure3);
out.println("<h1>函數ageofstu創建成功</h1><br>");
stmt.close();
//con.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 用完后要關閉連接,釋放資源
if (con != null)
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
%>
</body>
</html>
調用存儲過程:

<%
@ page language="java" contentType="text/html; charset=GBK"%>

<%
@ page import="java.sql.*,javax.naming.*,javax.sql.*" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>調用SQLServer數據庫存儲過程</title>
</head>
<body>

<%
Connection con = null;
// 定義調用存儲過程和函數的 SQL 語句
String callSQL1 = "{call show_students}";
String callSQL2 = "{call onestudent(?,?,?)}";
String callSQL3 = "{? = call ageofstu(?)}";
try {
// 通過連接池來獲得一個連接
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/sqlserver");
con = ds.getConnection();
// 調用第 1 個存儲過程
CallableStatement cs = con.prepareCall(callSQL1);
ResultSet rs = cs.executeQuery();
out.println("<h1>第一個存儲過程調用結果</h1><br>");
while (rs.next()) {
String id = rs.getString(1);
String name = rs.getString(2);
String age = rs.getString(3);
out.println(id + " " + name + " " + age+"<br>");
}
// 調用第 2 個存儲過程
cs = con.prepareCall(callSQL2);
cs.setString(1, "2");
cs.registerOutParameter(2, Types.CHAR);
cs.registerOutParameter(3, Types.INTEGER);
cs.execute();
String name = cs.getString(2);
int age = cs.getInt(3);
out.println("<h1>第二個存儲過程調用結果</h1><br>");
out.println("This student's name is " + name
+ " and age is " + age+"<br>");
// 調用函數
cs = con.prepareCall(callSQL3);
cs.setString(2, "小羅");
cs.registerOutParameter(1, Types.INTEGER);
cs.execute();
age = cs.getInt(1);
out.println("<h1>函數調用結果</h1><br>");
out.println("This student's name is " + age + ".<br>");
cs.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 用完后要關閉連接,釋放資源
if (con != null)
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
%>
</body>
</html>
注意:在調用存儲過程或函數需要使用返回參數時,須使用Call-ableStatement的registerOutParameter(序號,類型)對返回參數進行注冊。其中序號是調用語句從左至右順序號,且起始位置從1計數開始。