Java Oracle数据库BLOB字段的存取

java专栏 专栏收录该内容
105 篇文章 0 订阅
Oracle的Blob字段比较特殊,他比long字段的性能要好很多,可以用来保存例如图片之类的二进制数据。 

写入Blob字段和写入其它类型字段的方式非常不同,因为Blob自身有一个cursor,你必须使用cursor对 

blob进行操作,因而你在写入Blob之前,必须获得cursor才能进行写入,那么如何获得Blob的cursor呢? 

这需要你先插入一个empty的blob,这将创建一个blob的cursor,然后你再把这个empty的blob的cursor 

用select查询出来,这样通过两步操作,你就获得了blob的cursor,可以真正地写入blob数据了。 

【处理流程】

[sql] view plaincopy
--Oracle中的Lob类型示例表  
   
create table user_info    
(  
  user_id number(10) primary key,  
  name varchar2(20),  
  image blob  
);  
   
--1. 插入空blob: (如果在数据库中采用默认值方式对Blob字段赋值, 此步可省略)  
   
    insert into user_info values (1, 'Jacky', empty_blob());  
   
--2. 获得blob的cursor:  
     
    select image from user_info where user_id = ? for update;  
   
--3. 用cursor往数据库写数据:  
   
    update user_info set image = ? where user_id = ?;  

 

//读取Blob数据    
package demo;    
   
import java.sql.*;    
import java.io.*;    
   
