Java Database Connectivity(JDBC)JDBC以一种统一的方式来对各种各样的数据库进行存取。和ODBC一样,JDBC为开发人员隐藏了不同数据库的不同特性。另外,由于JDBC建立在Java的基础上,因此还提供了数据库存取的平台独立性。JDBC定义了4种不同的驱动程序:JDBC-ODBCBridge、JDBC-native driverbridge、JDBC-networkbridge、PureJavadriver。在一个多层的企业级应用中,更大的可能是在客户端和一个EJB进行通信,采用EJB建立数据库连接。为了实现和改进可伸缩性和系统性能, 也可以采用连接缓冲池(connection pool)优化数据库连接。
Java Naming and Directory Interface (JNDI)JNDI API被用于执行名字和目录服务。它提供了一致的模型来存取和操作企业级的资源如DNS和LDAP,本地文件系统,后者在应用服务器中的对象。在JNDI中,在目录结构中的每一个结点称为context。每一个JNDI名字都是相对于context的。应用可以通过这个初始化的context经有这个目录树来定位它所需要的资源或对象。(这句话什么意思呢?是这样的:
JNDI 对应于J2SE中的javax.naming包,这套API的主要作用在于:它可以把Java对象放在一个容器中(JNDI容器),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需通过名称检索即可。
其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。
)
JNDI的结构:
JNDI的结构自上而下包含3个主要层次:
1)应用层:该层使用JNDI API
2)JNDI API层:该层定义一组类和接口,可以用来访问任何命名或目录服务。
JNDI类库包含在 java2 SDK中,主要包含如下程序包:
1、用于命名操作的javax.naming;
2、用于目录操作的javax.naming.directory
3、在命名和目录服务器中请求事件通知的javax.naming.event;
4、提供LDAP支持的javax.naming.ldap
3)实现层:该层为JNDI SPI API及其具体实现,它包括以下几个命名/目录服务器提供者:LDAP,CORBA,RMI,DNS等。
JNDI命名与上下文:
JNDI命名服务对象映射为名称,这种映射可以获得远程对象的引用,以及调用远程对象的方法,而不必知道该对象在网络上的物理地址。可以使用javax.naming程序包来访问JNDI命名服务,这个程序包包括如下命名方法: 获得初始上下文,把对象绑定到名称上,查找对象等。
在命名系统中,上下文作为查找对象过程的起点。在文件系统中,每个目录都可以看做是一个上下文。JNDI API定义了Context接口,该接口在JNDI中起着重要作用。JNDI还定义了InitialContext类,要执行任何操作,必须建立一个InitialContext。
JNDI相对与JDBC来说是他的灵活性,程序员不需要关心“具体的数据库后台是什么?JDBC驱动程序是什么?JDBC URL格式是什么?访问数据库的用户名和口令是什么?”等等这些问题,程序员编写的程序应该没有对 JDBC 驱动程序的引用,没有服务器名称,没有用户名称或口令 —— 甚至没有数据库池或连接管理。而是把这些问题交给J2EE容器来配置和管理(比如weblogic),程序员只需要对这些配置和管理进行引用即可。
理解连接池
连接池技术能显著增加Java应用程序的性能,同时还能降低资源使用率。连接池技术的主要优点包括:
· 缩短了连接创建时间
与其他数据库相比,MySQL提供了快速的连接设置功能,连接时间通常不是问题,但创建新的JDBC连接仍会导致联网操作和一定的IDBC驱动开销,如果这类连接是“循环”使用的,使用该方式,可避免这类不利因素。
· 简化的编程模型
使用连接池技术时,每个单独线程能够像创建了自己的JDBC连接那样进行操作,从而允许使用直接的JDBC编程技术。
· 受控的资源使用
如果不使用连接池技术,而是在每次需要时为线程创建新的连接,那么应用程序的资源使用将十分浪费,而且在负载较重的情况下会导致无法预期的结果。连接池能够使性能最大化,同时还能将资源利用控制在一定的水平之下,如果超过该水平,应用程序将崩溃而不仅仅是变慢。
幸运的是,Sun公司通过JDBC-2.0“可选”接口,完成了JDBC中连接池概念的标准化实施,所有主要应用服务器均实施了能够与MySQL Connector/J一起良好工作的这类API。
通常,你可以在应用服务器的配置文件中配置连接池,并通过Java命名和目录接口(JNDI)访问它。在下面的代码中,介绍了在J2E应用服务器上运行的应用程序中使用连接池的方法:
示例26.12. 与J2EE应用服务器一起使用连接池
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import javax.naming.InitialContext;
import javax.sql.DataSource;
public class MyServletJspOrEjb {
public void doSomething() throws Exception {
/*
* Create a JNDI Initial context to be able to lookup the DataSource
*
* In production-level code, this should be cached as an instance or static variable, as it can be quite expensive
* to create a JNDI context.(创建一个JNDI context是非常昂贵的资源耗费)
*
* Note: This code only works when you are using servlets or EJBs in a J2EE application server. If you are
* using connection pooling in standalone Java code, you will have to create/configure datasources using whatever
* mechanisms your particular connection pooling library provides.
*/
InitialContext ctx = new InitialContext();
/*
* Lookup the DataSource, which will be backed by a pool that the application server provides. DataSource instances
* are also a good candidate for caching as an instance variable, as JNDI lookups can be expensive as well.
*/
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/MySQLDB");
/*
* The following code is what would actually be in your Servlet, JSP or EJB 'service' method...where you need
* to work with a JDBC connection.
*/
Connection conn = null;
Statement stmt = null;
try {
conn = ds.getConnection();
/*
* Now, use normal JDBC programming to work with MySQL, making sure to close each resource when you're
* finished with it, which allows the connection pool resources to be recovered as quickly as possible
*/
stmt = conn.createStatement();
stmt.execute("SOME SQL QUERY");
stmt.close();
stmt = null;
conn.close();
conn = null;
} finally {
/*
* close any jdbc instances here that weren't explicitly closed during normal code path, so
* that we don't 'leak' resources...
*/
if (stmt != null) {
try {
stmt.close();
} catch (sqlexception sqlex) {
// ignore -- as we can't do anything about it here
}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (sqlexception sqlex) {
// ignore -- as we can't do anything about it here
}
conn = null;
}
}
}
}
如上例所示,获得JNDI InitialContext并查找到数据库后,其余代码与过去在JDBC编程中使用的类似。
使用连接池时需要牢记的最重要事项是,无论在代码中出现了什么(异常、控制流等),连接以及由连接创建的任何部分(语句、结果集等)均应被关闭,以便能再次使用它们。如不然,它们将纠缠在一起,在最好的情况下,意味着它们所代表的MySQL服务器资源(缓冲区、锁定、套接字等)可能会捆绑一段时间,在最坏的情况下,可能会导致永久捆绑。
连接池的最佳大小是什么?
与所有其他配置经验规则一样,回答是“它取决于具体情况”。尽管最佳大小取决与预期的负载和平均的数据库事务时间,最佳的连接池大小小于你的预期。例如,如果使用的是Sun公司的Java Petstore Blueprint应用程序,对于包含15~20个连接的连接池,使用MySQL和Tomcat,在可接受的相应时间下,可服务于中等程度的负载(600个并发用户)。
要想确定用于应用程序的连接池大小,应使用诸如Apache Jmeter或The Grinder等工具创建负载测试脚本,并对应用程序进行负载测试。
确定出发点的一种简单方法是,将连接池的最大连接数配置为“无限”,运行负载测试,并测量最大的并发连接数。随后,应进行反向操作,确定出使应用程序具有最佳性能的连接池的最小和最大值。
什么是命名服务?
命名服务在一个集中位置存储信息,这样用户、计算机和应用程序便可通过网络进行通信。命名服务使用名称查找对象,而不是对象的物理ID。比如说在tomcat中查找JDBC数据源不是通过数据源的直接引用查找,而是通过JNDI与数据源绑定的名称查找(这句话,验证了我之前的猜想:JNDI这种命名服务接口,绑定了使用jdbc配置的数据源,正因为有了java命名和目录服务 即JNDI,所以用户只需要知道这个数据源的名称,就能调用该数据源,完成对数据库的连接操作),比如说通过DNS可以使用域名来访问Web应用,而不是直接通过输入Web服务器的IP地址来访问,这里的数据源的引用和IP地址都可以看作是资源的物理ID。命名服务有DNS,NIS,LDAP等。DNS称为域名系统,开发 DNS 后,网络中的计算机可由通用名称而非 Internet (IP)地址来标识。NIS称为网络信息服务,NIS 的主要作用是通过对各种网络信息进行集中控制来更好地管理网络。NIS 存储有关网络、计算机名称和地址、用户、以及网络服务的信息。
什么是目录服务?
目录服务是命名服务的扩展,目录服务的对象不单有名称,还有属性,既可以通过名称查找对象,也可以通过属性查找到一批对象然后筛选出自己需要的对象。目录服务应该比命名服务更广发,它可以将整个网络中的资源和设备组织起来,形成一个大的目录,然后通过目录就可以定位资源和设备,实现集中管理。
tomcat或Jboss的数据源属于什么服务?
属于命名服务,因为tomcat和Jbosss容器提供了命名服务或者目录服务,然后编程人员想用使用它们的服务时假如没有JNDI则必须遵照它们定的规范来开发,显然两个容器的使用服务的方法是不一样的,所以才有JNDI的规范。也就是说命名和目录服务并不是因为编程而存在的,而是Web容器本身就具有的。