Java DataSource和JDBC DataSource编程是在我们的Java程序中与数据库交互的方式。我们已经看到,JDBC DriverManager可以用来获取关系型数据库连接。但是当涉及到实际编程时,我们希望得到更多的功能,而不仅仅是连接。
Java DataSource
大多数时候,我们希望实现松耦合的连接,以便可以轻松切换数据库,实现连接池进行事务管理和分布式系统支持。如果你的应用程序需要这些功能,那么JDBC DataSource是首选的方法。Java DataSource接口位于javax.sql包中,它只声明了两个重载方法getConnection()和getConnection(String str1,String str2)。
JDBC DataSource
它是不同数据库供应商提供DataSource接口不同实现的责任。例如,MySQL JDBC驱动程序使用com.mysql.jdbc.jdbc2.optional.MysqlDataSource
类提供DataSource接口的基本实现,而Oracle数据库驱动程序则使用oracle.jdbc.pool.OracleDataSource
类实现。这些实现类通过方法提供了我们可以使用用户凭据提供数据库服务器详细信息的方式。这些JDBC DataSource实现类提供的其他常见功能有:
- 缓存PreparedStatement以加快处理速度
- 连接超时设置
- 日志记录功能
- ResultSet最大大小阈值
JDBC DataSource示例
让我们创建一个简单的JDBC DataSource示例项目,学习如何使用MySQL和Oracle DataSource基本实现类获取数据库连接。我们的最终项目将如下图所示。
Java JDBC 數據源 – 數據庫設置
在我們開始示例程序之前,我們需要一些數據庫設置,包括表和樣本數據。安裝 MySQL 或 Oracle 數據庫超出了本教程的範圍,所以我將直接設置帶有樣本數據的表。
-- 創建員工表
CREATE TABLE `Employee` (
`empId` int(10) unsigned NOT NULL,
`name` varchar(10) DEFAULT NULL,
PRIMARY KEY (`empId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 插入一些樣本數據
INSERT INTO `Employee` (`empId`, `name`)
VALUES
(1, 'Pankaj'),
(2, 'David');
commit;
CREATE TABLE "EMPLOYEE"
(
"EMPID" NUMBER NOT NULL ENABLE,
"NAME" VARCHAR2(10 BYTE) DEFAULT NULL,
PRIMARY KEY ("EMPID")
);
Insert into EMPLOYEE (EMPID,NAME) values (10,'Pankaj');
Insert into EMPLOYEE (EMPID,NAME) values (5,'Kumar');
Insert into EMPLOYEE (EMPID,NAME) values (1,'Pankaj');
commit;
現在讓我們繼續進行我們的 Java 程序。為了使數據庫配置鬆散耦合,我將從屬性文件中讀取它們。db.properties 文件:
# MySQL DB 屬性
MYSQL_DB_DRIVER_CLASS=com.mysql.jdbc.Driver
MYSQL_DB_URL=jdbc:mysql://localhost:3306/UserDB
MYSQL_DB_USERNAME=pankaj
MYSQL_DB_PASSWORD=pankaj123
# Oracle DB 屬性
ORACLE_DB_DRIVER_CLASS=oracle.jdbc.driver.OracleDriver
ORACLE_DB_URL=jdbc:oracle:thin:@localhost:1521:orcl
ORACLE_DB_USERNAME=hr
ORACLE_DB_PASSWORD=oracle
請確保上述配置與您的本地設置相匹配。同時,請確保您已將 MySQL 和 Oracle DB JDBC jars 包含在項目的構建路徑中。
Java JDBC 數據源 – MySQL、Oracle 示例
讓我們編寫一個工廠類,我們可以使用它來獲取 MySQL 或 Oracle 數據源。
package com.journaldev.jdbc.datasource;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import oracle.jdbc.pool.OracleDataSource;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
public class MyDataSourceFactory {
public static DataSource getMySQLDataSource() {
Properties props = new Properties();
FileInputStream fis = null;
MysqlDataSource mysqlDS = null;
try {
fis = new FileInputStream("db.properties");
props.load(fis);
mysqlDS = new MysqlDataSource();
mysqlDS.setURL(props.getProperty("MYSQL_DB_URL"));
mysqlDS.setUser(props.getProperty("MYSQL_DB_USERNAME"));
mysqlDS.setPassword(props.getProperty("MYSQL_DB_PASSWORD"));
} catch (IOException e) {
e.printStackTrace();
}
return mysqlDS;
}
public static DataSource getOracleDataSource(){
Properties props = new Properties();
FileInputStream fis = null;
OracleDataSource oracleDS = null;
try {
fis = new FileInputStream("db.properties");
props.load(fis);
oracleDS = new OracleDataSource();
oracleDS.setURL(props.getProperty("ORACLE_DB_URL"));
oracleDS.setUser(props.getProperty("ORACLE_DB_USERNAME"));
oracleDS.setPassword(props.getProperty("ORACLE_DB_PASSWORD"));
} catch (IOException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return oracleDS;
}
}
請注意,Oracle 和 MySQL 數據源實現類非常相似,讓我們編寫一個簡單的測試程序來使用這些方法並運行一些測試。
package com.journaldev.jdbc.datasource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
public class DataSourceTest {
public static void main(String[] args) {
testDataSource("mysql");
System.out.println("**********");
testDataSource("oracle");
}
private static void testDataSource(String dbType) {
DataSource ds = null;
if("mysql".equals(dbType)){
ds = MyDataSourceFactory.getMySQLDataSource();
}else if("oracle".equals(dbType)){
ds = MyDataSourceFactory.getOracleDataSource();
}else{
System.out.println("invalid db type");
return;
}
Connection con = null;
Statement stmt = null;
ResultSet rs = null;
try {
con = ds.getConnection();
stmt = con.createStatement();
rs = stmt.executeQuery("select empid, name from Employee");
while(rs.next()){
System.out.println("Employee ID="+rs.getInt("empid")+", Name="+rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
try {
if(rs != null) rs.close();
if(stmt != null) stmt.close();
if(con != null) con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
通知,客戶端類別完全獨立於任何特定數據庫類別。這有助於我們隱藏客戶端程序的底層實現細節,實現鬆散耦合和抽象效益。當我們運行上面的測試程序時,我們將獲得以下輸出。
Employee ID=1, Name=Pankaj
Employee ID=2, Name=David
**********
Employee ID=10, Name=Pankaj
Employee ID=5, Name=Kumar
Employee ID=1, Name=Pankaj
Apache Commons DBCP範例
如果您查看上面的Java DataSource工廠類別,它存在兩個主要問題。
- 創建MySQL和Oracle DataSource的工廠類別方法與相應的驅動程序API緊密耦合。如果未來要刪除對Oracle數據庫的支持,或者要添加對其他數據庫的支持,將需要進行代碼更改。
- 獲取MySQL和Oracle DataSource的大部分代碼是相似的,唯一不同的是我們使用的實現類別。
Apache Commons DBCP API通過提供作為我們程序和不同JDBC驅動程序之間抽象層的Java DataSource實現,幫助我們消除這些問題。Apache DBCP庫依賴於Commons Pool庫,因此確保它們都在構建路徑中,如圖所示。這是使用BasicDataSource的DataSource工廠類別,它是DataSource的簡單實現。
package com.journaldev.jdbc.datasource;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
public class DBCPDataSourceFactory {
public static DataSource getDataSource(String dbType){
Properties props = new Properties();
FileInputStream fis = null;
BasicDataSource ds = new BasicDataSource();
try {
fis = new FileInputStream("db.properties");
props.load(fis);
}catch(IOException e){
e.printStackTrace();
return null;
}
if("mysql".equals(dbType)){
ds.setDriverClassName(props.getProperty("MYSQL_DB_DRIVER_CLASS"));
ds.setUrl(props.getProperty("MYSQL_DB_URL"));
ds.setUsername(props.getProperty("MYSQL_DB_USERNAME"));
ds.setPassword(props.getProperty("MYSQL_DB_PASSWORD"));
}else if("oracle".equals(dbType)){
ds.setDriverClassName(props.getProperty("ORACLE_DB_DRIVER_CLASS"));
ds.setUrl(props.getProperty("ORACLE_DB_URL"));
ds.setUsername(props.getProperty("ORACLE_DB_USERNAME"));
ds.setPassword(props.getProperty("ORACLE_DB_PASSWORD"));
}else{
return null;
}
return ds;
}
}
當您看到根據用戶輸入,創建MySQL或Oracle數據源時,如果應用程序僅支持一個數據庫,則無需這些邏輯。只需更改屬性,即可在不同的數據庫服務器之間切換。Apache DBCP提供抽象的關鍵點是setDriverClassName()方法。以下是使用上述工廠方法獲取不同類型連接的客戶端程序。
package com.journaldev.jdbc.datasource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
public class ApacheCommonsDBCPTest {
public static void main(String[] args) {
testDBCPDataSource("mysql");
System.out.println("**********");
testDBCPDataSource("oracle");
}
private static void testDBCPDataSource(String dbType) {
DataSource ds = DBCPDataSourceFactory.getDataSource(dbType);
Connection con = null;
Statement stmt = null;
ResultSet rs = null;
try {
con = ds.getConnection();
stmt = con.createStatement();
rs = stmt.executeQuery("select empid, name from Employee");
while(rs.next()){
System.out.println("Employee ID="+rs.getInt("empid")+", Name="+rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
try {
if(rs != null) rs.close();
if(stmt != null) stmt.close();
if(con != null) con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
運行上面的程序時,輸出與之前的程序相同。如果查看Java JDBC數據源和上面的用法,也可以使用常規的DriverManager完成。Java數據源的主要好處是當它在上下文中並與JNDI一起使用時。通過簡單的配置,我們可以創建一個由容器本身維護的數據庫連接池。大多數Servlet容器(如Tomcat和JBoss)提供其自己的Java數據源實現,我們只需通過簡單的基於XML的配置進行配置,然後使用JNDI上下文查找來獲取Java數據源並使用它。這有助於我們將連接池和管理從應用程序端移到服務器端,從而給我們更多的時間為應用程序編寫業務邏輯。在下一個教程中,我們將學習如何在Tomcat容器中配置數據源並在Web應用程序中使用它。
Source:
https://www.digitalocean.com/community/tutorials/java-datasource-jdbc-datasource-example