Connection con = null;
try{
//通过得到字节码对象的方式加载静态代码块 从而加载注册驱动程序
Class.forName(String driver);
//注册URL取得对数据库的连接 获取连接对象
con = DriverManager.getConnection(uri, username, password);
...
con.close();
}catch(Exception e){
...
}finally{
...
}
使用这种连接数据库的方法,存在几个问题。
首先,“JDBCDriverName”是硬编码的,例如使用Oracle 数据库,只能使用oracle.jdbc.driver.OracleDriver 驱动,如果根据需要,改用其他的数据库产品例如DB2 或者MySQL,则JDBC 驱动程序包和类名需要做相应的修改。
其次,用于连接数据库的URL、用户名和密码也是硬编码的,这不仅使程序在不同的环境下不能正常运行,而且还存在严重的安全性问题,因为这些参数可能根据实际情况有所更改而且用户名和密码很容易遭到破解。
第三,应该使用连接池而不是自己管理连接。
JNDI(Java Naming and Directory Interface),即Java 命名与自录接口。JNDI为开发人员提供了查找和访问各种命名和目录服务通用的、统一的接口,完全解决了上述问题.使用JNDI,开发人员不用关心类似“具体的数据库后台是什么?JDBC 驱动程序是什么?JDBC URL格式是什么?访问数据库的用户名和口令是什么?”等等这些问题,开发人员编写的程序不需要包含对JDBC 驱动程序的引用,没有服务器名称,没有用户名称或口令,开发人员仅仅需要引用相应的数据源即可。
数据源是在JDBC 2.0中引入的一个概念。在JDBC 2.0扩展包中定义了javax.sql.DataSource接口来描述这个概念,数据源对象必须实现这个接口。如果用户希望建立一个数据库连接,通过查询在JNDI 服务中的数据源,就可以从数据源中获取相应的数据库连接。这样用户就只需要提供一个逻辑名称( Logic Name),而不是数据库登录的具体细节。
不再如方法1那样亲自管理数据库连接,而是让Servlet/JSP 容器来替你完成,需要对容器进行配置。在Tomcat 中,是通过在应用程序的Context 元素下声明Resource元素来实现的,Tomcat上下文中就包含了一个带有内部连接池的DataSource 资源。
1.在tomcat服务器conf文件夹下context.xml配置
<Context>
<Resource name="jdbc/myDataSource"
auth="Container"
type="javax.sql.DataSource"
username="root"
password="root"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/test" />
<WatchedResource>WEB-INF/web.xmlWatchedResource>
Context>
具体设置过程可参考:Tomcat7配置数据源
2.DataSourceCache类
这是一个辅助工具类,负责查找容器管理的DataSource ,并进行缓存。
package app10d.dao;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
/*DataSourceCache 类,这是一个辅助工具类,负责查找容器管
理的DataSource ,并进行缓存。*/
public class DataSourceCache {
private static DataSourceCache instance;
private DataSource dataSource;
static {
instance = new DataSourceCache();
}
private DataSourceCache() {
//该接口表示一个命名上下文,它由一组名称对对象绑定组成。 它包含检查和更新这些绑定的方法。
Context context = null;
try {
context = new InitialContext();
dataSource = (DataSource) context.lookup(
"java:comp/env/jdbc/myDataSource");
} catch (NamingException e) {
}
}
public static DataSourceCache getInstance() {
return instance;
}
public DataSource getDataSource() {
return dataSource;
}
}
3.BaseDao类
创建dataSource,就可以获取到连接
package app10d.dao;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import javax.sql.DataSource;
public class BaseDAO implements DAO {
public Connection getConnection() throws DAOException {
//问题在于,这个连接来自一个由容器维护的JNDI对象。那么在容器之
//外如何对ProductDAO 进行测试呢?
DataSource dataSource = DataSourceCache.getInstance().getDataSource();
try {
return dataSource.getConnection();
} catch (Exception e) {
e.printStackTrace();
throw new DAOException();
}
}
protected void closeDBObjects(ResultSet resultSet, Statement statement,
Connection connection) {
if (resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
}
}
if (statement != null) {
try {
statement.close();
} catch (Exception e) {
}
}
if (connection != null) {
try {
connection.close();
} catch (Exception e) {
}
}
}
}
配置容器数据源,存在一个问题,就是难于测试。要想测试DAO对象,必须先将整个应用程序部署在一个容器中,并用一个浏览器输入DAO 对象的值。这么做会损失多少生产效率呢?于是目前最流行的获取连接的方式就是利用一个依赖注入框架来管理数据库连接。
使用Dependencylnjector 类代替依赖注入框架
使用c3p0连接池
package app10e.util;
import javax.sql.DataSource;
import app10e.action.GetProductsAction;
import app10e.action.SaveProductAction;
import app10e.dao.ProductDAO;
import app10e.dao.ProductDAOImpl;
import app10e.validator.ProductValidator;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mchange.v2.c3p0.DataSources;
public class DependencyInjector {
private DataSource dataSource;
/*Dependencylnjector 类硬编码了许多值,包括JDBC URL 和数据库的用户名及密码。
在框架中,属性值一般来自可编辑的配置文件,形式通常为XML 文档。在本例中我们把它简单化了。*/
public void start() {
// create dataSource
ComboPooledDataSource cpds = new ComboPooledDataSource();
try {
cpds.setDriverClass("com.mysql.jdbc.Driver");
} catch (Exception e) {
e.printStackTrace();
}
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/test");
cpds.setUser("root");
cpds.setPassword("root");
// to override default settings:
cpds.setMinPoolSize(5);
cpds.setAcquireIncrement(5);
cpds.setMaxPoolSize(20);
dataSource = cpds;
}
public void shutDown() {
// destroy dataSource
try {
DataSources.destroy(dataSource);
} catch (Exception e) {
e.printStackTrace();
}
}
/*为了从DependencyInjector 中获得对象,需调用其getObject 方法,传递目标对象的Class。
*DependencyInjector 在app10e 中支持以下类型: ProductValidator、ProductDAO 、 GetProductsAction 及SaveProductAction 。
例如,为了获得一个ProductDAO 实例,要通过传递ProductDAO .class 来调用getObject:*/
public Object getObject(Class type) {
if (type == ProductValidator.class) {
return new ProductValidator();
} else if (type == ProductDAO.class) {
return createProductDAO();
} else if (type == GetProductsAction.class) {
return createGetProductsAction();
} else if (type == SaveProductAction.class) {
return createSaveProductAction();
}
return null;
}
/*Dependencylnjector (及所有依赖注入框架)的魅力在于, 它所返回的对象也和依赖一同被注入了。如果某一个依赖中还具有其他的依赖,那么这个依赖的依赖也会随之一起被注入。*/
private GetProductsAction createGetProductsAction() {
GetProductsAction getProductsAction = new GetProductsAction();
// inject a ProductDAO to getProductsAction
getProductsAction.setProductDAO(createProductDAO());
return getProductsAction;
}
private SaveProductAction createSaveProductAction() {
SaveProductAction saveProductAction = new SaveProductAction();
// inject a ProductDAO to saveProductAction
saveProductAction.setProductDAO(createProductDAO());
return saveProductAction;
}
private ProductDAO createProductDAO() {
ProductDAO productDAO = new ProductDAOImpl();
// inject a DataSource to productDAO
productDAO.setDataSource(dataSource);
return productDAO;
}
}
通过使用依赖注入框架,可以获得如下好处:
1. 可以很方便的在xml中修改数据库连接设置
2. 方便进行dao的测试
3. dataSource是放在依赖注入框架中,可以很方便地先注入到dao对象中,再注入到action对象中,即某一个依赖中还具有其他的依赖,那么这个依赖的依赖也会随之一起被注入
通过案例1与案例3的对比,也可以体现出DataSource相比DriverManager有以下好处,所以应优先使用。
1. 应用程序不必硬编码一个驱动程序类。
2. 可以对数据源的属性进行修改,这就意味着当数据源或者驱动程序改变时,不必修改应用程序的代码。
3. 通过与中间层协同工作的DataSource类(比如使用了C3P0连接池)就可实现连接池和分布式事务处理。