【网络转载】Weblogic数据源引发的Oracle-Clob字段问题

文章转载自:http://www.blogjava.net/SpartaYew/archive/2011/05/18/350480.html
原作者:sparta-紫杉

一、开发及运行环境


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

你可能感兴趣的:(JAVA相关文章)