Java DataSource,JDBC DataSource 示例
Java DataSource 和 JDBC DataSource 编程是在我们的 Java 程序中使用数据库的方式。我们已经看到,JDBC DriverManager可用于获取关系数据库连接。但在实际编程中,我们想要的不仅仅是连接。
Java数据源
大多数时候,我们都在寻找松散耦合的连接,以便我们可以轻松切换数据库,连接池用于事务管理和分布式系统支持。如果您在应用程序中寻找这些功能中的任何一个, JDBC DataSource是首选方法。Java DataSource 接口存在于javax.sql
包中,它只声明两个重载方法getConnection()
和getConnection(String str1,String str2)
。
JDBC 数据源
不同的数据库供应商负责提供不同类型的 DataSource 接口实现。例如,MySQL JDBC 驱动程序使用com.mysql.jdbc.jdbc2.optional.MysqlDataSource
类提供 DataSource 接口的基本实现,而 Oracle 数据库驱动程序使用类实现该接口oracle.jdbc.pool.OracleDataSource
。这些实现类提供方法,通过这些方法我们可以提供带有用户凭据的数据库服务器详细信息。这些 JDBC DataSource 实现类提供的其他一些常见功能包括:
- 缓存 PreparedStatement 以加快处理速度
- 连接超时设置
- 日志记录功能
- ResultSet 最大大小阈值
JDBC 数据源示例
让我们创建一个简单的 JDBC DataSource 示例项目,并学习如何使用 MySQL 和 Oracle DataSource 基本实现类来获取数据库连接。我们的最终项目将如下图所示。
Java JDBC 数据源 - 数据库设置
在开始示例程序之前,我们需要设置一些包含表和示例数据的数据库。MySQL 或 Oracle 数据库的安装超出了本教程的范围,因此我将继续设置包含示例数据的表。
--Create Employee table
CREATE TABLE `Employee` (
`empId` int(10) unsigned NOT NULL,
`name` varchar(10) DEFAULT NULL,
PRIMARY KEY (`empId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- insert some sample data
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 properties
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 Properties
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 jar。
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 DataSource实现类非常相似,让我们编写一个简单的测试程序来使用这些方法并运行一些测试。
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 数据源的工厂类方法与各自的驱动程序 API 紧密耦合。如果我们将来想删除对 Oracle 数据库的支持或想添加其他数据库支持,则需要更改代码。
- 获取 MySQL 和 Oracle DataSource 的大部分代码是类似的,唯一的不同是我们使用的实现类。
Apache Commons DBCP API 通过提供 Java DataSource 实现来帮助我们解决这些问题,该实现可作为我们程序和不同 JDBC 驱动程序之间的抽象层。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 DataSource。如果您在应用程序中仅支持一个数据库,那么您甚至不需要这些逻辑。只需更改属性,您就可以从一个数据库服务器切换到另一个数据库服务器。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 DataSource 和上述用法,也可以使用普通的 DriverManager 来完成。Java DataSource 的主要优点是当它在上下文中并与 JNDI 一起使用时。通过简单的配置,我们可以创建一个由容器本身维护的数据库连接池。大多数 servlet 容器(如 Tomcat 和 JBoss)都提供自己的 Java DataSource 实现,我们所需要做的就是通过简单的基于 XML 的配置对其进行配置,然后使用 JNDI 上下文查找来获取 Java DataSource 并使用它。这有助于我们处理从应用程序端到服务器端的连接池和管理,从而让我们有更多时间为应用程序编写业务逻辑。在下一个教程中,我们将学习如何在 Tomcat 容器中配置 DataSource 并在 Web 应用程序中使用它。