Sparta Yew

     简约、职业、恒久
随笔 - 15, 文章 - 1, 评论 - 276, 引用 - 0
数据加载中……

Weblogic、Oracle与Clob


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

posted on 2011-05-18 15:18 sparta-紫杉 阅读(1037) 评论(0)  编辑  收藏 所属分类: Java


只有注册用户登录后才能发表评论。


网站导航: