JNDI是 Java 命名與目錄接口(Java Naming and Directory Interface),在J2EE規範中是重要的規範之一,不少專家認爲,沒有透徹理解JNDI的意義和作用,就沒有真正掌握J2EE特別是EJB的知識。
那麼,JNDI到底起什麼作用?
要了解JNDI的作用,我們可以從“如果不用JNDI我們怎樣做?用了JNDI後我們又將怎樣做?”這個問題來探討。
沒有JNDI的做法:
程序員開發時,知道要開發訪問MySQL數據庫的應用,於是將一個對 MySQL JDBC 驅動程序類的引用進行了編碼,並通過使用適當的 JDBC URL 連接到數據庫。
就像以下代碼這樣:
Connection conn=null;
try {
Class.forName("com.mysql.jdbc.Driver",
true, Thread.currentThread().getContextClassLoader());
conn=DriverManager.getConnection("jdbc:mysql://MyDBServer?user=qingfeng&password=mingyue");
/* 使用conn並進行SQL操作 */
......
conn.close();
}
catch(Exception e) {
e.printStackTrace();
}
finally {
if(conn!=null) {
try {
conn.close();
} catch(SQLException e) {}
}
}
這是傳統的做法,也是以前非Java程序員(如Delphi、VB等)常見的做法。這種做法一般在小規模的開發過程中不會產生問題,只要程序員熟悉Java語言、瞭解JDBC技術和MySQL,可以很快開發出相應的應用程序。
沒有JNDI的做法存在的問題:
1、數據庫服務器名稱
MyDBServer 、用戶名和口令都可能需要改變,由此引發JDBC URL需要修改;
2、數據庫可能改用別的產品,如改用DB2或者Oracle,引發JDBC驅動程序包和類名需要修改;
3、隨着實際使用終端的增加,原配置的連接池參數可能需要調整;
4、......
解決辦法:
程序員應該不需要關心“具體的數據庫後臺是什麼?JDBC驅動程序是什麼?JDBC URL格式是什麼?訪問數據庫的用戶名和口令是什麼?”等等這些問題,程序員編寫的程序應該沒有對 JDBC 驅動程序的引用,沒有服務器名稱,沒有用戶名稱或口令 —— 甚至沒有數據庫池或連接管理。而是把這些問題交給J2EE容器來配置和管理,程序員只需要對這些配置和管理進行引用即可。
由此,就有了JNDI。
用了JNDI之後的做法:
首先,在在J2EE容器中配置JNDI參數,定義一個數據源,也就是JDBC引用參數,給這個數據源設置一個名稱;然後,在程序中,通過數據源名稱引用數據源從而訪問後臺數據庫。
具體操作如下(以JBoss爲例):
1、配置數據源
在JBoss的 D:/jboss420GA/docs/examples/jca 文件夾下面,有很多不同數據庫引用的數據源定義模板。將其中的 mysql-ds.xml 文件Copy到你使用的服務器下,如 D:/jboss420GA/server/default/deploy。
修改 mysql-ds.xml 文件的內容,使之能通過JDBC正確訪問你的MySQL數據庫,如下:
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
<jndi-name>MySqlDS</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/lw</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>root</user-name>
<password>rootpassword</password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
<metadata>
<type-mapping>mySQL</type-mapping>
</metadata>
</local-tx-datasource>
</datasources>
這裏,定義了一個名爲MySqlDS的數據源,其參數包括JDBC的URL,驅動類名,用戶名及密碼等。
2、在程序中引用數據源:
Connection conn=null;
try {
Context ctx=new InitialContext();
Object datasourceRef=ctx.lookup("java:MySqlDS"); //引用數據源
DataSource ds=(Datasource)datasourceRef;
conn=ds.getConnection();
/* 使用conn進行數據庫SQL操作 */
......
c.close();
}
catch(Exception e) {
e.printStackTrace();
}
finally {
if(conn!=null) {
try {
conn.close();
} catch(SQLException e) { }
}
}
直接使用JDBC或者通過JNDI引用數據源的編程代碼量相差無幾,但是現在的程序可以不用關心具體JDBC參數了。
在系統部署後,如果數據庫的相關參數變更,只需要重新配置 mysql-ds.xml 修改其中的JDBC參數,只要保證數據源的名稱不變,那麼程序源代碼就無需修改。
由此可見,JNDI避免了程序與數據庫之間的緊耦合,使應用更加易於配置、易於部署。
JNDI的擴展:
JNDI在滿足了數據源配置的要求的基礎上,還進一步擴充了作用:所有與系統外部的資源的引用,都可以通過JNDI定義和引用。
所以,在J2EE規範中,J2EE 中的資源並不侷限於 JDBC 數據源。引用的類型有很多,其中包括資源引用(已經討論過)、環境實體和 EJB 引用。特別是 EJB 引用,它暴露了 JNDI 在 J2EE 中的另外一項關鍵角色:查找其他應用程序組件。
EJB 的 JNDI 引用非常類似於 JDBC 資源的引用。在服務趨於轉換的環境中,這是一種很有效的方法。可以對應用程序架構中所得到的所有組件進行這類配置管理,從 EJB 組件到 JMS 隊列和主題,再到簡單配置字符串或其他對象,這可以降低隨時間的推移服務變更所產生的維護成本,同時還可以簡化部署,減少集成工作。 外部資源”。
總結:
J2EE 規範要求所有 J2EE 容器都要提供 JNDI 規範的實現。JNDI 在 J2EE 中的角色就是“交換機” —— J2EE 組件在運行時間接地查找其他組件、資源或服務的通用機制。在多數情況下,提供 JNDI 供應者的容器可以充當有限的數據存儲,這樣管理員就可以設置應用程序的執行屬性,並讓其他應用程序引用這些屬性(Java 管理擴展(Java Management Extensions,JMX)也可以用作這個目的)。JNDI 在 J2EE 應用程序中的主要角色就是提供間接層,這樣組件就可以發現所需要的資源,而不用瞭解這些間接性。
在 J2EE 中,JNDI 是把 J2EE 應用程序合在一起的粘合劑,JNDI 提供的間接尋址允許跨企業交付可伸縮的、功能強大且很靈活的應用程序。這是 J2EE 的承諾,而且經過一些計劃和預先考慮,這個承諾是完全可以實現的
Tomcat6.0連接池配置1
1.配置tomcat下的conf下的context.xml文件,在之間添加連接池配置: <Resource name="jdbc/oracle"
auth="Container"
type="javax.sql.DataSource"
driverClassName="oracle.jdbc.driver.OracleDriver"
url=" jdbc:oracle:thin:@host:port:databse"
username=" user "
password="password"
maxActive="100"
maxIdle="30"
maxWait="10000" />
2.配置你的應用下的web.xml中的之間加入:
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/oracle</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
3.把連接數據庫的第三方驅動放到common/lib下面就ok了
Tomcat6.0 連接池的配置2
配置步驟如下:1.Tomcat 6的配置和以前的不同了,不推薦在server.xml中進行配置,而是在%Tomcat_Home%\webapps\yourApp\META-INF \context.xml中進行配置纔是更好的方法。而不是以前版本%Tomcat_Home%\conf下的context.xml文件。這樣就可以在不同的web應用下單獨配置連接池了,且Tomcat會自動重載。當然你也可以更改%Tomcat_Home%\conf下的context.xml文件,將所有web應用下的連接池進行統一配置。
2.將代碼修改如下:
view plaincopy to clipboardprint?
<Context reloadable="true">
<WatchedResource>WEB-INF/web.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:@localhost:1521:ora9"/>
</Context>
<Context reloadable="true">
<WatchedResource>WEB-INF/web.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:@localhost:1521:ora9"/>
</Context>
name 爲當前數據源JNDI的名字,可以隨意設定;
auth 爲驗證方式;
type 資源類型;
driverClassName 爲Oracle驅動引用;
maxActiv 爲連接池最大激活的連接數,設爲0表示無限制;
maxIdle 爲連接池最大空閒的連接數,數據庫連接的最大空閒時間。超過空閒時間,
數據庫連接將被標記爲不可用,然後被釋放。設爲0表示無限制;
maxWait 爲連接最大的等待時間,單位毫秒,如果超過此時間將接到異常。設爲-1
表示無限制。;
username 爲oracle數據庫的一個用戶名;
password 爲username的密碼;
url 爲連接oracle的連接地址;
注:本人嘗試將代碼“driverClassName="oracle.jdbc.driver.OracleDriver"”改爲“driverClassName="oracle.jdbc.OracleDriver"”程序依然運行正常,剛開始以爲老師的代碼有問題
3.在程序中的調用形式爲:
view plaincopy to clipboardprint?
Context context = new InitialContext();
DataSource ds = (DataSource)context.lookup("java:/comp/env/jdbc/oracleds");
Connection conn = ds.getConnection();
Context context = new InitialContext();
DataSource ds = (DataSource)context.lookup("java:/comp/env/jdbc/oracleds");
Connection conn = ds.getConnection();
注:“java:/comp/env/jdbc/oracleds”紅色標記文字爲步驟1裏設置的Resource name
則可以將建立connection的方式以上述形式取代傳統方式:
view plaincopy to clipboardprint?
String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@localhost:1521:ora9";
String username = "scott";
String password = "tiger";
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, username, password);
String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@localhost:1521:ora9";
String username = "scott";
String password = "tiger";
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, username, password);
4.另外還需將用到的jdbc驅動類庫導入到%Tomcat_Home%\lib目錄下
否則會拋出如下異常:
org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot load JDBC driver class 'oracle.jdbc.driver.OracleDriver'
依上述步驟就能成功的配置Tomcat6.0 連接池,還有網友貼文說需
在web.xml文件中的web-app節點下加入如下代碼形式:
<resource-ref>
<res-ref-name>jdbc/myoracle</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
</resource-ref>
因本人未添加此項,程序依然正確,故本人認爲此步驟爲非必要項
今天需要在另一臺機器上重新部署系統,重新設置db的連接池。當我把tomcat拷貝到那臺機器,並且修改META-INF\context.xml,然後重新啓動tomcat,但發現系統連接的仍然是老的db。再次檢查了META-INF\context.xml文件,確信了這個文件已經正確設置db連接了,這也說明這個文件並沒有真正起作用。查看了tomcat下conf\context.xml也沒有設置db的連接池。那是哪個文件在起作用呢?折騰了辦法,後來發現tomcat在conf\Catalina\localhost下生成了一個和原來META-INF\context.xml相同內容的文件,懷疑一定是這個文件在起作用?刪除conf\Catalina\localhost目錄,重啓tomcat,問題消失。
Tomcat6.0 連接池的配置3
不管是tomcat5,Tomcat5.5或Tomcat6.0. 用下面的這兩中方法配置連接池,都可以成功.
1. 應該算是全局的連接池的配置
(1).不管是tomcat5 還是tomcat6 ,都首先找到Tomcat目錄下面的conf目錄中的server.xml文件.
找到<GlobalNamingResources> </GlobalNamingResources>這對標籤.
將這樣的一段配置加到標籤中間.
<Resource
name="jdbc/TestDB"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/test?autoReconnect=true"
maxActive="50"
maxldle="10"
maxWait="5000"
username="root"
password="admin" />
當然,樣例是使用 MYSQL配置, 根據不同的需要,將url,driverClassName,username,passsword等參數改變就行.
然後再找到和server.xml同目錄下面的context.xml文件.
在<Context></Context>標籤中添加如下配置.
<ResourceLink global="jdbc/TestDB" name="jdbc/TestDB" type="javax.sql.DataSource"/>
global="jdbc/TestDB" 中的參數值("jdbc/TestDB")必須和上一段<Resource >配置中的name屬性的值保持一樣.
name="jdbc/TestDB" 這個可以隨便取,但是在程序中調用的時候,就應該與name的值保持一致.
到這裏,連接池已經配置好啦
寫個jsp測試
<%@ page language="java" pageEncoding="gbk"%>
<%@page import="java.sql.Connection"%>
<%@page import="javax.naming.Context"%>
<%@page import="javax.naming.InitialContext"%>
<%@page import="javax.sql.DataSource"%>
<%@page import="java.sql.Statement"%>
<%@page import="java.sql.ResultSet"%>
<%
//連接池的獲取
Connection conn = null;
DataSource ds = null;
ResultSet rs =null;
Statement stmt = null;
Context initCtx = new InitialContext();
ds =(DataSource)initCtx.lookup("java:comp/env/jdbc/TestDB");
if(ds!=null){
out.println("已經獲得DataSource!");
out.println("<br>");
conn = ds.getConnection();
try{
stmt = conn.createStatement();
String sql ="select * from tree_table";
rs = stmt.executeQuery(sql);
out.println("以下是從數據庫中讀取出來的數據:<br>");
while(rs.next()){
out.println("<br>");
out.println(rs.getString("nodeName"));
}
}catch(Exception ex){
ex.printStackTrace();
}finally{
conn.close();
rs.close();
stmt.close();
}
}
%>
在這ds =(DataSource)initCtx.lookup("java:comp/env/jdbc/TestDB");這句代碼中的jdbc/TestDB必須和
<ResourceLink global="jdbc/TestDB" name="jdbc/TestDB" ........ />中的name 屬性保持一樣.
"lookup("java:comp/env/..."這都是固定寫法.
2. 應該算是局部的連接池的配置吧.(針對工程而言)
本身我們的工程中META-INF 中,沒有context.xml文件,
例如 :E:\apache-tomcat-5.5.16\webapps\myproj\META-INF\
此時我們可以在META-INF目錄下面新建一個context.xml文件.
裏面寫下如下的配置,具體的配置參數,按需改變.
Xml代碼
<?xml version="1.0" encoding="UTF-8"?>
<Context reloadable="true" crossContext="true">
<Resource
name="jdbc/TestDB"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/test?autoReconnect=true"
maxActive="50"
maxldle="10"
maxWait="5000"
username="root"
password="admin"
/>
</Context>
這樣子,連接池,也就配置好啦,並不需要修改tomcat裏面的文件。僅僅在我們的工程中的META-INF目錄加入一個context.xml配置文件就好啦.換tomcat版本,更容易.
注:也許你認爲配置這邊文章很簡單,配置數據源連接池也很簡單.但是對於初學者來說,可能是困窘他好久的問題.這方面以前我深有體會.大家都是這樣過來的. 僅供參考.