[Servlet&JSP] 使用JDBC連線資料庫
JDBC(Java DataBase Connectivity)時用於執行SQL的解決方案,開發人員使用JDBC的標準介面,資料庫廠商則對介面進行實現,這樣開發人員就可以無需接觸底層資料庫驅動程式的差異性。
JDBC簡介
廠商在實現JDBC驅動程式時,依方式可將驅動程式分為四種類型:
- JDBC-ODBC Bridge Driver
將JDBC呼叫轉換為ODBC呼叫 - Native API Driver
將JDBC呼叫轉換為原生連結庫中的相關API呼叫。特點:與平臺相依,是四種類型中最快的驅動程式。 - JDBC-Net Driver
將JDBC呼叫轉換為特定的網路協議呼叫,目的是與遠端資料庫特定的中間伺服器或元件進行協議操作,而中介軟體或組建再真正與資料庫進行操作。特點:彈性最高。 - Native Protocol Driver
主要作用是將JDBC呼叫轉換為特定的網路協議,所以驅動驅動程式可以使用Java技術來實現,遺傳你這型別的驅動程式可以跨平臺,在效能上也有不錯的表現。在不需要如第3種驅動型別的靈活性時,通常是會使用這型別驅動程式,該型別時最常見的驅動程式型別。
以下編寫一個簡單的JavaBean來測試可否連線資料庫:
package club.chuxing;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class DbBean {
private String url;
private String username;
private String password;
public DbBean(){
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
Logger.getLogger(DbBean.class.getName()).log(Level.SEVERE, null , e);
}
}
public boolean isConnectedOk() {
boolean ok = false;
Connection conn = null;
try {
conn = DriverManager.getConnection(url, username, password);
if (!conn.isClosed()) {
ok = true;
}
} catch (SQLException e) {
Logger.getLogger(DbBean.class.getName()).log(Level.SEVERE, null, e);
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
Logger.getLogger(DbBean.class.getName())
.log(Level.SEVERE, null, e);
}
}
}
return ok;
}
public void setPassword(String password) {
this.password = password;
}
public void setUrl(String url) {
this.url = url;
}
public void setName(String username) {
this.username = username;
}
}
之後就可以通過呼叫isConnectedOK()方法來看看是否可以連線成功。例如,可以寫個簡單的JSP網頁,程式碼如下:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<jsp:useBean id="db" class="club.chuxing.DbBean" />
<c:set target="${db}" property="url" value="jdbc:mysql://localhost:3306/test" />
<c:set target="${db}" property="username" value="root" />
<c:set target="${db}" property="password" value="123" />
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Test Database Connection</title>
</head>
<body>
<c:choose>
<c:when test="${db.connectedOK}">連線成功</c:when>
<%-- <c:when test="<%db.isConnectedOK();%>">連線成功</c:when> --%>
<c:otherwise>連線失敗</c:otherwise>
</c:choose>
</body>
</html>
使用DataSource取得連線
在Java EE中的環境中,將取得連線等與資料庫來源相關的行為規範在javax.sql.DataSource
介面,實際如何取得Connection則由實現介面的物件來負責。
為了讓應用程式在需要取得某些與系統相關的資源物件時,能與實際的系統配置、實體機器位置、環境架構等無關,在Java應用程式中可以通過JNDI(Java Naming Directory Interface)來取得所需的資源物件。可用如下方式取得DataSource例項:
try {
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
dataSource = (DataSource)envContext.lookup("jdbc/test");
} catch(NamingException ex) {
//other process
}
在建立Context物件的過程中會收集環境的相關資料,之後根據JNDI名稱jdbc/demo向JNDI伺服器查詢DataSource例項並返回。在這個程式片段中,不會知道實際的資源配置、實體機器位置、環境架構等資訊,應用程式不會與這些相依。
之前的DbBean類可以改為如下編寫方式:
package club.chuxing;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
public class DatabaseBean {
private DataSource dataSource;
public DatabaseBean(){
try {
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
dataSource = (DataSource)envContext.lookup("jdbc/test");
} catch(NamingException ex) {
Logger.getLogger(DatabaseBean.class.getName())
.log(Level.SEVERE, null, ex);
}
}
public boolean isConnectedOK(){
boolean ok = false;
Connection conn = null;
try {
conn = dataSource.getConnection();
if (!conn.isClosed()) {
ok = true;
}
} catch(SQLException ex) {
Logger.getLogger(DatabaseBean.class.getName())
.log(Level.SEVERE, null, ex);
} finally {
if (conn != null) {
try {
conn.close();
} catch(SQLException ex) {
Logger.getLogger(DatabaseBean.class.getName())
.log(Level.SEVERE, null, ex);
}
}
}
return ok;
}
}
以上程式碼並不含有驅動程式、資料庫使用者名稱、密碼等資訊,這些都由資料庫管理人員或伺服器管理人員負責設定,我們唯一需要知道的就是jdbc/test這個JNDI名稱,並且告訴web容器,也就是要在web.xml中設定。
web.xml:
<web-app ...>
<resource-ref>
<res-ref-name>jdbc/demo</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
</web-app>
在web.xml中設定的目的,就是讓web容器提供JNDI查詢時所需的相關環境資訊,這樣建立Context物件時就不用設定一大堆引數了。接著可以編寫一個簡單的JSP來使用DatabaseBean。
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<jsp:useBean id="db" class="club.chuxing.DatabaseBean" />
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Test Database Connection</title>
</head>
<body>
<c:choose>
<c:when test="${db.connectedOK}">連線成功</c:when>
<%-- <c:when test="<%= db.isConnectedOK()%>">連線成功</c:when> --%>
<c:otherwise>連線失敗</c:otherwise>
</c:choose>
</body>
</html>
對於Java開發人員來說,它的工作已經完成了。現在假設你是伺服器管理員,職責就是設定JNDI相關資源,但設定的方式並非標準的一部分,而是依應用程式伺服器而有所不同。假設應用程式將部署在Tomcat6上,則可以要求web應用程式在封裝為war檔案時,必須在META-INF資料夾中包括一個context.xml檔案。例如:
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/JDBCDemo">
<Resource name="jdbc/demo" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="1000" username="root" password="123"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8"/>
</Context>
name屬性用來設定JNDI名稱,username與password用來設定資料庫使用者名稱和密碼,driverClassName用來設定驅動程式類名稱,url用來設定JDBC URL。其他屬性則是與DBCP(Database Connection Pool)有關的,這是內建在Tomcat中的連線池機制。
當應用程式部署後,Tomcat會根據META-INF中context.xml的設定,尋找指定的驅動程式,所以必須將驅動程式的JAR檔案放置在Tomcat的lib目錄中,接著Tomcat就會為JNDI名稱jdbc/demo設定相關的資源。
使用root來操作資料庫是不安全的,root賬號用於操作資料庫的完整許可權,應用設定具有適當許可權的使用者來操作資料庫。