问题提出 应用系统中频繁的对数据库进行操作,每次操作都要进行“建立,操作,释放”等过程,导致性能低下,资源浪费。
数据库连接池(connection pool )的工作原理 对于共享资源,有一个很著名的设计模式:资源池(Resource Pool )。该模式正是为了解决资源的频繁分配﹑释放所造成的问题。为解决上述问题,可以采用数据库连接池技术。数据库连接池的基本思想就是为数据库连接 建立一个“ 缓冲池” 。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“ 缓冲池” 中取出一个,使用完毕之后再放回去。
服务器自带的连接池
JDBC 的API 中没有提供连接池的方法。一些大型的WEB 应用服务器如BEA 的WebLogic 和IBM 的WebSphere 等提供了连接池的机制,但是必须有其第三方的专用类方法支持连接池的用法。
连接池关键问题分析
1 、并发问题
为了使连接管理服务具有最大的通用性,必须考虑多线程环境,即并发问题。这个问题相对比较好解决,因为Java 语言自身提供了对并发管理的 支持,使用synchronized 关键字即可确保线程是同步的。使用方法为直接在类方法前面加上synchronized 关键字,如:
public synchronized Connection getConnection ()
2 、多数据库服务器和多用户
对于大型的企业级应用,常常需要同时连接不同的数据库(如连接Oracle 和Sybase )。如何连接不同的数据库呢?我们采用的策略是: 设计一个符合单例模式的连接池管理类,在连接池管理类的唯一实例被创建时读取一个资源文件,其中资源文件中存放着多个数据库的url 地址 ( )﹑用户名( )﹑密码 ( )等信息。
3 、事务处理
我们知道,事务具有原子性,此时要求对数据库的操作符合“ALL-ALL-NOTHING” 原则, 即对于一组SQL 语句要么全做,要么全不做。
在Java 语言中,Connection 类本身提供了对事务的支持,可以通过设置Connection 的AutoCommit 属性为 false, 然后显式的调用commit 或rollback 方法来实现。但要高效的进行Connection 复用,就必须提供相应的事务支持机制。可采用 每一个事务独占一个连接来实现,这种方法可以大大降低事务管理的复杂性。
4 、连接池的分配与释放
连接池的分配与释放,对系统的性能有很大的影响。合理的分配与释放,可以提高连接的复用度,从而降低建立新连接的开销,同时还可以加快用户的访问速度。
5 、连接池的配置与维护
连接池中到底应该放置多少连接,才能使系统的性能最佳?系统可采取设置最小连接数(minConn )和最大连接数(maxConn )来控制 连接池中的连接。
具体的实现 javax.sql.RowSet 包添加了对缓冲数据源的支持,即可以将缓冲池缓冲的数据库连接看作是一个是实实在在的数据源服务来使用。RowSet 包提供了好几个接口用于处理数据库缓冲池,主要的接口有: 1 、DataSource 接口:DataSource 接口的实例对象代表了存在于中间层服务器中的缓冲数据源服务。使用它可以返还数据库缓冲池中现存的数据库连接,DataSource 接口的实例对象实际上是某个JNDI 服务的提供者,在使用它之前,该JNDI 服务对象必须先在中间层服务器环境中注册,并且和某个服务名绑定在一起,然后它才能被别的Java 应用程序调用。 2 、ConnectionPoolDataSource 接口:该接口可以用于创建一个被缓冲于缓冲池的数据库物理连接,它有可能会被DataSource 接口的实例对象调用。 3 、PooledConnection 接口:该接口代表被缓冲的数据库连接,它定义了一个getConnection() 方法使用这个方法可以返回java.sql.Connection 接口的实例对象。
在继续连接池之前,先讲一下JNDI 概述
我们大家每天都不知不觉地使用了命名服务。例如,当你在web 浏览器输入URL,http://java.sun.com 时,DNS(Domain Name System, 域名系统) 将这个符号URL 名转换成通讯标识(IP 地址)。命名系统中的对象可以是DNS 记录中的名 称、应用服务器中的EJB 组件(Enterprise JavaBeans Component) 、 LDAP(Lightweight Directory Access Protocol) 中的用户Profile 。 目录服务是命名服务的自然扩展。两者之间的关键差别是目录服务中对象可以有属性(例如,用户有email 地址),而命名服务中对象没有属性。因此,在 目录服务中,你可以根据属性搜索对象。JNDI 允许你访问文件系统中的文件,定位远程RMI 注册的对象,访问象LDAP 这样的目录服务,定位网络上的 EJB 组件。 对于象LDAP 客户端、应用launcher 、类浏览器、网络管理实用程序,甚至地址薄这样的应用来说,JNDI 是一个很好的选择。
JAVA 和tomcat 应用。
JAVA 中代码
Context ic = new InitialContext();
DataSource source = (DataSource) ic .lookup( "java:comp/env/jdbc/books" );
connection = source .getConnection();
Tomcat 的conf/context.xml 中的配置, 在根节点Context 中加入
< Resource name ="jdbc/books" auth ="Container" type ="javax.sql.DataSource" maxActive ="100" maxIdle ="30" maxWait ="10000" username ="admin" password ="admin" driverClassName ="com.microsoft.jdbc.sqlserver.SQLServerDriver" url ="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=books" />
属性名称
说明
name
指定Resource 的JNDI 名称
auth
指定管理Resource 的Manager (Container: 由容器创建和管理|Application :由Web 应用创建和管理)
type
指定Resource 所属的Java 类
maxActive
指定连接池中处于活动状态的数据库连接的最大数目
maxIdle
指定连接池中处于空闲状态的数据库连接的最大数目
maxWait
指定连接池中的连接处于空闲的最长时间,超过这个时间会抛出异常,取值为-1 ,表示可以无限期等待
补充:如果是Mysql 的数据库
username="ccs" password="secret" url="jdbc:mysql://localhost:3306/ccs" driverClassName="com.mysql.jdbc.Driver" maxActive="20" maxIdle="10"/>
把数据库驱动(mssqlserver2.jar) 的.jar 文件,加入到Tomcat 的commonlib 中
注意:在Tomcate5.5 以下的版本,还需要项目工程的web.xml 中添加,资源引用
jdbc/books
javax.sql.DataSource
Container
getClass()
这是java 中的反射机制, 当一个class 生成实例的时候JVM 中都会有一个class 来描述这个class 的属性, 你可以通过实例的.getclass() 这个方法来拿到这个class
InputStream is = getClass().getResourceAsStream("/db.properties");
这个方法实际上是通过ClassLoader 来工作的 所以要把db.properties 放到ClassPath 里面去
注意:src/ 目录下的文件,部署后,会放到WEB-INFclasses 下
Class
将forName(className) 这个名为className 的类装入JVM 这样就可以动态的加载类,通过Class 的反射机制可以获得此类的一些信息
Class. forName(); 的好处在于你不用编译/ 运行时依赖于特定的 JDBC Driver 库, 也就减少了项目代码的依赖性. 而且也很容易改造成从配置文件读取JDBC 配置, 从而可以在运行时动态更换JDBC/DB.
实际上这样也可以的。
com.mysql.jdbc.Driver driver = new com.mysql.jdbc.Driver();
//or :
//new com.mysql.jdbc.Driver();
String url = "jdbc:mysql://127.0.0.1/test?useUnicode=true&characterEncoding=utf-8";
String user = "";
String psw = "";
Connection con = DriverManager.getConnection(url,user,psw);
Result 与ResultSet 的区别
在开发过程中,我们不能返回ResultSet 对象, 连接一旦断开,在连接上建立的会话和在会话上建立的结果集都会自动关闭. 所以我们必须开发类似于返 回List 的接口. 但是针对通用函数,List 是无能为力的,我们要返回比List 更通用的,jstl 中的Result 可以完成此任务.
解析Java 对象的equals() 和hashCode() 的使用。
在Java 语言中,equals() 和hashCode() 两个函数的使用是紧密配合的,你要是自己设计其中一个,就要设计另外一个。在多数情况 下,这两个函数是不用考虑的,直接使用它们的默认设计就可以了。但是在一些情况下,这两个函数最好是自己设计,才能确保整个程序的正常运行。
需要自己设计的场合:当一个对象(比如,自定义的类)加入到一个集合中时,并且要对集合中的对象进行添加,搜索,删除等操作时。可能需要重写这两个方法。
equals():
它是用于进行两个对象的比较的,是对象内容的比较,当然也能用于进行对象参阅值的比较。
对象内容的比较才是设计equals() 的真正目的,Java 语言对equals() 的要求如下,这些要求是必须遵循的。否则,你就不该浪费时间:
· 对称性:如果x.equals(y) 返回是“true” ,那么y.equals(x) 也应该返回是“true” 。
· 反射性:x.equals(x) 必须返回是“true” 。
· 类推性:如果x.equals(y) 返回是“true” ,而且y.equals(z) 返回是“true” ,那么z.equals(x) 也应该返回是“true” 。
· 还有一致性:如果x.equals(y) 返回是“true” ,只要x 和y 内容一直不变,不管你重复x.equals(y) 多少次,返回都是“true” 。
· 任何情况下,x.equals(null) ,永远返回是“false” ;x.equals( 和x 不同类型的对象) 永远返回是“false” 。
hashCode(): JDK API 1.6.0 的解释: hashCodepublic int hashCode () 返回该对象的哈希码值。支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。 hashCode 的常规协定是:
· 如果x.equals(y) 返回“true” ,那么x 和y 的hashCode() 必须相等。
· 如果x.equals(y) 返回“false” ,那么x 和y 的hashCode() 有可能相等,也有可能不等。
第二规则,可能是由于hash 冲突造成的。