public class ReadBlob    
{    
    //加载驱动程序    
    static    
    {    
           
[java] view plaincopy
//读取Blob数据  
package demo;  
   
import java.sql.*;  
import java.io.*;  
   
public class ReadBlob  
{  
    //加载驱动程序  
    static    
    {  
           
        try  
        {  
            Class.forName("oracle.jdbc.driver.OracleDriver");  
        } catch (ClassNotFoundException e)  
        {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
       
    public static void main(String[] args)  
    {  
        try  
        {  
            //1. 建立连接  
            String url = "jdbc:oracle:thin:@localhost:1521:OracleDB";  
            Connection conn = DriverManager.getConnection(url,"scott","tiger");  
            conn.setAutoCommit(false);  
               
            //2. 查询数据  
            String sql = "select image from user_info where user_id = 1";  
            Statement stmt = conn.createStatement();  
            ResultSet rs = stmt.executeQuery(sql);  
               
            //3. 读取Blob类型数据  
            Blob blob = null;  
            if(rs.next())  
            {  
                blob = rs.getBlob(1);  
            }  
            byte[] temp = new byte[(int)blob.length()];  
            InputStream in = blob.getBinaryStream();  
            in.read(temp)s  
[java] view plaincopy
            <strong>//保证文件名唯一,你可以用主键+时间啊等等方法</strong>                  
            File file = new File("D://img.bmp");  
            FileOutputStream fout = new FileOutputStream(file);  
            fout.write(temp);  
            in.close();  
            fout.close();  
        } catch (Exception e)  
        {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
}  



 

//写Blob数据    
package demo;    
   
import java.sql.*;    
import oracle.sql.BLOB;//▲此处的BLOB类全大写, 而java.sql.Blob中的Blob非全大写    
import java.io.*;    
   
public class WriteBlob    
{    
    //加载驱动程序    
    static    
    {    
        try  
        {    
            Class.forName("oracle.jdbc.driver.OracleDriver");    
        }    
        catch(Exception e)    
        {    
            e.printStackTrace();    
        }    
    }    
    public static void main(String[] args)    
    {    
        try  
        {    
            //1. 建立与数据库服务器的连接    
            String url = "jdbc:oracle:thin:@localhost:1521:OracleDB";    
            Connection conn = DriverManager.getConnection(url,"scott","tiger");    
            conn.setAutoCommit(false);    
               
            //2. 首先向表中插入空的Blob    
            //★注意: 对于empty_blob()应放在SQL语句中直接赋值, 使用预置语句的方式赋值无法实现.    
            String sql = "insert into user_info values(?,?,empty_blob())";    
            PreparedStatement ps = conn.prepareStatement(sql);    
            ps.setInt(1, 1);    
            ps.setString(2, "Lucy");    
            ps.executeUpdate();    
               
            //3. 查询Blob, 获得Blob的Cursor    
            sql = "select image from user_info where user_id = ?";    
            ps = conn.prepareStatement(sql);    
            ps.setInt(1, 1);    
            ResultSet rs = ps.executeQuery();    
            BLOB blob = null;    
            if(rs.next())    
            {    
                blob = (BLOB)rs.getBlob(1);    
            }    
               
            //4. 使用字节流将待入库的文件写入到blob中    
            File file = new File("D://iriver//sample1.bmp");    
            FileInputStream fin = new FileInputStream(file);    
            byte[] temp = new byte[fin.available()];    
            fin.read(temp);    
            OutputStream out = blob.getBinaryOutputStream();    
            out.write(temp);    
            fin.close();    
            out.close();    
               
            //5. 向数据库中写入数据    
            sql = "update user_info set image = ? where user_id = ?";    
            ps = conn.prepareStatement(sql);    
            ps.setBlob(1, blob);    
            ps.setInt(2, 1);    
            ps.executeUpdate();    
               
            conn.commit();    
        } catch (Exception e)    
        {    
            e.printStackTrace();    
        }    
    }    
}  

Java存取OracleBlob字段,图片存储,Blob和BLOB的问题,Clob,oracle

1、Blob和BLOB的问题

java.sql.Blob 
oracle.sql.BLOB 
这两个blob仅仅是大小写不同,但是差异很大,java.sql.Blob是一个接口,而oracle.sql.BLOB是一个实现java.sql.Blob的类,并且还有很多扩展的属性和方法,注意不要搞混了。 

2、JDBC2.0和JDBC3.0的问题 

classes12.zip实现了JDBC2.0(JDK1.3),而JDBC2.0对于Blob的操作只有读,没有写,所以classes12.zip只好自己扩展了一套对Blob进行写的API,我的例子就是用了这套API。 
ojdbc14.jar实现了JDBC3.0(JDK1.4),JDBC3.0已经包括了对Blob写的操作,而ojdbc14.jar也实现了该API。 

总之注意这些问题。 

你用的是什么容器?是不是weblogic?如果是weblogic,并且通过datasourse获取connection,取出来的就不是oracle.sql.BLOB,而是weblogic封装过的OracleThinBlob,所以cast的时候肯定会出错,但是如果在程序里面cast成OracleThinBlob,就会造成对weblogic的依赖,所以我在dao的impl中用反射解耦。并且这个只是在使用oracle的thin驱动的时候才会出现的问题

写CLOB的代码片段:
Java代码  
File blobFile = new File("image.jpg");  
int file_length = (int)blobFile.length();  
FileInputStream blobInStream = new FileInputStream(blobFile);  
PreparedStatement insert_prepared = null;  
insert_prepared = m_conn.prepareStatement(query);  
insert_prepared.setString(1, id);  
insert_prepared.setBinaryStream(2, blobInStream, file_length);  
insert_prepared.setString(3, tel);  
insert_prepared.executeUpdate();  
m_conn.commit();  
 
String SqlStr = "update TextInfo set Content=empty_clob() where TextInfoKey=" + Integer.toString(textinfokey);    
myConn.setAutoCommit(false);    
java.sql.PreparedStatement pstmt = myConn.prepareStatement(SqlStr);    
pstmt.executeUpdate();    
   
SqlStr = "select Content from TextInfo where TextInfoKey=" + Integer.toString(textinfokey) + " for update";    
java.sql.Statement st = myConn.createStatement();    
java.sql.ResultSet rs = st.executeQuery(SqlStr);    
java.sql.Clob clob ;    
if (rs.next()) {    
clob = rs.getClob("Content");    
weblogic.jdbc.rmi.SerialOracleClob cast1 =(weblogic.jdbc.rmi.SerialOracleClob)clob;    
weblogic.jdbc.rmi.internal.OracleTClobImpl cast2 =(weblogic.jdbc.rmi.internal.OracleTClobImpl)cast1.getTheRealClob();    
CLOB myClob = (oracle.sql.CLOB)cast2.getTheRealClob();    
java.io.Writer out=myClob.getCharacterOutputStream ();    
out.write(content);    
out.flush();    
out.close();    
   
}    
  -------------------------------------------------------------------------------------------------

存取BLOB出现这么多问题,我认为大半是由数据库开发商、应用服务器商在JDBC驱动上的不兼容性带来的。而实际应用中,每个人的开发运行环境不同,使得某个网友的solution没有办法在别人的应用中重现,以至于骂声一片。至于为什么会不兼容、有哪些问题,我没有时间去弄清,这里只说说我们怎样解决了问题的。

基于上述原因,先列出我们的开发环境,免得有人配不出来,招人唾骂。

数据库 Oracle 9i
应用服务器 BEA Weblogic 8.11
开发工具 JBuilder X

在JSP实现文件Upload/Download可以分成这样几块:文件提交到形成InputSteam;InputSteam以BLOB格式入库;数据从库中读出为InputSteam;InputStream输出到页面形成下载文件。先说BLOB吧。

1.   BLOB入库

(1)       直接获得数据库连接的情况

这是Oracle提供的标准方式,先插入一个空BLOB对象,然后Update这个空对象。代码如下:
Java代码  
//得到数据库连接(驱动包是weblogic的,没有下载任何新版本)  
   
Class.forName("oracle.jdbc.driver.OracleDriver");  
   
Connection con = DriverManager.getConnection(  
   
          "jdbc:oracle:thin:@localhost:1521:testdb", "test", "test");  
   
//处理事务  
   
con.setAutoCommit(false);  
   
Statement st = con.createStatement();  
   
//插入一个空对象  
   
st.executeUpdate("insert into BLOBIMG   values(103,empty_blob())");  
   
//用for update方式锁定数据行  
   
ResultSet rs = st.executeQuery(  
   
          "select contents from   BLOBIMG   where   id=103 for update");  
   
if (rs.next()) {  
   
    //得到java.sql.Blob对象,然后Cast为oracle.sql.BLOB  
   
oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob(1).;  
   
    //到数据库的输出流  
   
OutputStream outStream = blob.getBinaryOutputStream();  
   
    //这里用一个文件模拟输入流  
   
File file = new File("d:\proxy.txt");  
   
  InputStream fin = new FileInputStream(file);  
   
//将输入流写到输出流  
   
byte[] b = new byte[blob.getBufferSize()];  
   
        int len = 0;  
   
        while ( (len = fin.read(b)) != -1) {  
   
          outStream.write(b, 0, len);  
   
          //blob.putBytes(1,b);  
  }  

    //依次关闭(注意顺序)  
   
fin.close();  
   
    outStream.flush();  
   
    outStream.close();  
   
    con.commit();  
   
    con.close();  

(2)       通过JNDI获得数据库连接

在Weblogic中配置到Oracle的JDBC Connection Pool和DataSource,绑定到Context中,假定绑定名为”orads”。

为了得到数据库连接,做一个连接工厂,主要代码如下:

Context context = new InitialContext();

ds = (DataSource) context.lookup("orads");

return ds.getConnection();

以下是BLOB写入数据库的代码:


Java代码  
Connection con = ConnectionFactory.getConnection();  
   
con.setAutoCommit(false);  
   
Statement st = con.createStatement();  
   
st.executeUpdate("insert into BLOBIMG values(103,empty_blob())");  
   
ResultSet rs = st.executeQuery(  
   
          "select contents from   BLOBIMG   where   id=103 for update");  
   
if (rs.next()) {  
   
    //上面代码不变  
   
//这里不能用oracle.sql.BLOB,会报ClassCast 异常  
   
weblogic.jdbc.vendor.oracle.OracleThinBlob blob = (weblogic.jdbc.vendor.oracle.OracleThinBlob) rs.getBlob(1);  
   
    //以后代码也不变  
   
OutputStream outStream = blob.getBinaryOutputStream();  
   
File file = new File("d:\proxy.txt");  
   
  InputStream fin = new FileInputStream(file);  
   
byte[] b = new byte[blob.getBufferSize()];  
   
        int len = 0;  
   
        while ( (len = fin.read(b)) != -1) {  
   
          outStream.write(b, 0, len);  
   
        }
   
fin.close();  
   
    outStream.flush();  
   
    outStream.close();  
   
    con.commit();  
   
    con.close();

2.   BLOB出库

从数据库中读出BLOB数据没有上述由于连接池的不同带来的差异,只需要J2SE的标准类java.sql.Blob就可以取得输出流(注意区别java.sql.Blob和oracle.sql.BLOB)。代码如下:
Java代码  
Connection con = ConnectionFactory.getConnection();  
   
con.setAutoCommit(false);  
   
Statement st = con.createStatement();  
   
//这里的SQL语句不再需要”for update”  
   
ResultSet rs = st.executeQuery(  
   
          "select contents from   BLOBIMG   where   id=103 ");  
   
if (rs.next()) {  
   
    java.sql.Blob blob = rs.getBlob(1);  
   
   
   
    InputStream ins = blob.getBinaryStream();  
    //用文件模拟输出流  
   
File file = new File("d:\output.txt");  
    OutputStream fout = new FileOutputStream(file);  
    //下面将BLOB数据写入文件  
   
    byte[] b = new byte[1024];  
   
    int len = 0;  
   
        while ( (len = ins.read(b)) != -1) {  
   
          fout.write(b, 0, len);  
   
        }  
   
  //依次关闭  
   
  fout.close();  
   
  ins.close();  
   
  con.commit();  
   
  con.close();  
 


3.   从JSP页面提交文件到数据库
(1)       提交页面的代码如下:
Html代码  
<form action="handle.jsp" enctype="multipart/form-data" method="post" >  
   
<input type="hidden" name="id" value="103"/>  
   
<input type="file"   name="fileToUpload">  
   
<input type="submit"   value="Upload">  
   
</form>  
 


(2)       由于JSP没有提供文件上传的处理能力,只有使用第三方的开发包。网络上开源的包有很多,我们这里选择Apache Jakarta的FileUpload,在http://jakarta.apache.org/commons/fileupload/index.html 可以得到下载包和完整的API文档。法奥为adajspException

处理页面(handle.jsp)的代码如下
Html代码  
<%  
   
boolean isMultipart = FileUpload.isMultipartContent(request);  
   
    if (isMultipart) {  
   
      // 建立一个新的Upload对象  
   
      DiskFileUpload upload = new DiskFileUpload();  
   
   
   
    // 设置上载文件的参数  
   
    //upload.setSizeThreshold(yourMaxMemorySize);  
   
    //upload.setSizeMax(yourMaxRequestSize);  
   
    String rootPath = getServletConfig().getServletContext().getRealPath("/") ;  
   
    upload.setRepositoryPath(rootPath+"\uploads");  
   
   
   
      // 分析request中的传来的文件流,返回Item的集合,  
   
      // 轮询Items,如果不是表单域,就是一个文件对象。  
   
      List items = upload.parseRequest(request);  
   
      Iterator iter = items.iterator();  
   
      while (iter.hasNext()) {  
   
        FileItem item = (FileItem) iter.next();  
   
        //如果是文件对象  
   
if (!item.isFormField()) {  
   
   
   
          //如果是文本文件,可以直接显示  
   
          //out.println(item.getString());  
   
   
   
          //将上载的文件写到服务器的WEB-INFwebstart下,文件名为test.txt  
   
          //File uploadedFile = new File(rootPath+"\uploads\test.txt");  
   
          //item.write(uploadedFile);  
        //下面的代码是将文件入库(略):  
        //注意输入流的获取  
InputStream uploadedStream = item.getInputStream();  
        }  

Oracle数据库BLOB字段的存取  

首先建立测试数据表
  drop table filelist;
  commit;

  CREATE TABLE SYSTEM.FILELIST (

  "FILENAME" VARCHAR2(50) NOT NULL,

  "FILESIZE" NUMBER(20)   NULL,

  "FILEBODY" BLOB   NULL, 

  PRIMARY KEY("FILENAME"), UNIQUE("FILENAME")) ;

  commit;

      测试过程,首先将硬盘文件读入数据库,然后再读出到硬盘的另一个新文件里,原码如下:


import java.io.*;

import java.util.*;

import java.sql.*;

import oracle.sql.*;

import oracle.jdbc.driver.*;

import java.text.*;

public class test

{
  public static void main(String args[]) throws java.io.IOException,java.sql.SQLException
  {
  dbBean db1=new dbBean();
 

  byte a[]=null;//**将测试文件test.doc读入此字节数组

  java.io.FileInputStream fin=null;

  java.io.FileOutputStream fout=null;

  oracle.jdbc.OracleResultSet ors=null;//**这里rs一定要用Oracle提供的

  oracle.jdbc.driver.OraclePreparedStatement   opst=null;//**PreparedStatement用

//Oracle提供的

  try

  {
    java.io.File f1=new java.io.File("c:/temp/test.doc");

    java.io.File f2=new java.io.File("c:/temp/testout.doc");//**从BLOB读出的信息写

//入该文件,和源文件对比测试用

    fin=new java.io.FileInputStream(f1);

    fout=new java.io.FileOutputStream(f2);

    int flength=(int)f1.length();//**读入文件的字节长度

    System.out.println("file length::"+flength);

    a=new byte[flength];

    int i=0;int itotal=0;

    /**将文件读入字节数组

    for (;itotal

    {
    i=fin.read(a,itotal,flength-itotal);
    }

    fin.close();
    System.out.println("read itotal::"+itotal);

  /**注意Oracle的 BLOB一定要用EMPTY_BLOB()初始化 

  String mysql="insert into filelist (FileName,FileSize,FileBody) values (?,?,EMPTY_BLOB())";

  opst=(oracle.jdbc.driver.OraclePreparedStatement)db1.conn.prepareStatement(mysql);

          opst.setString(1,"wordtemplate");

          opst.setInt (2,flength);

          opst.executeUpdate();

          opst.clearParameters();

          /**插入其它数据后,定位BLOB字段

            mysql="select filebody from filelist where filename=?";

            opst=(oracle.jdbc.driver.OraclePreparedStatement)db1.conn.prepareStatement(mysql);

            opst.setString(1,"wordtemplate");

            ors=(oracle.jdbc.OracleResultSet)opst.executeQuery();

            if (ors.next())

            {
            oracle.sql.BLOB blob=ors.getBLOB(1);/**得到BLOB字段          

            int j=blob.putBytes(1,a);/**将字节数组写入BLOB字段

            System.out.println("j:"+j);

            db1.conn.commit();

            ors.close();

            }          
    System.out.println("insert into ok");
    byte b[]=null;/**保存从BLOB读出的字节

    opst.clearParameters();

            mysql="select filebody from filelist where filename=?";

            opst=(oracle.jdbc.driver.OraclePreparedStatement)db1.conn.prepareStatement(mysql);

            opst.setString(1,"wordtemplate");

            ors=(oracle.jdbc.OracleResultSet)opst.executeQuery();

            if (ors.next())

            {
            oracle.sql.BLOB blob2=ors.getBLOB(1);    

            System.out.println("blob2 length:"+blob2.length());

            b=blob2.getBytes(1,flength);/**从BLOB取出字节流数据

            System.out.println("b length::"+b.length);

            db1.conn.commit();

           

            ors.close();

            /**将从BLOB读出的字节写入文件

            fout.write(b,0,b.length);

            fout.close();  
      System.out.println("write itotal::"+b.length);
  }

  catch(Exception e)

  {

    System.out.println("errror :"+e.toString() );

    e.printStackTrace();

   

  }

  finally

  { /**关闭所有数据联接

    stmt.close();

    db1.closeConn();

  }

  }

}

    编译运行在TomCat下调试通过。

    需要注意的是Blob存取的过程,一般先存入和BLOB相关的控制数据,如文件的名字,然后查询定位BLOB字段,利用OracleBlob提供的方法:

    public int putBytes(long pos,byte bytes[])

    public byte[]   getBytes(long pos,byte bytes[])

    或者利用

    public OutputStream getBinaryOutputStream() throws SQLException

    public InputStream   getBinaryStream() throws SQLException

  因为利用输入输出流总归还是利用到字节数组缓冲流,所以就不举例子了。
  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:1024 设计师:我叫白小胖 返回首页

打赏作者

郡麟天下

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值