您的位置:首页 > 数据库 > Oracle

Weblogic、Tomcat、Oracle与Clob

2016-03-17 16:09 711 查看
http://www.blogjava.net/SpartaYew/archive/2011/05/18/350480.html
http://zzldn.iteye.com/blog/711199
一、开发及运行环境

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错误。

开发项目一般都喜欢用Tomcat做测试环境,开发好后需要移到weblogic下,这时候发现问题不断。

由于weblogic的严密性,程序中有些数据的转换出现错误,一些小问题倒是好解决,知道在处理BLOB字段类型数据时走了不少弯路,下面就说说这个BLOB字段在这两种web服务器类型下的不同处理情况。

我的大内容文字都是存在CLOB字段里,BLOB用于存储图片。

一、Tomcat环境下的BLOB处理

在Tomcat下的BLOB处理相对简单很多,在Tomcat下从表中搜到的BLOB字段数据都只需简单强制转化就可以了,看源码:

(1)获取BLOB数据

public BLOB getTMIMage(String TM_code,Connection con) {

BLOB blob=null;

try{

ArrayList paramList=new ArrayList();

String sql="select FIMAGE from BM_TTMIMAGE where FTMID=?";

paramList.add(TM_code);

ResultSet rst= doSelect(sql, paramList, con); //doSelect(sql, paramList, con); 封装处理数据的过程

if(rst.next()){

blob=(BLOB)rst.getObject(1);

}

rst.close();

}catch(Exception e){e.printStackTrace();}

return blob;

}

blob=(BLOB)rst.getObject(1); 这样的获取方法应该是最好的,其实在默认的时候应该是blob=(BLOB)rst.getBlob(1) 的, 但有些环境下rst.getBlob(1)会报错,我个人还是喜欢blob=(BLOB)rst.getObject(1); 这种方式。

(2)插入BLOB数据

public void SaveIMage(String Image_ID,BLOB blb,Connection con) {

try{

ArrayList paramList=new ArrayList();

StringBuffer sql=new StringBuffer();

paramList.add(Image_ID);

sql.append("select IMAGE from BM_IMAGE where IMG_ID=? for update");

ResultSet rst= doSelect(sql.toString(), paramList, con);

if(rst.next()) {

BLOB blob=(BLOB)rst.getObject(1);

OutputStream os = blob.getBinaryOutputStream();

BufferedOutputStream output = new BufferedOutputStream(os);

if(blb==null){

}else{

BufferedInputStream input = new BufferedInputStream(blb.getBinaryStream());

byte[] buff = new byte[2048]; //用做文件写入的缓冲

int bytesRead;

while(-1 != (bytesRead = input.read(buff, 0, buff.length))) {

output.write(buff, 0, bytesRead);

}

input.close();

output.flush();

output.close();

os.close();

}

}

rst.close();

}catch(Exception e){e.printStackTrace();}

}

插入图片数据是先存储数据再读取写入图片数据,先给BLOB一个初始化值(可以在建表时给默认值函数empty_blob())。

二、weblogic环境下的BLOB处理

(1)获取BLOB数据

public Blob getTMIMage(String TM_code,Connection con){

Blob blob=null;

try{

ArrayList paramList=new ArrayList();

String sql="select FIMAGE from BM_TTMIMAGE where FTMID=?";

paramList.add(TM_code);

ResultSet rst= doSelect(sql, paramList, con);

if(rst.next()){

blob=rst.getBlob(1);

}

rst.close();

}catch(Exception e){e.printStackTrace();}

return blob;

}

这里就和Tomcat环境下不一样了,这时候结果集里的BLOB数据取出来就不能强制转化为BLOB了,网上好像说这个时候的BLOB读取的数据实际是weblogic.jdbc.vendor.oracle.OracleThinBlob了。后来,我看在weblogic下不能有对BLOB的直接处理,最好在程序应用时用java.sql.Blob类型。这里blob=rst.getBlob(1);也不需要用blob=(Blob)rst.getObject(1);

(2)插入BLOB数据

public void SaveIMage(String Image_ID,Blob blb,Connection con){

try{

ArrayList paramList=new ArrayList();

StringBuffer sql=new StringBuffer();

paramList.add(Image_ID);

sql.append("select IMAGE from BM_IMAGE where IMG_ID=? for update");

ResultSet rst= doSelect(sql.toString(), paramList, con);

if(rst.next()){

OracleThinBlob blob=(OracleThinBlob)rst.getBlob(1);

OutputStream os = blob.getBinaryOutputStream();

BufferedOutputStream output = new BufferedOutputStream(os);

if(blb!=null){

BufferedInputStream input = new BufferedInputStream(blb.getBinaryStream());

byte[] buff = new byte[2048]; //用做文件写入的缓冲

int bytesRead;

while(-1 != (bytesRead = input.read(buff, 0, buff.length))){

output.write(buff, 0, bytesRead);

}

input.close();

output.flush();

output.close();

os.close();

}

}

rst.close();

}catch(Exception e){e.printStackTrace();}

}

在这里,又看到了OracleThinBlob对象,经过多次转换,终于发现问题,对于Blob、BLOB、OracleThinBlob三种对象,有了一些理解。

Blob是java.sql.*包里的对象

BLOB是oracle.sql.*包里的对象

OracleThinBlob是weblogic.jdbc.vendor.oracle.*包里的对象

问题在于这三种类型对流在转化,我主要用的是这三种对象转化为输出流和输入流,

Blob b=new Blob();

b.getBinaryStream(); // 输出流

BLOB bl=new BLOB();

bl.getBinaryStream(); //输出流

bl.getBinaryOutputStream(); //输入流

OracleThinBlob bb=new OracleThinBlob();

bb.getBinaryOutputStream(); //输入流

可以看到,Blob只可以直接转化为输出流,OracleThinBlob 只可以转化为输入流,BLOB两种流都可以转化,当然,用BLOB就轻松多了,可是在weblogic下不能直接转化BLOB,于是我就只能用Blob对象获取数据然后转化为输出流,在插入数据的时候把空的BLOB字段数据转化为OracleThinBlob然后获取输入流写入图片文件内容。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: