转:java操纵主要数据库的lob类型数据

简介:
2010-04-11 
转:java操纵主要数据库的lob类型数据 
文章分类:Java编程 
Clob和blob的操作主要分为3种:插入,更新和读取显示。 

对于插入,可以分为两类。一类是可以直接按照正常的字段处理,一类为必须先插入空clob/blob再更新为真正的内容。 

插入: 
对于大部分的数据库,在插入lob时都可以通过PreparedStatement.setAsciiStream或PreparedStatement.setBinaryStream直接写入,查到的资料包括: 
H2数据库:http://code.google.com/p/h2database/issues/detail?id=100#c3 
Mysql数据库:http://lavasoft.blog.51cto.com/62575/64963 
DB2:http://lavasoft.blog.51cto.com/62575/64683 

而对于Oracle数据库则必须先插入空lob,重新查询再进行更新(查询时需要加for update)。方法如下: 
Oracle数据库:http://www.iteye.com/topic/254 
插入空Lob的方式很多,如可以插入长度为1的byte[1到blob中]或1个字符的字符串到clob中。也可以在创建表时通过default empty_clob()由数据库自动创建。 

对于上面帖子的补充:robbin同志介绍说需要加for update行锁,是为了防止被其他连接并发更新,这种提法并不完整。oracle数据库要求进行lob更新时,必须加for update,否则报错,这是数据库一个强制要求。就算你知道不会有并发修改,也必须加。 

对于Lob更新,方法都比较类似,查询到Lob字段然后直接更新(H2不支持Lob字段的更新)。需要注意的是,获取到Lob的Stream(如:Blob.setBinaryStream())写入完毕后,必须调用flush(),否则可能会出现数据丢失(在Oracle10g + odbc14.jar下测试发现)。 

对于Lob读取:可以使用传统的java.sql.Clob/java.sql.Blob的api,某些驱动也可以使用rs.getString()或rs.getBytes()(没有测试过,据说大部分都行)当成普通的varchar和字节字段处理,很是方便。 

对于Clob/Blob的使用,大部分都纠结在Oracle上,Oracle有各种各样的限制,如经典的长度限制,最新的odbc14.jar驱动据说已经解决了所有问题,也就是增加一个连接参数SetBigStringTryClob的事情。相关的资料如下: 
Oracle官方文档: 
http://www.oracle.com/technology/sample_code/tech/java/codesnippet/jdbc/clob10g/handlingclobsinoraclejdbc10g.html
也可以看看一大堆跟贴的讨论: 
http://www.iteye.com/topic/254 

我没有测试过rs.getString()的方法,我测试过Oracle和H2的Clob api。通过clob api的方法,可以正常的写入和读取100万字符的大字符串。最理想,最节省内存,也最通用的应该还是JDBC为Lob设计的api。 





JDBC操作MySQL Lob字段记实 

虽然Java的持久化框架多如牛毛,但都离不开JDBC技术,JDBC在某些时候是其他框架难以取代的。也是java操作数据库最根本的技术。 

上文写了JDBC操作DB2 Lob字段bug问题,为此,我还特意写了MySQL平台下的Lob字段操作,以便能得出更为准确的结论。 

本文通过一个简单的Java类,就能增删改查MySQL的Lob字段。google一下,JDBC操作数据库Lob字段的完整代码一个也没找到。因此把这个测试代码也放在blog上,希望给正在用JDBC做MySQL开发的朋友们一点参考。 

环境: 
MySQL-5.0.45 
mysql-connector-java-5.1.5.zip 

测试的SQL脚本: 
CREATE TABLE t_lob ( 
  NAME varchar(24) DEFAULT NULL, 
  TXT text, 
  IMG blob 
) ENGINE=InnoDB DEFAULT CHARSET=gbk; 

测试代码: 
package lob; 

import java.sql.*; 
import java.io.*; 

