DataSource简介
一、简介
Java DataSource接口位于javax.sql包中,它只声明了两个重载方法:getConnection()和getConnection(String username, String password)。
DataSource对象表示特定的DBMS或其他数据源(例如文件),使用DataSource可以获取松耦合的连接,方便切换数据库;除此之外,DataSource还可以提供连接池和分布式事务。
DataSource通常由驱动程序供应商实现,可以通过三种方式:
- 产生标准Connection对象的DataSource实现(不支持连接池和分布式事务)。
- 支持连接池的DataSource的实现,产生可回收的Connection对象。
- 支持分布式事务的DataSource的实现,产生可以在分布式事务中使用的Connection对象(在一个事务中访问两个或多个DBMS服务器);支持分布式事务的DataSource通常也实现了对连接池的支持。
不同的数据库供应商提供了不同的DataSource实现,例如:MySQL使用com.mysql.jdbc.jdbc2.optional.MysqlDataSource提供DataSource接口的基本实现;Oracle使用oracle.jdbc.pool.OracleDataSource提供DataSource接口的基本实现。除了获取数据库连接外,这些JDBC数据源实现类还提供了其他一些常见功能,例如:
- 缓存PreparedStatement以加快处理速度
- 连接超时设置
- 日志记录
- ResultSet最大阈值设置
二、JDBC数据源
数据库表:
create table employee(
id int primary key auto_increment,
age int,
firstname varchar(30),
lastname varchar(30)
)
| id | age | firstname | lastname |
| 2 | 36 | Michael | Stonebridge |
| 3 | 33 | Damien | Scott |
| 4 | 38 | Rachel | Dalton |
数据库配置(使用properties配置方式提供松耦合的数据库配置):
#MySQL
MYSQL_DB_DRIVERCLASS=com.mysql.jdbc.Driver
MYSQL_DB_URL=jdbc:mysql://localhost:3306/test
MYSQL_DB_USERNAME=root
MYSQL_DB_PASSWORD=123456
#Oracle
ORACLE_DB_DRIVERCLASS=oracle.jdbc.driver.OracleDriver
ORACLE_DB_URL=jdbc:oracle:thin:@localhost:1521:orcl
ORACLE_DB_USERNAME=admin
ORACLE_DB_PASSWORD=123456
还需要在工程中引入对应数据库的驱动jar。
1、样例一
数据源工厂MyDataSourceFactory:
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import oracle.jdbc.pool.OracleDataSource;
public class MyDataSourceFactory {
public static DataSource getDataSource(String type){
DataSource ds = null;
if("MySQL".equals(type)){
ds = getMySQLDataSource();
}else if("Oracle".equals(type)){
ds = getOracleDataSource();
}else{
System.out.println("invalid db type");
}
return ds;
}
private static DataSource getMySQLDataSource(){
MysqlDataSource dataSource = new MysqlDataSource();
try {
Properties props = new Properties();
props.load(new FileInputStream("src/db.properties"));
dataSource.setURL(props.getProperty("MYSQL_DB_URL"));
dataSource.setUser(props.getProperty("MYSQL_DB_USERNAME"));
dataSource.setPassword(props.getProperty("MYSQL_DB_PASSWORD"));
}catch (IOException e) {
e.printStackTrace();
}
return dataSource;
}
private static DataSource getOracleDataSource(){
OracleDataSource dataSource = null;
try {
dataSource = new OracleDataSource();
Properties props = new Properties();
props.load(new FileInputStream("db.properties"));
dataSource.setURL(props.getProperty("ORACLE_DB_URL"));
dataSource.setUser(props.getProperty("ORACLE_DB_USERNAME"));
dataSource.setPassword(props.getProperty("ORACLE_DB_PASSWORD"));
}catch (IOException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return 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) {
DataSource ds = MyDataSourceFactory.getDataSource("MySQL");
try(Connection connection = ds.getConnection()){
try(Statement stmt = connection.createStatement()){
String sql = "select id,age,firstname,lastname from employee";
try(ResultSet rs = stmt.executeQuery(sql)){
while(rs.next()){
String format = "ID: %s, Age: %s, FirstName: %s, LastName: %s.";
System.out.println(String.format(format, rs.getInt("id"), rs.getInt("age"), rs.getString("firstname"), rs.getString("lastname")));
}
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
样例中的测试类完全独立于任何特定的数据库类,这样可以隐藏底层细节,实现松耦合。
2、样例二
样例一中的数据源工厂类存在以下问题:
- 用于创建MySQL和Oracle数据源的工厂类与各数据库的驱动程序API紧耦合;
- 获取MySQL和Oracle数据源的大部分代码是相似的,唯一不同的是使用的实现类。
此样例中使用Apache Commons DBCP API提供的Java DataSource实现来处理这些问题(Apache DBCP库依赖于Commons Pool库,需要引入相关的jar包)。
数据库配置文件:
mysql.properties:
#MySQL
DB_DRIVERCLASS=com.mysql.jdbc.Driver
DB_URL=jdbc:mysql://localhost:3306/test
DB_USERNAME=root
DB_PASSWORD=123456
oracle.properties:
#Oracle
DB_DRIVERCLASS=oracle.jdbc.driver.OracleDriver
DB_URL=jdbc:oracle:thin:@localhost:1521:orcl
DB_USERNAME=admin
DB_PASSWORD=123456
数据源工厂DBCPDataSourceFactory:
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 type){
BasicDataSource dataSource = new BasicDataSource();
String filename = null;
if("MySQL".equals(type)){
filename = "mysql.properties";
}else if("Oracle".equals(type)){
filename = "oracle.properties";
}else{
System.out.println("invalid db type");
}
if(filename != null){
try {
Properties props = new Properties();
props.load(new FileInputStream(filename));
dataSource.setDriverClassName(props.getProperty("DB_DRIVERCLASS"));
dataSource.setUrl(props.getProperty("DB_URL"));
dataSource.setUsername(props.getProperty("DB_USERNAME"));
dataSource.setPassword(props.getProperty("DB_PASSWORD"));
}catch (IOException e) {
e.printStackTrace();
}
}
return dataSource;
}
}
测试类只需要替换获取DataSource的代码:
DataSource ds = DBCPDataSourceFactory.getDataSource("MySQL");
从上面的例子中可以看到Apache DBCP提供抽象的关键点是setDriverClassName()方法。
三、Tomcat容器中的数据源
DataSource的主要好处是当它在上下文中使用并与JNDI一起使用时,可以通过简单的配置创建一个由容器本身维护的数据库连接池。大多数的Servlet容器(例如Tomcat和JBoss)都提供了自己的Java DataSource实现,我们所需要的只是通过简单的XML配置并使用JNDI上下文查找来获取Java DataSource。
Tomcat提供了三种在JNDI上下文中配置DataSource的方法:
-
应用程序配置
context.xml这是配置DataSource的最简单方法,只需要在
META-INF目录下的context.xml文件中配置Resource,容器将负责加载。这种方式有以下缺点:context.xml与war包捆绑在一起,修改时需要重新构建部署新的war包。- 数据源仅供应用程序使用,不能全局使用。
- 如果(在server.xml中)定义了同名的全局数据源,则会忽略该应用程序数据源。
-
服务器配置
context.xml
如果服务器中有多个应用程序,并且希望在它们之间共享DataSource,那么可以在服务器的context.xml文件中配置数据源(此文件位于Tomcat的conf目录下)。
由于服务器context.xml文件的作用域是应用程序,将为每个应用程序创建数据源,因此,如果定义了一个包含100个连接的DataSource连接池,而且服务器有20个应用程序,这将会有2000个连接,这样显然会消耗数据库服务器资源并降低应用程序性能。
- 配置
server.xml和context.xml
可以通过server.xml中的GlobalNamingResources元素中定义全局DataSource,使服务器上运行的多个应用程序之间共享公共资源池。
在server.xml中定义了一个名为jdbc/MyDB的数据源,供Web应用通过JNDI查找使用:
<GlobalNamingResources>
<Resource name="jdbc/MyDB"
global="jdbc/MyDB"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/test"
username="root"
password="123456"
maxActive="100"
maxIdle="20"
minIdle="5"
maxWait="10000"/>
</GlobalNamingResources>
Web应用若需访问全局资源,需要在context.xml中引用:
<Context>
<ResourceLink name="jdbc/MyLocalDB"
global="jdbc/MyDB"
auth="Container"
type="javax.sql.DataSource" />
</Context>
使用示例:
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("java:/comp/env/jdbc/MyLocalDB");
Connection con = ds.getConnection();