Java于我,是一门很实用的语言。在大学里,起码在我们那时的大学,Java可不是一门登堂入室的语言,那是汇编语言,C语言,甚至Pascal语言的天下。不过Java却是学生时代兼职的第一语言(嘿嘿,真实用)。这对大学的语言教学真有那么一点讽刺意味。
当然,我觉得相比C/C++,Java做为后起之秀,吸取着以前语言的经验教训,而又没有类似C++必须兼容C的沉重负担,一切从新设计,这使它变成了一门简单而又强大的语言。还有,Java有着丰富的入门教程,新手很容易通过例子掌握一个具体的知识点。Java的IDE应该是做得比较出色的,如JBuilder,JDeveloper,Eclipse。
而正式工作后,在实际项目中使用C/C++的机会其实也很少,这是一个开发效率的问题,虽说C/C++的运行效率比Java更高,但现在人们更关注的,却是开发效率,还有应用总体的高性能与高可扩展性问题。对于开发并发应用,Java提供了内置的支持,而C/C++,线程安全,并发技术,对于一般的入门者那应该是很高阶的话题吧。
当然,Java的易于上手,并不表明新手们就能把它用得很好。例如,对于与数据库打交道,没有对数据库的基础知识那也是不行的,对于Java来说,很大一部分就在于如何使用JDBC开发出高性能的数据库Java应用程序。这里当然可以推而广之,对于其它的编程语言,这一问题也广泛存在,如如何使用OCI开发高性能的数据库C应用程序,Pro*C应用程序,PHP应用程序。
对于Oracle数据库而言,起码有以下几点是Java程序员所应该掌握的:
第一是连接池技术,数据库默认就提供了对高并发的支持。应用程序有两种极端,一种是全局只使用一个连接,这很幼稚,但这确实存在;另一种是只要需要操作数据库,就创建一个新的连接,从而会出现同一时刻,并发数据库连接数成千上万的情形。解决以上两种问题的方法就是使用连接池技术,并设置一个合理的连接数的最大值,这取决于数据库服务器的CPU个数,最佳的连接数一般应该在CPU个数的2到4倍左右。
UCP是Oracle提供的连接池驱动,以ucp.jar的形式提供。一个简单的示例如下:
?
1
2
3
4
5
6
7
8
9
|
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL("jdbc:oracle:thin:@//localhost:1521/orcl");
pds.setUser("scott");
pds.setPassword("tiger");
pds.setInitialPoolSize(20);
pds.setMinPoolSize(20);
pds.setMaxPoolSize(20);
Connection conn = pds.getConnection();
|
如果不用连接池,就不需要上面的ucp.jar,只需要一般的驱动,如ojdbc5.jar。
?
1
2
3
4
5
|
OracleDataSource ds = new OracleDataSource();
ds.setURL("jdbc:oracle:oci:@//localhost:1521/orcl");
ds.setUser("scott");
ds.setPassword("tiger");
Connection conn = pds.getConnection();
|
第二是语句池技术,解析SQL语句并生成执行计划,对于数据库引擎来说是很昂贵的操作。因此,在SQL语句中必须使用绑定变量而不是文本常量。另一方面,即使通过绑定变量避免了重新生成执行计划,但如果还存在软解析也会给系统带来额外的CPU消耗。如下面的语句:
?
1
2
3
4
5
6
7
8
|
while(true){
OraclePreparedStatement stmt = (OraclePreparedStatement) conn.prepareStatement("select ename from emp where empno = ?");
stmt.setString(1, "xxx");
OracleResultSet rs = (OracleResultSet) a_stmt.executeQuery();
while (rs.next()) {
System.out.println(rs.getString(1));
}
}
|
每执行一次conn.prepareStatement都会导致数据库里的一次软解析。解决方法有三种:
1. 把这个conn.prepareStatement移动while循环的外面。
2. 对于直接从DataSource获取的连接,启用连接的语句缓冲。
?
1
2
|
conn.setImplicitCachingEnabled(true);
conn.setStatementCacheSize(10);
|
3. 而对于UCP连接池,则可以通过设置连接池的属性启用语句的缓冲。
?
1
|
pds.setMaxStatements(10);
|
第三是各种泄露问题,连接泄露,语句游标泄露,锁泄露。Java语言只会帮助回收应用程序里的内存结构,却不会帮助你关闭曾经打开的数据库连接、游标,以及对事务的提交或回滚。下图是一个演示锁泄露的例子,粉红色区域面积越来越大,代表等待事件enq: TX - row lock contention越来越严重。一个常见的原因就是异常处理的代码中忘了对事务进行commit或者rolloback。
|