/** 
* JDBC 读取MySQL lob字段测试 
* File: TestLob4MySQL.java 
* User: leizhimin 
* Date: 2008-3-3 14:44:30 
*/ 
public class TestLob4MySQL { 
    public static final String url = "jdbc:mysql://localhost/testdb"; 
    public static final String username = "root"; 
    public static final String password = "leizhimin"; 
    public static final String driverClassName = "com.mysql.jdbc.Driver"; 


    /** 
     * 数据库连接获取器 
     * 
     * @return 数据库连接 
     */ 
    public static Connection makeConnection() { 
        Connection conn = null; 
        try { 
            Class.forName(driverClassName); 
        } catch (ClassNotFoundException e) { 
            e.printStackTrace(); 
        } 
        try { 
            conn = DriverManager.getConnection(url, username, password); 
        } catch (SQLException e) { 
            e.printStackTrace(); 
        } 
        return conn; 
    } 

    /** 
     * 测试数据库连接 
     */ 
    public static void testConnection() { 
        Connection conn = makeConnection(); 
        try { 
            Statement stmt = conn.createStatement(); 
            ResultSet rs = stmt.executeQuery("SELECT * FROM mysql.user"); 
            while (rs.next()) { 
                String s1 = rs.getString(1); 
                System.out.println(s1); 
            } 
            rs.close(); 
            stmt.close(); 
        } catch (SQLException e) { 
            e.printStackTrace(); 
        } finally { 
            try { 
                conn.close(); 
            } catch (SQLException e) { 
                e.printStackTrace(); 
            } 
        } 
    } 

    /** 
     * 插入Lob字段 
     */ 
    public static void testInsertlob() { 
        Connection conn = makeConnection(); 
        try { 
            conn.setAutoCommit(false); 
            File txtFile = new File("C:\\txt.txt"); 
            File imgFile = new File("C:\\img.png"); 
            int txt_len = (int) txtFile.length(); 
            int img_len = (int) imgFile.length(); 
            try { 
                InputStream fis1 = new FileInputStream(txtFile); 
                InputStream fis2 = new FileInputStream(imgFile); 
                PreparedStatement pstmt = conn.prepareStatement("INSERT INTO T_LOB(NAME,TXT,IMG) VALUES('G',?,?)"); 
                pstmt.setAsciiStream(1, fis1, txt_len); 
                pstmt.setBinaryStream(2, fis2, img_len); 
                pstmt.executeUpdate(); 
                conn.commit(); 
            } catch (FileNotFoundException e) { 
                e.printStackTrace(); 
            } 
        } catch (SQLException e) { 
            e.printStackTrace(); 
        } finally { 
            try { 
                conn.close(); 
            } catch (SQLException e) { 
                e.printStackTrace(); 
            } 
        } 
    } 

    /** 
     * 读取lob字段 
     */ 
    public static void testQueryLob() { 
        Connection conn = makeConnection(); 
        try { 
            conn.setAutoCommit(false); 
            Statement stmt = conn.createStatement(); 
            ResultSet rs = stmt.executeQuery("SELECT TXT,IMG FROM T_LOB"); 
            int i = 1; 
            while (rs.next()) { 
                Clob clob = rs.getClob("TXT"); 
                Blob blob = rs.getBlob("IMG"); 
                InputStream txtIs = rs.getAsciiStream("TXT"); 
                InputStream imgIs = rs.getBinaryStream("IMG"); 

                InputStreamReader txtIsr = new InputStreamReader(txtIs); 
                InputStreamReader imgIsr = new InputStreamReader(imgIs); 

                BufferedReader buff_txtIsr = new BufferedReader(txtIsr); 
                BufferedReader buff_imgIsr = new BufferedReader(imgIsr); 

                String line = null; 
                while (null != (line = buff_txtIsr.readLine())) { 
                    System.out.println(line); //将其输出至屏幕,实际你可以按照需要处理 
                } 

                File fileOutput = new File("c:\\img_x" + i + ".png"); 
                FileOutputStream fo = new FileOutputStream(fileOutput); 
                int c; 
                while ((c = imgIs.read()) != -1) 
                    fo.write(c); 
                fo.close(); 
                System.out.println("img " + i + " retrieved!"); 
                i++; 
            } 
            conn.commit(); 
        } catch (SQLException e) { 
            e.printStackTrace(); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } finally { 
            try { 
                conn.close(); 
            } catch (SQLException e) { 
                e.printStackTrace(); 
            } 
        } 

    } 

