Java DataSource对象
?
連接數據源對象
本節介紹DataSource對象,這是獲得與數據源的連接的首選方法。除了它們的其他優點(將在后面解釋)之外,DataSource對象還可以提供連接池和分布式事務。此功能對于企業數據庫計算至關重要。特別是,它是Enterprise JavaBeans(EJB)技術不可或缺的。
本節向您展示如何使用該DataSource接口獲得連接以及如何使用分布式事務和連接池。兩者都涉及JDBC應用程序中很少的代碼更改。
系統管理員通常使用工具(例如Apache Tomcat或Oracle WebLogic Server)來部署使這些操作成為可能的類而執行的工作因要部署的DataSource對象類型而異。因此,本節的大部分內容專門介紹系統管理員如何設置環境,以便程序員可以使用DataSource對象來獲得連接。
涵蓋以下主題:
- 使用數據源對象獲取連接
- 部署基本數據源對象
- 部署其他數據源實現
- 獲取和使用池化連接
- 部署分布式事務
- 使用連接進行分布式事務
使用數據源對象獲取連接
在建立連接中,您學習了如何使用DriverManager該類來獲得連接。本節說明如何使用DataSource對象來建立與數據源的連接,這是首選方法。
由實現的類實例化的對象DataSource表示特定的DBMS或某些其他數據源,例如文件。甲DataSource對象表示一個特定的DBMS或一些其它數據源,諸如一個文件。如果公司使用多個數據源,它將DataSource為每個數據源部署一個單獨的對象。該DataSource接口由驅動程序供應商實現。它可以通過三種不同的方式實現:
- 基本DataSource實現產生的標準Connection對象沒有在分布式事務中合并或使用。
- 甲DataSource實現,它支持連接池產生Connection參與連接池,即,可循環使用的連接對象。
- 阿DataSource那分布式支持事務執行產生Connection,可以在分布式事務,即,訪問兩個或多個DBMS服務器事務中使用的對象。
JDBC驅動程序應至少包括一個基本DataSource實現。例如,Java DB JDBC驅動程序包括org.apache.derby.jdbc.ClientDataSourceMySQL的實現和com.mysql.jdbc.jdbc2.optional.MysqlDataSource。如果您的客戶端在Java 8 Compact Profile 2上運行,則Java DB JDBC驅動程序為org.apache.derby.jdbc.BasicClientDataSource40。本教程的示例要求壓縮配置文件3或更高。
一DataSource類是支持分布式事務通常也實現了連接池支持。例如,DataSourceEJB供應商提供的類幾乎總是支持連接池和分布式事務。
假設從前面的示例來看,The Coffee Break商店蓬勃發展的連鎖店的所有者已決定通過在Internet上出售咖啡來進一步擴大規模。預期會有大量的在線業務,因此所有者肯定需要連接池。打開和關閉連接會涉及大量開銷,并且所有者希望此在線訂購系統需要大量的查詢和更新。使用連接池,可以反復使用連接池,從而避免了為每次數據庫訪問創建新連接的開銷。此外,所有者現在擁有第二個DBMS,其中包含最近收購的咖啡烘焙公司的數據。這意味著所有者將希望能夠編寫使用舊DBMS服務器和新DBMS服務器的分布式事務。
鏈所有者已重新配置計算機系統,以服務于新的更大的客戶群。所有者購買了最新的JDBC驅動程序和與其一起使用的EJB應用程序服務器,以便能夠使用分布式事務并獲得連接池帶來的更高性能。提供了許多與最近購買的EJB服務器兼容的JDBC驅動程序。所有者現在具有三層體系結構,中間層是新的EJB應用程序服務器和JDBC驅動程序,第三層是兩個DBMS服務器。發出請求的客戶端計算機是第一層。
部署基本數據源對象
系統管理員需要部署DataSource對象,以便Coffee Break的編程團隊可以開始使用它們。部署DataSource對象包括三個任務:
首先,考慮最基本的情況,即使用DataSource接口的基本實現,即不支持連接池或分布式事務的接口。在這種情況下,僅DataSource需要部署一個對象。基本的實現DataSource產生與類產生的相同類型的連接DriverManager。
創建數據源類的實例并設置其屬性
假設一家只希望基本實現的DataSource公司從JDBC供應商DB Access,Inc.購買了一個驅動程序。該驅動程序包括com.dbaccess.BasicDataSource實現該DataSource接口的類。以下代碼摘錄創建該類的實例BasicDataSource并設置其屬性。BasicDataSource部署完實例之后,程序員可以調用該方法DataSource.getConnection來獲取與公司數據庫的連接CUSTOMER_ACCOUNTS。首先,系統管理員使用默認構造函數創建BasicDataSource對象ds。然后,系統管理員設置三個屬性。請注意,以下代碼通常由部署工具執行:
com.dbaccess.BasicDataSource ds = new com.dbaccess.BasicDataSource(); ds.setServerName("grinder"); ds.setDatabaseName("CUSTOMER_ACCOUNTS"); ds.setDescription("Customer accounts database for billing");ds現在,該變量表示CUSTOMER_ACCOUNTS服務器上安裝的數據庫。該BasicDataSource對象產生的任何連接ds都將是與數據庫的連接CUSTOMER_ACCOUNTS。
向使用JNDI API的命名服務注冊數據源對象
通過設置屬性,系統管理員可以向BasicDataSourceJNDI(Java命名和目錄接口)命名服務注冊對象。通常使用的特定命名服務由系統屬性確定,此處未顯示。以下代碼摘錄注冊該BasicDataSource對象并將其與邏輯名綁定jdbc/billingDB:
Context ctx = new InitialContext(); ctx.bind("jdbc/billingDB", ds);此代碼使用JNDI API。第一行創建一個InitialContext對象,該對象用作名稱的起點,類似于文件系統中的根目錄。第二行將BasicDataSource對象關聯或綁定ds到邏輯名jdbc/billingDB。在下一個代碼摘錄中,為命名服務賦予此邏輯名,然后它返回BasicDataSource對象。邏輯名稱可以是任何字符串。在這種情況下,公司決定使用該名稱billingDB作為CUSTOMER_ACCOUNTS數據庫的邏輯名稱。
在前面的示例中,jdbc是初始上下文下的子上下文,就像根目錄下的目錄是子目錄一樣。該名稱jdbc/billingDB類似于路徑名,其中路徑中的最后一項類似于文件名。在這種情況下,billingDB是賦予BasicDataSource對象的邏輯名ds。子上下文jdbc是保留給邏輯名綁定到DataSource對象的,因此jdbc它將始終是數據源邏輯名的第一部分。
使用部署的數據源對象
在DataSource系統管理員部署了基本實現之后,程序員就可以使用它了。這意味著程序員可以提供綁定到DataSource類實例的邏輯數據源名稱,并且JNDI命名服務將返回DataSource該類的實例。getConnection然后可以在該DataSource對象上調用該方法以獲取與其表示的數據源的連接。例如,程序員可能編寫以下兩行代碼來獲取一個DataSource對象,該對象產生與數據庫的連接CUSTOMER_ACCOUNTS。
Context ctx = new InitialContext(); DataSource ds = (DataSource)ctx.lookup("jdbc/billingDB");代碼的第一行以初始上下文為檢索DataSource對象的起點。在jdbc/billingDB為方法提供邏輯名時lookup,該方法將返回DataSource系統管理員jdbc/billingDB在部署時綁定到的對象。因為該方法的返回值lookup是Java?Object,所以DataSource在將其分配給變量之前,必須將其轉換為更特定的類型ds。
變量ds是com.dbaccess.BasicDataSource實現DataSource接口的類的實例。調用該方法將ds.getConnection產生與CUSTOMER_ACCOUNTS數據庫的連接。
Connection con = ds.getConnection("fernanda","brewed");該getConnection方法僅需要用戶名和密碼,因為該變量在其屬性中ds具有與CUSTOMER_ACCOUNTS數據庫建立連接所需的其余信息,例如數據庫名稱和位置。
數據源對象的優點
由于其屬性,DataSource與DriverManager用于連接的類相比,對象是更好的替代方法。程序員不再需要在其應用程序中對驅動程序名稱或JDBC URL進行硬編碼,從而使它們更易于移植。而且,DataSource屬性使代碼維護更加簡單。如果進行了更改,則系統管理員可以更新數據源屬性,而不必擔心更改與該數據源建立連接的每個應用程序。例如,如果將數據源移動到其他服務器,則系統管理員要做的就是將serverName屬性設置為新的服務器名稱。
除了可移植性和易于維護之外,使用DataSource對象進行連接還可以提供其他優點。當實現DataSource接口以與實現一起使用ConnectionPoolDataSource時,DataSource該類實例產生的所有連接將自動為池連接。類似地,當DataSource實現被實現為與XADataSource類一起使用時,它產生的所有連接將自動為可在分布式事務中使用的連接。下一節將說明如何部署這些類型的DataSource實現。
部署其他數據源實現
系統管理員或以該身份工作的其他人可以部署DataSource對象,以便它產生的連接為池連接。為此,他(她)首先部署一個ConnectionPoolDataSource對象,然后部署一個DataSource實現為可以使用的對象。ConnectionPoolDataSource設置對象的屬性,使其代表將與其建立連接的數據源。將ConnectionPoolDataSource對象注冊到JNDI命名服務后,將DataSource部署對象。通常,僅需為該DataSource對象設置兩個屬性:description和dataSourceName。賦予該dataSourceName屬性的值是標識ConnectionPoolDataSource先前部署的對象的邏輯名,該邏輯名是包含進行連接所需的屬性的對象。
隨著ConnectionPoolDataSource與DataSource對象部署,您可以調用該方法DataSource.getConnection上DataSource的對象,并得到一個連接池。此連接將連接到ConnectionPoolDataSource對象屬性中指定的數據源。
以下示例描述了The Coffee Break的系統管理員如何部署DataSource實現為提供池化連接的對象。系統管理員通常會使用部署工具,因此本節中顯示的代碼片段是部署工具將執行的代碼。
為了獲得更好的性能,Coffee Break公司從DB Access,Inc.購買了JDBC驅動程序com.dbaccess.ConnectionPoolDS,其中包括實現該ConnectionPoolDataSource接口的class?。系統管理員創建創建此類的實例,設置其屬性,并將其注冊到JNDI命名服務。Coffee Break從其EJB服務器供應商Application Logic,Inc.購買了其DataSource類com.applogic.PooledDataSource。該類com.applogic.PooledDataSource使用ConnectionPoolDataSource該類提供的基礎支持來實現連接池com.dbaccess.ConnectionPoolDS。
該ConnectionPoolDataSource對象必須首先部署。以下代碼創建的實例com.dbaccess.ConnectionPoolDS并設置其屬性:
com.dbaccess.ConnectionPoolDS cpds = new com.dbaccess.ConnectionPoolDS(); cpds.setServerName("creamer"); cpds.setDatabaseName("COFFEEBREAK"); cpds.setPortNumber(9040); cpds.setDescription("Connection pooling for " + "COFFEEBREAK DBMS");ConnectionPoolDataSource部署對象后,系統管理員將部署DataSource對象。以下代碼向JNDI命名服務注冊com.dbaccess.ConnectionPoolDS對象cpds。請注意,與cpds變量關聯的邏輯名具有在子上下文pool下添加的子上下文jdbc,這類似于將子目錄添加到分層文件系統中的另一個子目錄。該類的任何實例的邏輯名稱com.dbaccess.ConnectionPoolDS始終以開頭jdbc/pool。Oracle建議將所有ConnectionPoolDataSource對象放在子上下文下jdbc/pool:
Context ctx = new InitialContext(); ctx.bind("jdbc/pool/fastCoffeeDB", cpds);接下來,部署DataSource實現為與cpds變量和com.dbaccess.ConnectionPoolDS該類的其他實例交互的類。以下代碼創建此類的實例并設置其屬性。請注意,僅為此實例設置了兩個屬性com.applogic.PooledDataSource。description設置該屬性是因為它始終是必需的。設置的另一個屬性dataSourceName給出了的邏輯JNDI名稱cpds,它是com.dbaccess.ConnectionPoolDS該類的實例。換句話說,cpds表示ConnectionPoolDataSource將為對象實現連接池的DataSource對象。
以下代碼(可能由部署工具執行)創建一個PooledDataSource對象,設置其屬性,并將其綁定到邏輯名稱jdbc/fastCoffeeDB:
com.applogic.PooledDataSource ds = new com.applogic.PooledDataSource(); ds.setDescription("produces pooled connections to COFFEEBREAK"); ds.setDataSourceName("jdbc/pool/fastCoffeeDB"); Context ctx = new InitialContext(); ctx.bind("jdbc/fastCoffeeDB", ds);此時,將DataSource部署一個對象,應用程序可以從該對象獲得與數據庫的池化連接COFFEEBREAK。
獲取和使用池化連接
一個連接池是數據庫連接對象的緩存。這些對象表示物理數據庫連接,應用程序可以使用這些物理數據庫連接來連接數據庫。在運行時,應用程序請求池中的連接。如果池包含可以滿足請求的連接,則它將連接返回給應用程序。如果未找到任何連接,則會創建一個新的連接并返回到應用程序。應用程序使用該連接在數據庫上執行某些工作,然后將對象返回到池中。然后,該連接可用于下一個連接請求。
連接池可促進連接對象的重用,并減少創建連接對象的次數。連接池顯著提高了數據庫密集型應用程序的性能,因為創建連接對象在時間和資源上都非常昂貴。
現在這些DataSource和ConnectionPoolDataSource對象的部署,程序員可以使用DataSource對象來獲取連接池。獲取池化連接的代碼與獲取非池化連接的代碼一樣,如以下兩行所示:
ctx = new InitialContext(); ds = (DataSource)ctx.lookup("jdbc/fastCoffeeDB");該變量ds表示一個DataSource對象,該對象產生與數據庫的池化連接COFFEEBREAK。您只需要檢索DataSource一次該對象,因為您可以使用它來產生所需的任意多個池連接。getConnection在ds變量上調用方法會自動產生一個池化連接,因為DataSource該ds變量代表的對象已配置為產生池化連接。
連接池通常對程序員是透明的。使用池連接時,只需要做兩件事:
使用DataSource對象而不是DriverManager類來獲得連接。在下面的代碼行中,ds是一個DataSource對象的實現和部署,以便它將創建池連接,username并且password是代表有權訪問數據庫的用戶憑據的變量:
Connection con = ds.getConnection(username, password);使用finally語句關閉池化連接。在適用于使用池化連接的代碼的代碼塊finally之后,將出現以下try/catch代碼塊:
try {Connection con = ds.getConnection(username, password);// ... code to use the pooled// connection con } catch (Exception ex {// ... code to handle exceptions } finally {if (con != null) con.close(); }否則,使用池連接的應用程序與使用常規連接的應用程序相同。應用程序程序員在完成連接池時可能會注意到的唯一另一件事是性能更好。
以下示例代碼獲取一個DataSource對象,該對象產生與數據庫的連接,COFFEEBREAK并使用它來更新表中的價格COFFEES:
import java.sql.*; import javax.sql.*; import javax.ejb.*; import javax.naming.*;public class ConnectionPoolingBean implements SessionBean {// ...public void ejbCreate() throws CreateException {ctx = new InitialContext();ds = (DataSource)ctx.lookup("jdbc/fastCoffeeDB");}public void updatePrice(float price, String cofName,String username, String password)throws SQLException{Connection con;PreparedStatement pstmt;try {con = ds.getConnection(username, password);con.setAutoCommit(false);pstmt = con.prepareStatement("UPDATE COFFEES " +"SET PRICE = ? " +"WHERE COF_NAME = ?");pstmt.setFloat(1, price);pstmt.setString(2, cofName);pstmt.executeUpdate();con.commit();pstmt.close();} finally {if (con != null) con.close();}}private DataSource ds = null;private Context ctx = null; }此代碼示例中的連接參與連接池,因為以下是正確的:
- 一個類實現的實例ConnectionPoolDataSource已部署。
- DataSource已經部署了一個類實現的實例,并且為其dataSourceName屬性設置的值是綁定到先前部署的ConnectionPoolDataSource對象的邏輯名稱。
請注意,盡管此代碼與您之前看到的代碼非常相似,但在以下方面有所不同:
-
它進口javax.sql,javax.ejb和javax.naming包除java.sql。
的DataSource和ConnectionPoolDataSource接口處于javax.sql封裝,JNDI構造InitialContext和方法Context.lookup是的一部分javax.naming封裝。此特定示例代碼采用使用javax.ejb包中API的EJB組件的形式。該示例的目的是說明使用池化連接的方式與使用非池化連接的方式相同,因此您不必擔心理解EJB API。
-
它使用DataSource對象來獲得連接,而不是使用DriverManager設施。
-
它使用一個finally塊來確保關閉連接。
獲取和使用池化連接類似于獲取和使用常規連接。當某人充當系統管理員正確部署了一個ConnectionPoolDataSource對象和一個DataSource對象后,應用程序將使用該DataSource對象來獲得池化連接。但是,應用程序應使用finally塊來關閉池化連接。為簡單起見,前面的示例使用一個finally塊,但不使用任何catch塊。如果該try塊中的方法引發了異常,則默認情況下將引發該異常,finally無論如何該子句都將執行。
部署分布式事務
DataSource可以部署對象以獲得可在分布式事務中使用的連接。與連接池一樣,必須部署兩個不同的類實例:一個XADataSource對象和一個DataSource實現為與之協同工作的對象。
假設The Coffee Break企業家購買的EJB服務器包含DataSource類com.applogic.TransactionalDS,XADataSource該類與諸如com.dbaccess.XATransactionalDS。它可以與任何XADataSource類一起使用的事實使EJB服務器可以跨JDBC驅動程序移植。當DataSource與XADataSource物體的部署,產生的連接將能夠參與分布式事務。在這種情況下,將com.applogic.TransactionalDS實現該類,以使生成的連接也成為池連接,對于DataSource作為EJB服務器實現的一部分提供的類,通常是這種情況。
該XADataSource對象必須首先部署。以下代碼創建的實例com.dbaccess.XATransactionalDS并設置其屬性:
com.dbaccess.XATransactionalDS xads = new com.dbaccess.XATransactionalDS(); xads.setServerName("creamer"); xads.setDatabaseName("COFFEEBREAK"); xads.setPortNumber(9040); xads.setDescription("Distributed transactions for COFFEEBREAK DBMS");以下代碼向JNDI命名服務注冊com.dbaccess.XATransactionalDS對象xads。請注意,與之關聯的邏輯名稱在下方添加xads了子上下文。Oracle建議類的任何實例的邏輯名稱始終以開頭。xajdbccom.dbaccess.XATransactionalDSjdbc/xa
Context ctx = new InitialContext(); ctx.bind("jdbc/xa/distCoffeeDB", xads);接下來,部署DataSource實現xads與其他XADataSource對象交互的對象。請注意,DataSource類com.applogic.TransactionalDS可以與XADataSource任何JDBC驅動程序供應商的類一起使用。部署DataSource對象涉及創建com.applogic.TransactionalDS類的實例并設置其屬性。該dataSourceName屬性設置為jdbc/xa/distCoffeeDB,與關聯的邏輯名稱com.dbaccess.XATransactionalDS。這是實現XADataSource該類的分布式事務處理功能的DataSource類。以下代碼部署DataSource該類的實例:
com.applogic.TransactionalDS ds = new com.applogic.TransactionalDS(); ds.setDescription("Produces distributed transaction " +"connections to COFFEEBREAK"); ds.setDataSourceName("jdbc/xa/distCoffeeDB"); Context ctx = new InitialContext(); ctx.bind("jdbc/distCoffeeDB", ds);既然類的實例com.applogic.TransactionalDS,并com.dbaccess.XATransactionalDS已經部署,應用程序可以調用該方法getConnection的實例TransactionalDS類來獲取到的連接COFFEEBREAK可在分布式事務中使用的數據庫。
使用連接進行分布式事務
要獲得可用于分布式事務的連接,必須使用DataSource已正確實現和部署的對象,如“部署分布式事務”部分中所示。使用這樣的DataSource對象,對其調用方法getConnection。建立連接后,請像使用其他任何連接一樣使用它。由于jdbc/distCoffeesDB已與XADataSourceJNDI命名服務中的對象相關聯,因此以下代碼生成了Connection可在分布式事務中使用的對象:
Context ctx = new InitialContext(); DataSource ds = (DataSource)ctx.lookup("jdbc/distCoffeesDB"); Connection con = ds.getConnection();對于此連接作為分布式事務的一部分時的使用方式,存在一些較小但重要的限制。事務管理器控制分布式事務何時開始以及何時提交或回滾。因此,應用程序代碼絕不應調用方法Connection.commit或Connection.rollback。應用程序同樣不應調用Connection.setAutoCommit(true),它啟用了自動提交模式,因為這也會干擾事務管理器對事務邊界的控制。這說明了為什么在分布式事務范圍內創建的新連接默認情況下會禁用其自動提交模式。請注意,這些限制僅在連接參與分布式事務時才適用。連接不是分布式事務的一部分時,沒有任何限制。
對于以下示例,假設已訂購一份咖啡,這將觸發對位于不同DBMS服務器上的兩個表的更新。第一個表是一個新INVENTORY表,第二個COFFEES表是該表。因為這些表位于不同的DBMS服務器上,所以涉及它們的事務將是分布式事務。以下示例中的代碼(該示例獲得一個連接,更新該COFFEES表并關閉該連接)是分布式事務的第二部分。
請注意,由于分布式事務的范圍由中間層服務器的基礎系統基礎結構控制,因此代碼不會明確地提交或回退更新。同樣,假設用于分布式事務的連接是池化連接,則應用程序使用一個finally塊來關閉該連接。這樣可以保證即使拋出異常也將關閉有效的連接,從而確保將連接返回到連接池以進行回收。
下面的代碼示例說明了一個Enterprise Bean,它是一個實現可以由客戶端計算機調用的方法的類。這個例子的目的是說明用于分布式事務應用程序代碼是沒有從其他代碼不同,除了它不調用Connection方法commit,rollback或setAutoCommit(true)。因此,您不必擔心了解所使用的EJB API。
import java.sql.*; import javax.sql.*; import javax.ejb.*; import javax.naming.*;public class DistributedTransactionBean implements SessionBean {// ...public void ejbCreate() throws CreateException {ctx = new InitialContext();ds = (DataSource)ctx.lookup("jdbc/distCoffeesDB");}public void updateTotal(int incr, String cofName, String username,String password)throws SQLException {Connection con;PreparedStatement pstmt;try {con = ds.getConnection(username, password);pstmt = con.prepareStatement("UPDATE COFFEES " +"SET TOTAL = TOTAL + ? " +"WHERE COF_NAME = ?");pstmt.setInt(1, incr);pstmt.setString(2, cofName);pstmt.executeUpdate();stmt.close();} finally {if (con != null) con.close();}}private DataSource ds = null;private Context ctx = null; }總結
以上是生活随笔為你收集整理的Java DataSource对象的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 建立APICloud云端数据库
- 下一篇: 网易考拉Android客户端路由总线设计