sparta-紫杉 2010-9-25 10:49
一、开发及运行环境
eclipse3.4.2, weblogic8.12, oracle9.2, jdk1.4.2, struts1框架。
二、背景
今天在对项目代码review时,发现存在一个问题,在某一个Dao的代码里面,多了一个得到原生Connection的getConnection()方法,方法如下:
/** *//**
* 得到一个数据库的连接
*
* @return 返加Connection对象
*/
public java.sql.Connection getConnection() {
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
conn = DriverManager.getConnection(
"jdbc:oracle:thin:@192.168.0.72:1521:ora9", "kfzx", "kfzx");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
上述代码比较好理解,返回一个原生的Java Connection对象,但不免生出几点疑问:
1、整个项目采用Weblogic的连接池并通过DataSource获取连接,为何还要单独独立出一个连接代码来呢?
2、这个独立出的连接有什么具体的作用吗? 难道Weblogic的连接池的DataSource连接有一些无法完成的功能?
3、通过Weblogic的DataSource建立的连接与Java原生Connection有何区别?
下面给出Weblogic的连接池并通过DataSource获取连接的ConnectDB类:
/** *//**
* 通过获取配置文件中定义的DataSourceName获取连接(通过Weblogic维护的连接池)。
*/
public class ConnectDB {
private static DataSource dataSource = null;
private DataSource currentDataSource = null;
public ConnectDB() {
if (dataSource == null) init();
currentDataSource = dataSource;
}
synchronized private static void init() {
try {
logger.warn("******ConnectDB()..init() NOW******");
javax.naming.Context ctx = new javax.naming.InitialContext();
String dataSourceName = AppinitWebBean.getInitKeyValue("dataSourceName");
if (dataSourceName == null) dataSourceName = "kfzx";
dataSource = (DataSource) ctx.lookup(dataSourceName);
logger.warn("******ConnectDB()..init() OK******");
}
catch (javax.naming.NamingException e) {
logger.error("--ConnectDB.getConnection()--",e);
}
}
public Connection getConnection() throws SQLException {
try {
return currentDataSource.getConnection();
}
catch (SQLException e) {
logger.error("getConnection()", e);
}
return currentDataSource.getConnection();
}
}
带着上述问题,对代码进行分析,得到如下结果:
1、独立出的连接代码是单独为处理CLOB类型的字段数据内容而建立的,CLOB不能使用传统的insert into 方法插入,必须通过处理CLOB的专用方法。
2、通过ConnectDB.java中的getConnection()方法虽然也能得到Java原生的Connection,但是在存取CLOB的字段时,总是出现“ClassCastException”错误。
出错代码如下:
oracle.sql.CLOB clob = ((OracleResultSet) rss).getCLOB(1);
原因是与Weblogic中的对Clob处理的代码有转型上的错误。
3、经过分析发现,应该就是由于使用通过ConnectDB.java建立的Weblogic的DataSource连接池无法处理OracleResultSet类型的CLOB数据。
为了验证自己的分析,将由ConnectDB.java中的getConnection()方法换为Dao代码中的getConnection(),
oracle.sql.CLOB clob = ((OracleResultSet) rss).getCLOB(1);代码是完全可以正常运行的。
所以,开发人员在Weblogic的连接池之外,又重新建立了一个Java原生的Connection,用以专门处理CLOB的字段内容,似乎是无奈之举。
但不难发现,上述代码有下面的几个缺点:
1、在项目中建立两套连接(并且两套连接指向同一个数据库)是非常不规范的处理方式,况且其中之一并非Weblogic管理的连接池,而是一个Java原生Connection,不能利用Weblogic的连接池的优点。
2、管理两套连接除了带来管理上的困难,而且带来编码上的麻烦。
3、使用Java原生Connection是硬编码方式,必须在不同的数据库连接环境(数据库连接的字串、SID、IP等)中硬编码切换,应用网和本地来回切换,若不注意,则会造成错误。
通过以上的缺点,笔者认为应该充分利用Weblogic的DataSource数据源及连接池,在整个项目中仅保留一套Connection,因此针对该问题进行研究,
使Weblogic的DataSource数据源及连接池也能够连接正确处理CLOB。
三、分析研究
其实在ConnectDB.java中通过Weblogic的DataSource生成的Connection和Java原生Connection本质上是相同的,处理CLOB出错的原因并非在Connection,
而在于通过数据库连接生成的结果集(ResultSet),Java原生ResultSet和Weblogic对于ResultSet处理上是不同的,这里就是产生CastException的原因,
这也是对oracle.sql.CLOB clob = ((OracleResultSet) rss).getCLOB(1);这行代码进行研究后得出的结果。
weblogic服务器中,在通过datasourse获取connection,CLOB字段取出来的就不是oracle.sql.CLOB类型,而是weblogic封装过的OracleThinClob类型,
执行CLOB oCLOB = (CLOB) rs.getClob(1);所以cast的时候肯定会出错,出现ClassCaseException异常。
换言之,通过Weblogic的DataSource连接获得的CLOB的ResultSet是不能转型为java原生CLOB的ResultSet的。 也就是说,除了Java原生ResultSet之外,
每个应用服务器(Tomcat,weblogic,jboss等)都应该有自己处理CLOB型ResultSet的方式方法,并且不能通用(至少不可以转型)。
目前,笔者见到的有三种处理CLOB型ResultSet的方法:
1、Java原生ResultSet,通常的代码为: oracle.sql.CLOB clob = ((OracleResultSet) rss).getCLOB(1);
2、Tomcat处理CLOB型ResultSet:须将lib下的classes12.jar(oracle包)删除,但程序不需要改动。
3、通过Weblogic的DataSource获得的连接池,可采用如下代码:
weblogic.jdbc.vendor.oracle.OracleThinClob clob = (weblogic.jdbc.vendor.oracle.OracleThinClob)rss.getClob(1);
当然,要获得weblogic.jdbc.vendor.oracle.OracleThinClob接口还必须将D:\bea812\weblogic81\server\lib下的weblogic.jar
拷贝到项目的E:\eclipse3.4.2\workspace\sykf\webapp\WEB-INF\lib下,才能正常使用。
鉴于本项目使用Weblogic做应用服务器,并且需要充分利用Weblogic的连接池建立的数据库连接,因此,笔者采用第三种方式来解决这个问题,也就是说将
D:\bea812\weblogic81\server\lib下的weblogic.jar拷贝到项目的E:\eclipse3.4.2\workspace\sykf\webapp\WEB-INF\lib下。
四、先为weblogic.jar减减肥吧
要知道,从D:\bea812\weblogic81\server\lib下的weblogic.jar获得的该jar容量是非常大的,达36M之多,这么大的jar包放到项目lib下有些笨拙,这个不要紧,可以对之进行减肥,将里面不需要的包删除掉就可以了,具体步骤如下:
1)、解压该jar包,成为一个文件夹。
2)、将里面除jdbc之外的包全部删除。
3)、重新压缩成jar包。
笔者通过上述处理之后,成功将36M体积的Weblogic.jar减肥为713K。
在这里可能有读者会有如下疑问:
你的lib下有weblogic.jar包,并且Weblogic下也有weblogic.jar包,并且两个jar中包含的jdbc.vendor.oracle.OracleThinclob相同,两者不会冲突吗?
这个不必担心,因为包名类名虽然都相同,即使jvm也没法区分,那JVM在处理时就只有第一个包被引入(在classpath路径下排在前面的包),
第二个包会在classloader加载类时判断重复而忽略。
五、下面给出Weblogic的Datasource连接的Resultset写CLOB字段的代码
写方法:
public boolean importQKSM( Object[] obj){
Connection conn = null;
ConnectDB connectDB = new ConnectDB();
try {
conn = ( Connection ) connectDB.getConnection();
} catch (SQLException e1) {
e1.printStackTrace();
return false;
}
ResultSet rss = null;
PreparedStatement stmt = null;
//得到维修费的"情况说明"
String qksm = obj[1].toString().trim();
//得到维修费业务的业务id
String wxfId = obj[0].toString().trim();
// 向cbgl_wxf_qksm表中添加大数据
if ( null != qksm && !"".equals(qksm)) {
String sql1 = " insert into cbgl_wxf_qksm (id,qksm) "
+ " values ('" + wxfId + "',empty_clob()) ";
// 重新取出CLOB数据,赋值
String sql2 = "select qksm from cbgl_wxf_qksm where id='"
+ wxfId + "' for update";
try {
conn.setAutoCommit(false);
stmt = conn.prepareStatement(sql1);
stmt.executeUpdate(sql1);
rss = stmt.executeQuery(sql2);
if (rss.next()) {
// 得到流
weblogic.jdbc.vendor.oracle.OracleThinClob clob =
(weblogic.jdbc.vendor.oracle.OracleThinClob)rss.getClob(1);
clob.putString(1, qksm );
String sqlUpd = "update cbgl_wxf_qksm set qksm = ? " +
"where id ='" + wxfId + "'";
stmt = conn.prepareStatement( sqlUpd );
stmt.setClob( 1, (Clob) clob );
stmt.executeUpdate();
// 正式提交
conn.commit();
conn.setAutoCommit(true);
}
} catch (Exception e) {
logger.error(this, e);
//出错后回滚事务
try {
conn.rollback();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return false;
} finally {
try {
if (rss != null)
rss.close();
} catch (SQLException e) {
}
try {
if (stmt != null)
stmt.close();
} catch (SQLException e) {
}
try {
if (conn != null && !conn.isClosed())
conn.close();
} catch (SQLException e) {
}
}
} else {
// 写入一条空记录到表cbgl_wxf_qksm中
String sql1 = " insert into cbgl_wxf_qksm (id,qksm) "
+ "values ('" + wxfId + "',empty_clob()) ";
try {
conn.setAutoCommit(false);
stmt = conn.prepareStatement(sql1);
stmt.executeUpdate(sql1);
} catch (Exception e) {
logger.error(this, e);
return false;
}finally{
try {
if (stmt != null)
stmt.close();
} catch (SQLException e) {
}
try {
if (conn != null && !conn.isClosed())
conn.close();
} catch (SQLException e) {
}
}
}
return true;
}
读方法:
// 根据wxfid得到维修费的model
public WxfModel getWxfModel(long wxfid) {
WxfModel wxfModel = new WxfModel();
String qksm = "";
ResultSet rss = null;
Statement stmt = null;
//获得数据库连接,以Weblogic连接池代替硬编码获得Connection方式 sparta 10/9/25
try {
conn = connectDB.getConnection();
} catch (SQLException e1) {
e1.printStackTrace();
}
String sql1 = "select * from cbgl_wxf where wxfid='" + wxfid + "'";
ConnectDB connectDB = new ConnectDB();
CmResultSet rs = connectDB.getRs(sql1, null);
try {
if (rs.next()) {
wxfModel = WxfDao.rsToModel(rs);
}
} catch (Exception e) {
e.printStackTrace();
}
String sql2 = "select qksm from cbgl_wxf_qksm where id='" + wxfid + "'";
try {
stmt = conn.prepareStatement(sql2);
rss = stmt.executeQuery(sql2);
if (rss.next()) {
Clob clob = rss.getClob(1);
Reader in = clob.getCharacterStream();
BufferedReader br = new BufferedReader(in);
String qksmBlock = "";
try {
while (( qksmBlock = br.readLine()) != null) {
qksm += qksmBlock;
}
} catch (IOException e) {
e.printStackTrace();
}
wxfModel.setQksm(qksm);
}
} catch (SQLException e) {
logger.error(this, e);
}finally{
//关闭ResultSet, Statement, Connection , sparta 10/9/25
try{
if( rss != null) rss.close();
}catch( Exception ex ){}
try{
if( stmt != null) stmt.close();
}catch( Exception ex ){}
try{
if( conn != null && !conn.isClosed() ) conn.close();
}catch( Exception ex ){}
}
return wxfModel;
}
六、另外再给出Tomcat的连接池处理ResultSet的CLOB字段的方法
代码与Java原生ResultSet写CLOB型字段的代码一样,但是需要注意,要将项目的lib文件夹下的classes12.jar删除才不会出现ClassCastException错误。
-东营 sparta-紫杉 原创,转载请注明出处 :)
http://www.blogjava.net/SpartaYew/
SpartaYew@163.com
QQ:22086526