    /** 
     * 读取lob字段 
     */ 
    public static void testQueryLob1() { 
        Connection conn = makeConnection(); 
        try { 
            conn.setAutoCommit(false); 
            Statement stmt = conn.createStatement(); 
            ResultSet rs = stmt.executeQuery("SELECT TXT,IMG FROM T_LOB"); 
            while (rs.next()) { 
                Clob clob = rs.getClob("TXT"); 
                Blob blob = rs.getBlob("IMG"); 
                InputStream txtIs = clob.getAsciiStream(); 
                InputStream imgIs = blob.getBinaryStream(); 

                InputStreamReader txtIsr = new InputStreamReader(txtIs); 
                InputStreamReader imgIsr = new InputStreamReader(imgIs); 

                BufferedReader buff_txtIsr = new BufferedReader(txtIsr); 
                BufferedReader buff_imgIsr = new BufferedReader(imgIsr); 

                String line = null; 
                while (null != (line = buff_txtIsr.readLine())) { 
                    System.out.println(line); //将其输出至屏幕,实际你可以按照需要处理 
                } 
            } 
            conn.commit(); 
        } catch (SQLException e) { 
            e.printStackTrace(); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } finally { 
            try { 
                conn.close(); 
            } catch (SQLException e) { 
                e.printStackTrace(); 
            } 
        } 
    } 

    /** 
     * 删除lob字段 
     */ 
    public static void testDeleteLob() { 
        Connection conn = makeConnection(); 
        try { 
            conn.setAutoCommit(false); 
            Statement stmt = conn.createStatement(); 
            int row = stmt.executeUpdate("DELETE FROM T_LOB"); 
            conn.commit(); 
            System.out.println("删除 " + row + " 行数据!"); 
        } catch (SQLException e) { 
            e.printStackTrace(); 
        } finally { 
            try { 
                conn.close(); 
            } catch (SQLException e) { 
                e.printStackTrace(); 
            } 
        } 
    } 

    /** 
     * 读取lob字段 
     */ 
    public static void testUpdateLob() { 
        Connection conn = makeConnection(); 

        try { 
            String in_str="HAHAHAHAHAHA!!!"; 
            File in_file=new File("c:\\img_haha.png"); 
            InputStream txt_is = string2InputStream(in_str); 
            InputStream img_is =new FileInputStream(in_file); 

            conn.setAutoCommit(false); 
            PreparedStatement pstmt = conn.prepareStatement("UPDATE T_LOB SET TXT=?, IMG=? WHERE NAME='G'"); 
            pstmt.setAsciiStream(1,txt_is,in_str.getBytes().length); 
            pstmt.setBinaryStream(2,img_is,(int)in_file.length()); 

            int row = pstmt.executeUpdate(); 

            conn.commit(); 
            txt_is.close(); 
            img_is.close(); 

//            System.out.println("更新 " + row + " 行数据!"); 
        } catch (SQLException e) { 
            e.printStackTrace(); 
        } catch (FileNotFoundException e) { 
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates. 
        } catch (IOException e) { 
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates. 
        } finally { 
            try { 
                conn.close(); 
            } catch (SQLException e) { 
                e.printStackTrace(); 
            } 
        } 
    } 

    public static void main(String args[]) { 
//        testInsertlob(); 
//        testQueryLob(); 
//        testQueryLob1(); 
//        testDeleteLob(); 
        testUpdateLob(); 
    } 

