1 金蝶Apusic应用服务器的数据源管理
金蝶Apusic应用服务器支持业界主流的各种数据库,在Apusic应用服务器之内进行数据源的配置与使用都非常简单,同时,它提供了许多增值特性,能够为应用的正常运行提供额外的保障。
1.1 数据库连接池的逻辑连接与物理连接
我们注意到:java.sql.Connection是一个Interface,那么,真正实现这个接口的类是什么呢?
我们可以做一个简单的测试案例,在普通的Java Application中,调用如下方法:
|
public void showConnection() {
Connection conn = null;
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
conn = DriverManager.getConnection(
"jdbc:oracle:thin:@localhost:1521:KEVINORA",
"system", "manager");
System.out.println("Connection Class is:" + conn.getClass().getName());
} catch (Exception e) {
e.printStackTrace();
} finally {
DbUtils.closeQuietly(conn);
}
}
|
|
<示例代码五>
|
得到的输出结果是:Connection Class is:
oracle.jdbc.driver.T4CConnection
而在Apusic应用服务器中运行如下方法:
|
public void showConnection() {
Connection conn = null;
try {
Context ctx = new InitialContext();
ds = (DataSource) ctx.lookup("jdbc/oracle");
conn = ds.getConnection();
System.out.println("Connection Class is:" +
conn.getClass().getName());
} catch (Exception e) {
e.printStackTrace();
} finally {
DbUtils.closeQuietly(conn);
}
}
|
|
<示例代码六>
|
得到的输出结果是:Connection Class is: com.apusic.jdbc.adapter.ConnectionHandle
明明用相同的JDBC Driver连接同一个数据库,为什么取得的Connection却是不同的类呢?事实上,通过Apusic应用服务器获得的数据库连接其实只是一个逻辑连接,真正的物理连接隐藏在该逻辑连接之内,这是一个典型的Delegate模式,而恰恰是这个模式,通过 Apusic应用服务器对数据源进行管理,将给我们的应用开发带来很多好处:
1.2 当事务结束以后,在该事务上下文中申请的物理连接,都将主动释放
我们以一个最简单的Stateless Session Bean为例:
|
public class SimpleBean implements SessionBean {
public void foo() {
Connection conn = null;
try {
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("jdbc/oracle");
conn = ds.getConnection();
System.out.println("not release connection");
} catch (Exception e) {
e.printStackTrace();
} finally {
// Not close the connection
// DbUtils.closeQuietly(conn);
}
}
}
|
|
<示例代码七>
|
SimpleBean中的foo方法的事务属性设置为Required,在该方法中,我们申请了一个数据库连接,但并没有释放它,在运行之前,我们通过SQLPlus观察Oracle数据库的Session,得到的结果是:
|
|
|
<图一 执行方法之前的Oracle Session>
|
而在执行完SimpleBean的foo方法之后,我们再次观察Oracle数据库的Session,得到的结果是:
|
|
|
<图二:执行方法之后的Oracle Session>
|
由此,我们可以得知:即便由于程序的书写错误,没能够释放申请的数据库连接,但Apusic应用服务器在事务完成之后,能够把该事务上下文中申请的物理连接主动释放,这对提升应用的容错性带来一定的好处。
1.3 当jsp/servlet运行结束以后,在jsp/servlet中申请的物理连接,都将主动释放
同事务中申请的数据库连接会主动释放一样,在jsp/servlet中申请的数据库物理连接,当jsp/servlet运行完毕以后,如果用户没有释放这些连接,Apusic应用服务器也将予以主动释放。读者可以尝试自己做一个案例:在jsp中申请一个连接,故意不释放,在jsp执行完毕以后,可以通过SQLPlus或者Apusic性能监控工具,查看连接是否已经被应用服务器主动释放。
由上述两节内容我们可以看到,Apusic应用服务器能够有效避免2.1节中所描述的问题。
3.4 Connection Sharing:同一个事务上下文中申请的物理连接可以共享
通过共享连接可以更有效地使用资源及提高性能,并且可以防止连接之间的资源锁定问题。
例如两个EJB组件A和B,它们的事务属性都设置为Required。在调用EJB A的方法时打开了一个数据库连接,并对数据库中的某个表进行了更新操作,而在关闭连接之前EJB A调用了EJB B的某个方法,同样EJB B打开同一个数据库的连接,也对数据库中同一个表进行了更新操作。倘若没有连接共享机制,这两个连接指向的是两个不同的物理连接,在其上执行的数据库操作将会互相锁定,而这种死锁状态是无法恢复的。现在有了连接共享机制可以有效地解决这个问题。在EJB A和B中所获得的连接对象实际上都指向同一个物理连接。这一个过程可以简单描述如下:
|
con1 = getConnection();
Transaction.begin
perform database operation on con1
con2 = getConnection();
perform database operation on con2
con2.close();
Transaction.commit();
con1.close();
|
|
<示例代码八>
|
无论两个连接是在事务边界之内或之外打开和关闭都没有问题。只有在一个事务边界之内连接才会被共享,如果一个连接是在事务边界之外打开的,那么在事务开始时会将此连接参与到事务中,并找到一个具有正确事务场景的物理连接和连接对象相关联。在离开事务场景之后如果连接对象仍未关闭,则将其关联到一个不具有事务场景的物理连接。
可以在部署描述中指定一个资源引用的res-sharing-scope属性来允许或禁止连接共享,属性值shareable为允许共享,unshareable为禁止共享,缺省情况下为允许共享。
回到2.2节中Customer那个测试案例,我们已经说过,Customer的sequencePlus方法、getSequenceCurrentVal方法、以及addCustomer方法,需要放在一个事务中处理。但在这三个方法中,使用的是不同的Connection,而由于Connection的隔离级别,将导致插入T_CUSTOMER表中的ID主键将重复,最终导致事务回滚。利用Apusic应用服务器连接共享特性,能够很好的解决这个问题。也就是说:虽然这三个方法申请的逻辑连接是不同的,但逻辑连接内部所使用的物理连接是同一个,这样,将保证不同方法中对数据库的操作结果相见可见,从而保证事务的正常提交。
举例如下:假设在一个jsp文件中,这样调用:
在上述代码中,通过UserTransaction 启动一个事务,然后在该事务上下文中,增加一笔Customer的记录,我们发觉,在不需要更改Customer类的情况下,上述方法能够正常完成。
由此可以得知:在Apusic应用服务器中进行应用的开发,我们无需因为考虑数据库Connection的隔离级别而影响我们对系统的面向对象的分析方法,Apusic应用服务器将替我们保证在同一事务上下文中,使用相同的物理连接。
通过Apusic应用服务器的这个特性,能够有效的解决2.2节中描述的问题。
1.2 Lazy Connection Association Optimization: 数据库连接延迟关联的优化机制
在3.1节中我们谈到:通过Apusic应用服务器管理的数据库连接分逻辑连接与物理连接,物理连接隐藏在逻辑连接的背后。那么,逻辑连接何时与一个真正的物理连接相关联的呢?在关联的过程之中,Apusic应用服务器又提供了哪些优化机制呢?举例如下:
J2EE组件可能会将连接对象保存在其实例变量中从而可以在多个事务之间重复使用,但是如果这个组件在使用一次之后就很少再被用到,那么系统资源将会被组件白白占用而得不到释放,当连接池被占满时就再也无法获得新的连接。Lazy Connection Association Optimization是这样一种机制,当J2EE组件方法调用完成时,释放连接对象所指向的物理连接以供其他组件使用,连接对象进入一个Inactive状态,在这个状态下它不和任何物理连接相关联。当J2EE组件需要使用该连接对象时,容器将其激活,将其和一个实际的物理连接相关联。这一过程对于应用组件来说是完全透明的。J2EE程序员经常犯的一个错误是忘记关闭连接,特别是发生异常时没有执行正确的清理,过去我们解决这一问题是在方法调用完成时强制关闭所有的连接,现在有了Lazy Connection Association Optimization机制可以更完美地解决这一问题。
Connection Sharing和Lazy Connection Association Optimization是同时起作用的,例如,当一个连接被激活时,它将被包含在当前事务场景中,并与同一事务场景中的其他逻辑连接共享同一个物理连接。
我们在2.3节中强调:将Connection作为成员变量是一种糟糕的设计模式,但同时,我们也看到:哪怕用户旧有系统中存在这样的用法,Apusic应用服务器也能够很好的解决由于这种糟糕的设计所带来的缺陷。
2 总结
本文首先与读者分析了一些错误或者不当的数据库资源使用方法,然后简要介绍了金蝶Apusic应用服务器在数据源管理上的一些特性。这些特性,对应用的健壮性及容错性带来一定的好处。但需要再次提醒的是:应用服务器提供的一些增值特性,仅能够当作保障我们应用正常运行的最后一道屏障,我们切不可依赖于这些特性而忽视程序自身的编码质量。一个J2EE应用能否正常的运行,程序自身的设计与编码永远是主要因素。