问题提出
应用系统中频繁的对数据库进行操作,每次操作都要进行“建立,操作,释放”等过程,导致性能低下,资源浪费。
数据库连接池(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冲突造成的。