    public static InputStream string2InputStream(String str) { 
        if (str == null) return null; 
        return new ByteArrayInputStream(str.getBytes()); 
    } 

    public static String inputStream2String(InputStream is) { 
        StringBuffer sb = new StringBuffer(); 
        BufferedReader br = new BufferedReader(new InputStreamReader(is)); 
        String inputLine; 
        try { 
            while ((inputLine = br.readLine()) != null) { 
                sb.append(inputLine).append("\n"); 
            } 
        } catch (IOException e) { 
            e.printStackTrace(); 
        } 
        return sb.toString(); 
    } 
} 
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
存储 算法 安全
Java集合框架:理解类型多样性与限制
总之,在 Java 题材中正确地应对多样化与约束条件要求开发人员深入理解面向对象原则、范式编程思想以及JVM工作机理等核心知识点。通过精心设计与周密规划能够有效地利用 Java 高级特征打造出既健壮又灵活易维护系统软件产品。
323 7
|
Java 开发者
Java 函数式编程全解析:静态方法引用、实例方法引用、特定类型方法引用与构造器引用实战教程
本文介绍Java 8函数式编程中的四种方法引用:静态、实例、特定类型及构造器引用,通过简洁示例演示其用法,帮助开发者提升代码可读性与简洁性。
|
Java API 开发工具
【Azure Developer】Java代码实现获取Azure 资源的指标数据却报错 "invalid time interval input"
在使用 Java 调用虚拟机 API 获取指标数据时,因本地时区设置非 UTC,导致时间格式解析错误。解决方法是在代码中手动指定时区为 UTC,使用 `ZoneOffset.ofHours(0)` 并结合 `withOffsetSameInstant` 方法进行时区转换,从而避免因时区差异引发的时间格式问题。
494 4
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
731 16
|
安全 算法 Java
Java泛型编程:类型安全与擦除机制
Java泛型详解:从基础语法到类型擦除机制,深入解析通配符与PECS原则,探讨运行时类型获取技巧及最佳实践,助你掌握泛型精髓,写出更安全、灵活的代码。
|
数据采集 JSON Java
Java爬虫获取1688店铺所有商品接口数据实战指南
本文介绍如何使用Java爬虫技术高效获取1688店铺商品信息,涵盖环境搭建、API调用、签名生成及数据抓取全流程,并附完整代码示例,助力市场分析与选品决策。
|
安全 IDE Java
Java记录类型(Record):简化数据载体类
Java记录类型(Record):简化数据载体类
726 143
|
Java 测试技术
Java浮点类型详解:使用与区别
Java中的浮点类型主要包括float和double,它们在内存占用、精度范围和使用场景上有显著差异。float占用4字节,提供约6-7位有效数字;double占用8字节,提供约15-16位有效数字。float适合内存敏感或精度要求不高的场景,而double精度更高,是Java默认的浮点类型,推荐在大多数情况下使用。两者都存在精度限制,不能用于需要精确计算的金融领域。比较浮点数时应使用误差范围或BigDecimal类。科学计算和工程计算通常使用double,而金融计算应使用BigDecimal。
3958 102
|
安全 Java 编译器
Java类型提升与类型转换详解
本文详解Java中的类型提升与类型转换机制,涵盖类型提升规则、自动类型转换(隐式转换)和强制类型转换(显式转换)的使用场景与注意事项。内容包括类型提升在表达式运算中的作用、自动转换的类型兼容性规则,以及强制转换可能引发的数据丢失和运行时错误。同时提供多个代码示例,帮助理解byte、short、char等类型在运算时的自动提升行为,以及浮点数和整型之间的转换技巧。最后总结了类型转换的最佳实践,如避免不必要的转换、使用显式转换提高可读性、金融计算中使用BigDecimal等,帮助开发者写出更安全、高效的Java代码。
736 0
|
数据采集 存储 前端开发
Java爬虫性能优化:多线程抓取JSP动态数据实践
Java爬虫性能优化:多线程抓取JSP动态数据实践