<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    太陽雨

    痛并快樂著

    BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
      67 Posts :: 3 Stories :: 33 Comments :: 0 Trackbacks
    本文講解了在 Spring 中處理 LOB 數(shù)據(jù)的原理和方法,對于 Spring JDBC 以及 Spring 所集成的第三方 ORM 框架(包括 JPA、Hibernate 和 iBatis)如何處理 LOB 數(shù)據(jù)進行了闡述。

    概述

    LOB 代表大對象數(shù)據(jù),包括 BLOB 和 CLOB 兩種類型,前者用于存儲大塊的二進制數(shù)據(jù),如圖片數(shù)據(jù),視頻數(shù)據(jù)等,而后者用于存儲長文本數(shù)據(jù),如論壇的帖子內(nèi)容,產(chǎn)品的詳細描述等。值得注意的是:在不同的數(shù)據(jù)庫中,大對象對應的字段類型是不盡相同的,如 DB2 對應 BLOB/CLOB,MySql 對應 BLOB/LONGTEXT,SqlServer 對應 IMAGE/TEXT。需要指出的是,有些數(shù)據(jù)庫的大對象類型可以象簡單類型一樣訪問,如 MySql 的 LONGTEXT 的操作方式和 VARCHAR 類型一樣。在一般情況下, LOB 類型數(shù)據(jù)的訪問方式不同于其它簡單類型的數(shù)據(jù),我們經(jīng)常會以流的方式操作 LOB 類型的數(shù)據(jù)。此外,LOB 類型數(shù)據(jù)的訪問不是線程安全的,需要為其單獨分配相應的數(shù)據(jù)庫資源,并在操作完成后釋放資源。最后,Oracle 9i 非常有個性地采用非 JDBC 標準的 API 操作 LOB 數(shù)據(jù)。所有這些情況給編寫操作 LOB 類型數(shù)據(jù)的程序帶來挑戰(zhàn),Spring 在 org.springframework.jdbc.support.lob 包中為我們提供了相應的幫助類,以便我們輕松應對這頭攔路虎。

    Spring 大大降低了我們處理 LOB 數(shù)據(jù)的難度。首先,Spring 提供了 NativeJdbcExtractor 接口,您可以在不同環(huán)境里選擇相應的實現(xiàn)類從數(shù)據(jù)源中獲取本地 JDBC 對象;其次,Spring 通過 LobCreator 接口取消了不同數(shù)據(jù)廠商操作 LOB 數(shù)據(jù)的差別,并提供了創(chuàng)建 LobCreator 的 LobHandler 接口,您只要根據(jù)底層數(shù)據(jù)庫類型選擇合適的 LobHandler 進行配置即可。

    本文將詳細地講述通過 Spring JDBC 插入和訪問 LOB 數(shù)據(jù)的具體過程。不管是以塊的方式還是以流的方式,您都可以通過 LobCreator 和 LobHandler 方便地訪問 LOB 數(shù)據(jù)。對于 ORM 框架來說,JPA 擁有自身處理 LOB 數(shù)據(jù)的配置類型,Spring 為 Hibernate 和 iBatis 分別提供了 LOB 數(shù)據(jù)類型的配置類,您僅需要使用這些類進行簡單的配置就可以像普通類型一樣操作 LOB 類型數(shù)據(jù)。





    回頁首


    本地 JDBC 對象

    當您在 Web 應用服務器或 Spring 中配置數(shù)據(jù)源時,從數(shù)據(jù)源中返回的數(shù)據(jù)連接對象是本地 JDBC 對象(如 DB2Connection、OracleConnection)的代理類,這是因為數(shù)據(jù)源需要改變數(shù)據(jù)連接一些原有的行為以便對其進行控制:如調(diào)用 Connection#close() 方法時,將數(shù)據(jù)連接返回到連接池中而非將其真的關閉。

    在訪問 LOB 數(shù)據(jù)時,根據(jù)數(shù)據(jù)庫廠商的不同,可能需要使用被代理前的本地 JDBC 對象(如 DB2Connection 或 DB2ResultSet)特有的 API。為了從數(shù)據(jù)源中獲取本地 JDBC 對象, Spring 定義了 org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor 接口并提供了相應的實現(xiàn)類。NativeJdbcExtractor 定義了從數(shù)據(jù)源中抽取本地 JDBC 對象的若干方法:

    方法 說明
    Connection getNativeConnection(Connection con) 獲取本地 Connection 對象
    Connection getNativeConnectionFromStatement(Statement stmt) 獲取本地 Statement 對象
    PreparedStatement getNativePreparedStatement(PreparedStatement ps) 獲取本地 PreparedStatement 對象
    ResultSet getNativeResultSet(ResultSet rs) 獲取本地 ResultSet 對象
    CallableStatement getNativeCallableStatement(CallableStatement cs) 獲取本地 CallableStatement 對象
    ?

    有些簡單的數(shù)據(jù)源僅對 Connection 對象進行代理,這時可以直接使用 SimpleNativeJdbcExtractor 實現(xiàn)類。但有些數(shù)據(jù)源(如 Jakarta Commons DBCP)會對所有的 JDBC 對象進行代理,這時,就需要根據(jù)具體的情況選擇適合的抽取器實現(xiàn)類了。下表列出了不同數(shù)據(jù)源本地 JDBC 對象抽取器的實現(xiàn)類:

    數(shù)據(jù)源類型 說明
    WebSphere 4 及以上版本的數(shù)據(jù)源 org.springframework.jdbc.support.nativejdbc.WebSphereNativeJdbcExtractor
    WebLogic 6.1+ 及以上版本的數(shù)據(jù)源 org.springframework.jdbc.support.nativejdbc.WebLogicNativeJdbcExtractor
    JBoss 3.2.4 及以上版本的數(shù)據(jù)源 org.springframework.jdbc.support.nativejdbc.JBossNativeJdbcExtractor
    C3P0 數(shù)據(jù)源 org.springframework.jdbc.support.nativejdbc.C3P0NativeJdbcExtractor
    DBCP 數(shù)據(jù)源 org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor
    ObjectWeb 的 XAPool 數(shù)據(jù)源 org.springframework.jdbc.support.nativejdbc.XAPoolNativeJdbcExtractor

    下面的代碼演示了從 DBCP 數(shù)據(jù)源中獲取 DB2 的本地數(shù)據(jù)庫連接 DB2Connection 的方法:


    清單 1. 獲取本地數(shù)據(jù)庫連接
    package com.baobaotao.dao.jdbc;
                import java.sql.Connection;
                import COM.ibm.db2.jdbc.net.DB2Connection;
                import org.springframework.jdbc.core.support.JdbcDaoSupport;
                import org.springframework.jdbc.datasource.DataSourceUtils;
                public class PostJdbcDao extends JdbcDaoSupport implements PostDao {
                public void getNativeConn(){
                try {
                Connection conn = DataSourceUtils.getConnection(getJdbcTemplate()
                .getDataSource()); ① 使用 DataSourceUtils 從模板類中獲取連接
                ② 使用模板類的本地 JDBC 抽取器獲取本地的 Connection
                conn = getJdbcTemplate().getNativeJdbcExtractor().getNativeConnection(conn);
                DB2Connection db2conn = (DB2Connection) conn; ③ 這時可以強制進行類型轉換了
                …
                } catch (Exception e) {
                e.printStackTrace();
                }
                }
                }
                

    在 ① 處我們通過 DataSourceUtils 獲取當前線程綁定的數(shù)據(jù)連接,為了使用線程上下文相關的事務,通過 DataSourceUtils 從數(shù)據(jù)源中獲取連接是正確的做法,如果直接通過 dateSource 獲取連接,則將得到一個和當前線程上下文無關的數(shù)據(jù)連接實例。

    JdbcTemplate 可以在配置時注入一個本地 JDBC 對象抽取器,要使代碼 清單 1 正確運行,我們必須進行如下配置:


    清單 2. 為 JdbcTemplate 裝配本地 JDBC 對象抽取器
    …
                <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
                destroy-method="close">
                <property name="driverClassName"
                value="${jdbc.driverClassName}" />
                <property name="url" value="${jdbc.url}" />
                <property name="username" value="${jdbc.username}" />
                <property name="password" value="${jdbc.password}" />
                </bean>
                ① 定義 DBCP 數(shù)據(jù)源的 JDBC 本地對象抽取器
                <bean id="nativeJdbcExtractor"
                class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"
                lazy-init="true" />
                <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
                <property name="dataSource" ref="dataSource" />
                ② 設置抽取器
                <property name="nativeJdbcExtractor" ref="nativeJdbcExtractor"/>
                </bean>
                <bean id="postDao" class="com.baobaotao.dao.jdbc.PostJdbcDao">
                <property name="jdbcTemplate" ref="jdbcTemplate" />
                </bean>
                

    在獲取 DB2 的本地 Connection 實例后,我們就可以使用該對象的一些特有功能了,如使用 DB2Connection 的特殊 API 對 LOB 對象進行操作。

    LobCreator

    雖然 JDBC 定義了兩個操作 LOB 類型的接口:java.sql.Blobjava.sql.Clob,但有些廠商的 JDBC 驅動程序并不支持這兩個接口。為此,Spring 定義了一個獨立于 java.sql.Blob/ClobLobCreator 接口,以統(tǒng)一的方式操作各種數(shù)據(jù)庫的 LOB 類型數(shù)據(jù)。因為 LobCreator 本身持有 LOB 所對應的數(shù)據(jù)庫資源,所以它不是線程安全的,一個 LobCreator 只能操作一個 LOB 數(shù)據(jù)。

    為了方便在 PreparedStatement 中使用 LobCreator,您可以直接使用 JdbcTemplate#execute(String sql,AbstractLobCreatingPreparedStatementCallback lcpsc) 方法。下面對 LobCreator 接口中的方法進行簡要說明:

    方法 說明
    void close() 關閉會話,并釋放 LOB 資源
    void setBlobAsBinaryStream(PreparedStatement ps, int paramIndex, InputStream contentStream, int contentLength) 通過流填充 BLOB 數(shù)據(jù)
    void setBlobAsBytes(PreparedStatement ps, int paramIndex, byte[] content) 通過二進制數(shù)據(jù)填充 BLOB 數(shù)據(jù)
    void setClobAsAsciiStream(PreparedStatement ps, int paramIndex, InputStream asciiStream, int contentLength) 通過 Ascii 字符流填充 CLOB 數(shù)據(jù)
    void setClobAsCharacterStream(PreparedStatement ps, int paramIndex, Reader characterStream, int contentLength) 通過 Unicode 字符流填充 CLOB 數(shù)據(jù)
    void setClobAsString(PreparedStatement ps, int paramIndex, String content) 通過字符串填充 CLOB 數(shù)據(jù)

    LobHandler

    LobHandler 接口為操作 BLOB/CLOB 提供了統(tǒng)一訪問接口,而不管底層數(shù)據(jù)庫究竟是以大對象的方式還是以一般數(shù)據(jù)類型的方式進行操作。此外,LobHandler 還充當了 LobCreator 的工廠類。

    大部分數(shù)據(jù)庫廠商的 JDBC 驅動程序(如 DB2)都以 JDBC 標準的 API 操作 LOB 數(shù)據(jù),但 Oracle 9i 及以前的 JDBC 驅動程序采用了自己的 API 操作 LOB 數(shù)據(jù),Oracle 9i 直接使用自己的 API 操作 LOB 數(shù)據(jù),且不允許通過 PreparedStatement 的 setAsciiStream()setBinaryStream()setCharacterStream() 等方法填充流數(shù)據(jù)。Spring 提供 LobHandler 接口主要是為了遷就 Oracle 特立獨行的作風。所以 Oracle 必須使用 OracleLobHandler 實現(xiàn)類,而其它的數(shù)據(jù)庫統(tǒng)一使用 DefaultLobHandler 就可以了。Oracle 10g 改正了 Oracle 9i 這個異化的風格,終于天下歸一了,所以 Oracle 10g 也可以使用 DefaultLobHandler。 下面,我們來看一下 LobHandler 接口的幾個重要方法:

    方法 說明
    InputStream getBlobAsBinaryStream(ResultSet rs, int columnIndex) 從結果集中返回 InputStream,通過 InputStream 讀取 BLOB 數(shù)據(jù)
    byte[] getBlobAsBytes(ResultSet rs, int columnIndex) 以二進制數(shù)據(jù)的方式獲取結果集中的 BLOB 數(shù)據(jù);
    InputStream getClobAsAsciiStream(ResultSet rs, int columnIndex) 從結果集中返回 InputStream,通過 InputStreamn 以 Ascii 字符流方式讀取 BLOB 數(shù)據(jù)
    Reader getClobAsCharacterStream(ResultSet rs, int columnIndex) 從結果集中獲取 Unicode 字符流 Reader,并通過 Reader以Unicode 字符流方式讀取 CLOB 數(shù)據(jù)
    String getClobAsString(ResultSet rs, int columnIndex) 從結果集中以字符串的方式獲取 CLOB 數(shù)據(jù)
    LobCreator getLobCreator() 生成一個會話相關的 LobCreator 對象




    回頁首


    在 Spring JDBC 中操作 LOB 數(shù)據(jù)

    插入 LOB 數(shù)據(jù)

    假設我們有一個用于保存論壇帖子的 t_post 表,擁有兩個 LOB 字段,其中 post_text 是 CLOB 類型,而 post_attach 是 BLOB 類型。下面,我們來編寫插入一個帖子記錄的代碼:


    清單 3. 添加 LOB 字段數(shù)據(jù)
    package com.baobaotao.dao.jdbc;
                …
                import java.sql.PreparedStatement;
                import java.sql.SQLException;
                import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatementCallback;
                import org.springframework.jdbc.support.lob.LobCreator;
                import org.springframework.jdbc.support.lob.LobHandler;
                public class PostJdbcDao extends JdbcDaoSupport implements PostDao {
                private LobHandler lobHandler; ① 定義 LobHandler 屬性
                public LobHandler getLobHandler() {
                return lobHandler;
                }
                public void setLobHandler(LobHandler lobHandler) {
                this.lobHandler = lobHandler;
                }
                public void addPost(final Post post) {
                String sql = " INSERT INTO t_post(post_id,user_id,post_text,post_attach)"
                + " VALUES(?,?,?,?)";
                getJdbcTemplate().execute(sql,
                new AbstractLobCreatingPreparedStatementCallback(this.lobHandler) { ②
                protected void setValues(PreparedStatement ps,LobCreator lobCreator)
                throws SQLException {
                ps.setInt(1, 1);
                ps.setInt(2, post.getUserId());
                ③ 設置 CLOB 字段
                lobCreator.setClobAsString(ps, 3, post.getPostText());
                ④ 設置 BLOB 字段
                lobCreator.setBlobAsBytes(ps, 4, post.getPostAttach());
                }
                });
                }
                …
                }
                

    首先,我們在 PostJdbcDao 中引入了一個 LobHandler 屬性,如 ① 所示,并通過 JdbcTemplate#execute(String sql,AbstractLobCreatingPreparedStatementCallback lcpsc) 方法完成插入 LOB 數(shù)據(jù)的操作。我們通過匿名內(nèi)部類的方式定義 LobCreatingPreparedStatementCallback 抽象類的子類,其構造函數(shù)需要一個 LobHandler 入?yún)ⅲ?② 所示。在匿名類中實現(xiàn)了父類的抽象方法 setValues(PreparedStatement ps,LobCreator lobCreator),在該方法中通過 lobCreator 操作 LOB 對象,如 ③、④ 所示,我們分別通過字符串和二進制數(shù)組填充 BLOB 和 CLOB 的數(shù)據(jù)。您同樣可以使用流的方式填充 LOB 數(shù)據(jù),僅需要調(diào)用 lobCreator 相應的流填充方法即可。

    我們需要調(diào)整 Spring 的配置文件以配合我們剛剛定義的 PostJdbcDao。假設底層數(shù)據(jù)庫是 Oracle,可以采用以下的配置方式:


    清單 4. Oracle 數(shù)據(jù)庫的 LobHandler 配置
    …
                <bean id="nativeJdbcExtractor"
                class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"
                lazy-init="true"/>
                <bean id="oracleLobHandler"
                class="org.springframework.jdbc.support.lob.OracleLobHandler"
                lazy-init="true">
                <property name="nativeJdbcExtractor" ref="nativeJdbcExtractor"/> ① 設置本地 Jdbc 對象抽取器
                </bean>
                <bean id="postDao" class="com.baobaotao.dao.jdbc.PostJdbcDao">
                <property name="lobHandler" ref="oracleLobHandler"/> ② 設置 LOB 處理器
                </bean>
                

    大家可能已經(jīng)注意到 nativeJdbcExtractororacleLobHandler Bean 都設置為 lazy-init="true",這是因為 nativeJdbcExtractor 需要通過運行期的反射機制獲取底層的 JDBC 對象,所以需要避免在 Spring 容器啟動時就實例化這兩個 Bean。

    LobHandler 需要訪問本地 JDBC 對象,這一任務委托給 NativeJdbcExtractor Bean 來完成,因此我們在 ① 處為 LobHandler 注入了一個 nativeJdbcExtractor。最后,我們把 lobHandler Bean 注入到需要進行 LOB 數(shù)據(jù)訪問操作的 PostJdbcDao 中,如 ② 所示。

    如果底層數(shù)據(jù)庫是 DB2、SQL Server、MySQL 等非 Oracle 的其它數(shù)據(jù)庫,則只要簡單配置一個 DefaultLobHandler 就可以了,如下所示:


    清單 5. 一般數(shù)據(jù)庫 LobHandler 的配置
    <bean id="defaultLobHandler"
                class="org.springframework.jdbc.support.lob.DefaultLobHandler"
                lazy-init="true"/>
                <bean id="postDao" class="com.baobaotao.dao.jdbc.PostJdbcDao">
                <property name="lobHandler" ref=" defaultLobHandler"/>
                <property name="jdbcTemplate" ref="jdbcTemplate" />
                </bean>
                

    DefaultLobHandler 只是簡單地代理標準 JDBC 的 PreparedStatement 和 ResultSet 對象,由于并不需要訪問數(shù)據(jù)庫驅動本地的 JDBC 對象,所以它不需要 NativeJdbcExtractor 的幫助。您可以通過以下的代碼測試 PostJdbcDao 的 addPost() 方法:


    清單 6. 測試 PostJdbcDao 的 addPost() 方法
    package com.baobaotao.dao.jdbc;
                import org.springframework.core.io.ClassPathResource;
                import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
                import org.springframework.util.FileCopyUtils;
                import com.baobaotao.dao.PostDao;
                import com.baobaotao.domain.Post;
                public class TestPostJdbcDaoextends AbstractDependencyInjectionSpringContextTests {
                private PostDao postDao;
                public void setPostDao(PostDao postDao) {
                this.postDao = postDao;
                }
                protected String[] getConfigLocations() {
                return new String[]{"classpath:applicationContext.xml"};
                }
                public void testAddPost() throws Throwable{
                Post post = new Post();
                post.setPostId(1);
                post.setUserId(2);
                ClassPathResource res = new ClassPathResource("temp.jpg"); ① 獲取圖片資源
                byte[] mockImg = FileCopyUtils.copyToByteArray(res.getFile());  ② 讀取圖片文件的數(shù)據(jù)
                post.setPostAttach(mockImg);
                post.setPostText("測試帖子的內(nèi)容");
                postDao.addPost(post);
                }
                }
                

    這里,有幾個知識點需要稍微解釋一下:AbstractDependencyInjectionSpringContextTests 是 Spring 專門為測試提供的類,它能夠直接從 IoC 容器中裝載 Bean。此外,我們使用了 ClassPathResource 加載圖片資源,并通過 FileCopyUtils 讀取文件的數(shù)據(jù)。ClassPathResourceFileCopyUtils 都是 Spring 提供的非常實用的工具類。

    以塊數(shù)據(jù)方式讀取 LOB 數(shù)據(jù)

    您可以直接用數(shù)據(jù)塊的方式讀取 LOB 數(shù)據(jù):用 String 讀取 CLOB 字段的數(shù)據(jù),用 byte[] 讀取 BLOB 字段的數(shù)據(jù)。在 PostJdbcDao 中添加一個 getAttachs() 方法,以便獲取某一用戶的所有帶附件的帖子:


    清單 7. 以塊數(shù)據(jù)訪問 LOB 數(shù)據(jù)
    public List getAttachs(final int userId){
                String sql = "SELECT post_id,post_attach FROM t_post “+
                “where user_id =? and post_attach is not null ";
                return getJdbcTemplate().query(
                sql,new Object[] {userId},
                new RowMapper() {
                public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
                int postId = rs.getInt(1);
                ① 以二進制數(shù)組方式獲取 BLOB 數(shù)據(jù)。
                byte[] attach = lobHandler.getBlobAsBytes(rs, 2);
                Post post = new Post();
                post.setPostId(postId);
                post.setPostAttach(attach);
                return post;
                }
                });
                }
                

    通過 JdbcTemplate 的 List query(String sql, Object[] args, RowMapper rowMapper) 接口處理行數(shù)據(jù)的映射。在 RowMapper 回調(diào)的 mapRow() 接口方法中,通過 LobHandler 以 byte[] 獲取 BLOB 字段的數(shù)據(jù)。

    以流數(shù)據(jù)方式讀取 LOB 數(shù)據(jù)

    由于 LOB 數(shù)據(jù)可能很大(如 100M),如果直接以塊的方式操作 LOB 數(shù)據(jù),需要消耗大量的內(nèi)存資源,對應用程序整體性能產(chǎn)生巨大的沖擊。對于體積很大的 LOB 數(shù)據(jù),我們可以使用流的方式進行訪問,減少內(nèi)存的占用。JdbcTemplate 為此提供了一個 Object query(String sql, Object[] args, ResultSetExtractor rse) 方法,ResultSetExtractor 接口擁有一個處理流數(shù)據(jù)的抽象類 org.springframework.jdbc.core.support.AbstractLobStreamingResultSetExtractor,可以通過擴展此類用流的方式操作 LOB 字段的數(shù)據(jù)。下面我們?yōu)?PostJdbcDao 添加一個以流的方式獲取某個帖子附件的方法:


    清單 8. 以流方式訪問 LOB 數(shù)據(jù)
    …
                public void getAttach(final int postId,final OutputStream os){ ① 用于接收 LOB 數(shù)據(jù)的輸出流
                String sql = "SELECT post_attach FROM t_post WHERE post_id=? ";
                getJdbcTemplate().query(
                sql, new Object[] {postId},
                new AbstractLobStreamingResultSetExtractor() { ② 匿名內(nèi)部類
                ③ 處理未找到數(shù)據(jù)行的情況
                protected void handleNoRowFound() throws LobRetrievalFailureException {
                System.out.println("Not Found result!");
                }
                ④ 以流的方式處理 LOB 字段
                public void streamData(ResultSet rs) throws SQLException, IOException {
                InputStream is = lobHandler.getBlobAsBinaryStream(rs, 1);
                if (is != null) {
                FileCopyUtils.copy(is, os);
                }
                }
                }
                );
                }
                

    通過擴展 AbstractLobStreamingResultSetExtractor 抽象類,在 streamData(ResultSet rs) 方法中以流的方式讀取 LOB 字段數(shù)據(jù),如 ④ 所示。這里我們又利用到了 Spring 的工具類 FileCopyUtils 將輸入流的數(shù)據(jù)拷貝到輸出流中。在 getAttach() 方法中通過入?yún)?OutputStream os 接收 LOB 的數(shù)據(jù),如 ① 所示。您可以同時覆蓋抽象類中的 handleNoRowFound() 方法,定義未找到數(shù)據(jù)行時的處理邏輯。





    回頁首


    在 JPA 中操作 LOB 數(shù)據(jù)

    在 JPA 中 LOB 類型的持久化更加簡單,僅需要通過特殊的 LOB 注釋(Annotation)就可以達到目的。我們對 Post 中的 LOB 屬性類型進行注釋:


    清單 9. 注釋 LOB 類型屬性
    package com.baobaotao.domain;
                …
                import javax.persistence.Basic;
                import javax.persistence.Lob;
                import javax.persistence. Column;
                @Entity(name = "T_POST")
                public class Post implements Serializable {
                …
                @Lob ①-1 表示該屬性是 LOB 類型的字段
                @Basic(fetch = FetchType.EAGER) ①-2 不采用延遲加載機制
                @Column(name = "POST_TEXT", columnDefinition = "LONGTEXT NOT NULL") ①-3 對應字段類型
                private String postText;
                @Lob ②-1 表示該屬性是 LOB 類型的字段
                @Basic(fetch = FetchType. LAZY) ②-2 采用延遲加載機制
                @Column(name = "POST_ATTACH", columnDefinition = "BLOB") ②-3 對應字段類型
                private byte[] postAttach;
                …
                }
                

    postText 屬性對應 T_POST 表的 POST_TEXT 字段,該字段的類型是 LONTTEXT,并且非空。JPA 通過 @Lob 將屬性標注為 LOB 類型,如 ①-1 和 ②-1 所示。通過 @Basic 指定 LOB 類型數(shù)據(jù)的獲取策略,FetchType.EAGER 表示非延遲加載,而 FetchType.LAZY 表示延遲加載,如 ①-2 和 ②-2 所示。通過 @ColumncolumnDefinition 屬性指定數(shù)據(jù)表對應的 LOB 字段類型,如 ①-3 和 ②-3 所示。

    關于 JPA 注釋的更多信息,請閱讀 參考資源 中的相關技術文章。





    回頁首


    在 Hibernate 中操作 LOB 數(shù)據(jù)

    提示

    使用 Spring JDBC 時,我們除了可以按 byte[]、String 類型處理 LOB 數(shù)據(jù)外,還可以使用流的方式操作 LOB 數(shù)據(jù),當 LOB 數(shù)據(jù)體積較大時,流操作是唯一可行的方式。可惜,Spring 并未提供以流方式操作 LOB 數(shù)據(jù)的 UserType(記得 Spring 開發(fā)組成員認為在實現(xiàn)上存在難度)。不過,www.atlassian.com 替 Spring 完成了這件難事,讀者可以通過 這里 了解到這個滿足要求的 BlobInputStream 類型。

    Hibernate 為處理特殊數(shù)據(jù)類型字段定義了一個接口:org.hibernate.usertype.UserType。Spring 在 org.springframework.orm.hibernate3.support 包中為 BLOB 和 CLOB 類型提供了幾個 UserType 的實現(xiàn)類。因此,我們可以在 Hibernate 的映射文件中直接使用這兩個實現(xiàn)類輕松處理 LOB 類型的數(shù)據(jù)。

    • BlobByteArrayType:將 BLOB 數(shù)據(jù)映射為 byte[] 類型的屬性;
    • BlobStringType:將 BLOB 數(shù)據(jù)映射為 String 類型的屬性;
    • BlobSerializableType:將 BLOB 數(shù)據(jù)映射為 Serializable 類型的屬性;
    • ClobStringType:將 CLOB 數(shù)據(jù)映射為 String 類型的屬性;

    下面我們使用 Spring 的 UserType 為 Post 配置 Hibernate 的映射文件,如 清單 10 所示:


    清單 10 . LOB 數(shù)據(jù)映射配置
    <?xml version="1.0" encoding="UTF-8"?>
                <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
                "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
                <hibernate-mapping auto-import="true" default-lazy="false">
                <class name="com.baobaotao.domain.Post" table="t_post">
                <id name="postId" column="post_id">
                <generator class="identity" />
                </id>
                <property name="userId" column="user_id"/>
                <property name="postText" column="post_text"
                type="org.springframework.orm.hibernate3.support.ClobStringType"/>①對應 CLOB 字段
                <property name="postAttach" column="post_attach"
                type="org.springframework.orm.hibernate3.support.BlobByteArrayType"/>② BLOB 字段
                <property name="postTime" column="post_time" type="date" />
                <many-to-one name="topic" column="topic_id" class="com.baobaotao.domain.Topic" />
                </class>
                </hibernate-mapping>
                

    postTextString 類型的屬性,對應數(shù)據(jù)庫的 CLOB 類型,而 postAttachbyte[] 類型的屬性,對應數(shù)據(jù)庫的 BLOB 類型。分別使用 Spring 所提供的相應 UserType 實現(xiàn)類進行配置,如 ① 和 ② 處所示。

    在配置好映射文件后,還需要在 Spring 配置文件中定義 LOB 數(shù)據(jù)處理器,讓 SessionFactory 擁有處理 LOB 數(shù)據(jù)的能力:


    清單 11 . 將 LobHandler 注入到 SessionFactory 中
    …
                <bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler"
                lazy-init="true" />
                <bean id="sessionFactory"
                class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
                <property name="dataSource" ref="dataSource" />
                <property name="lobHandler" ref="lobHandler" /> ① 設置 LOB 處理器
                …
                </bean>
                

    在一般的數(shù)據(jù)庫(如 DB2)中,僅需要簡單地使用 HibernateTemplate#save(Object entity) 等方法就可以正確的保存 LOB 數(shù)據(jù)了。如果是 Oracle 9i 數(shù)據(jù)庫,還需要配置一個本地 JDBC 抽取器,并使用特定的 LobHandler 實現(xiàn)類,如 清單 4 所示。

    使用 LobHandler 操作 LOB 數(shù)據(jù)時,需要在事務環(huán)境下才能工作,所以必須事先配置事務管理器,否則會拋出異常。





    回頁首


    在 iBatis 中操作 LOB 數(shù)據(jù)

    iBatis 為處理不同類型的數(shù)據(jù)定義了一個統(tǒng)一的接口:com.ibatis.sqlmap.engine.type.TypeHandler。這個接口類似于 Hibernate 的 UserType。iBatis 本身擁有該接口的眾多實現(xiàn)類,如 LongTypeHandler、DateTypeHandler 等,但沒有為 LOB 類型提供對應的實現(xiàn)類。Spring 在 org.springframework.orm.ibatis.support 包中為我們提供了幾個處理 LOB 類型的 TypeHandler 實現(xiàn)類:

    • BlobByteArrayTypeHandler:將 BLOB 數(shù)據(jù)映射為 byte[] 類型;
    • BlobSerializableTypeHandler:將 BLOB 數(shù)據(jù)映射為 Serializable 類型的對象;
    • ClobStringTypeHandler:將 CLOB 數(shù)據(jù)映射為 String 類型;

    當結果集中包括 LOB 數(shù)據(jù)時,需要在結果集映射配置項中指定對應的 Handler 類,下面我們采用 Spring 所提供的實現(xiàn)類對 Post 結果集的映射進行配置。


    清單 12 . 對 LOB 數(shù)據(jù)進行映射
    <?xml version="1.0" encoding="UTF-8" ?>
                <!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
                "http://ibatis.apache.org/dtd/sql-map-2.dtd">
                <sqlMap namespace="Post">
                <typeAlias alias="post" type="com.baobaotao.domain.Post"/>
                <resultMap id="result" class="post">
                <result property="postId" column="post_id"/>
                <result property="userId" column="user_id"/>
                <result property="postText" column="post_text" ① 讀取 CLOB 類型數(shù)據(jù)
                typeHandler="org.springframework.orm.ibatis.support.ClobStringTypeHandler"/>
                <result property="postAttach" column="post_attach" ② 讀取 BLOB 類型數(shù)據(jù)
                typeHandler="org.springframework.orm.ibatis.support.BlobByteArrayTypeHandler"/>
                </resultMap>
                <select id="getPost" resultMap="result">
                SELECT post_id,user_id,post_text,post_attach,post_time
                FROM t_post  WHERE post_id =#postId#
                </select>
                <insert id="addPost">
                INSERT INTO t_post(user_id,post_text,post_attach,post_time)
                VALUES(#userId#,
                #postText,handler=org.springframework.orm.ibatis.support.ClobStringTypeHandler#, ③
                #postAttach,handler=org.springframework.orm.ibatis.support.BlobByteArrayTypeHandler#, ④
                #postTime#)
                </insert>
                </sqlMap>
                

    提示

    為每一個 LOB 類型字段分別指定處理器并不是一個好主意,iBatis 允許在 sql-map-config.xml 配置文件中通過 <typeHandler> 標簽統(tǒng)一定義特殊類型數(shù)據(jù)的處理器,如:

    <typeHandler jdbcType="CLOB" javaType="java.lang.String" callback="org.springframework.orm.ibatis.support.ClobStringTypeHandler"/>

    當 iBatis 引擎從結果集中讀取或更改 LOB 類型數(shù)據(jù)時,都需要指定處理器。我們在 ① 和 ② 處為讀取 LOB 類型的數(shù)據(jù)指定處理器,相似的,在 ③ 和 ④ 處為插入 LOB 類型的數(shù)據(jù)也指定處理器。

    此外,我們還必須為 SqlClientMap 提供一個 LobHandler:


    清單 13. 將 LobHandler 注入到 SqlClientMap 中
    <bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler"
                lazy-init="true" />
                <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
                <property name="dataSource" ref="dataSource" />
                <property name="lobHandler" ref="lobHandler" /> ①設置LobHandler
                <property name="configLocation"
                value="classpath:com/baobaotao/dao/ibatis/sql-map-config.xml" />
                </bean>
                

    處理 LOB 數(shù)據(jù)時,Spring 要求在事務環(huán)境下工作,所以還必須配置一個事務管理器。iBatis 的事務管理器和 Spring JDBC 事務管理器相同,此處不再贅述。

    posted on 2009-10-22 17:22 小蟲旺福 閱讀(353) 評論(0)  編輯  收藏 所屬分類: spring相關
    主站蜘蛛池模板: 亚洲欧美国产国产一区二区三区| 亚洲国产日韩在线人成下载 | fc2免费人成在线视频| 亚洲乱码国产一区三区| 99久久99这里只有免费费精品| 国产高潮流白浆喷水免费A片 | 国产黄色片免费看| 亚洲福利视频一区| 国产视频精品免费| 久久精品一区二区免费看| 亚洲乱人伦精品图片| 亚洲精品综合久久| 亚洲性线免费观看视频成熟 | MM1313亚洲精品无码久久| 精品久久香蕉国产线看观看亚洲| 五月婷婷在线免费观看| 猫咪免费人成网站在线观看入口| 亚洲精品成人av在线| 国产嫩草影院精品免费网址| 亚洲视频在线免费观看| 亚洲а∨精品天堂在线| 亚洲视频一区二区三区| 亚洲精品国产电影| 成年轻人网站色免费看| 在线看片免费人成视频福利| 亚洲Av永久无码精品一区二区| 久久青青草原亚洲av无码app| 人人狠狠综合久久亚洲高清| 美女视频黄的全免费视频| baoyu116.永久免费视频| 亚洲a无码综合a国产av中文| 久久亚洲AV永久无码精品| 最近中文字幕免费mv视频7| 丰满人妻一区二区三区免费视频| 亚洲欧美日韩中文二区| 911精品国产亚洲日本美国韩国| 免费人成网站在线播放| 中国在线观看免费国语版| 嫩草在线视频www免费观看| 免费人成大片在线观看播放| 亚洲成人激情小说|