LOBs 映射有两种形式:

  • 使用 JDBC locator types
  • materializing the LOB data

JDBC LOB locators 用于高效访问 LOB data, 允许 JDBC driver 传送部分数据,但是有限制,比如一个 LOB locator 只在事务之内才可用。

materialized LOBs 则将整个 LOB 数据放入内存,使用 String, char[], byte[] 存储数据,但不够高效。

JDBC LOB locator types 包括

  • java.sql.Blob
  • java.sql.Clob
  • java.sql.NClob

JDBC locator types - Clob

package org.example.demo.hibernate;

import java.io.BufferedReader;
import java.io.Reader;
import java.sql.Clob;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Persistence;

import org.hibernate.engine.jdbc.ClobProxy;

public class Test {
    public static void main(String[] args) throws Exception {
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
        EntityManager em = factory.createEntityManager();

        Product product = new Product();
        product.warranty = ClobProxy.generateProxy("My product warranty\nnew line\nnew line again");

        em.getTransaction().begin();
        em.persist(product);
        em.getTransaction().commit();

        em.clear();

        em.getTransaction().begin();
        Product p = em.find(Product.class, product.id);
        try (Reader reader = p.warranty.getCharacterStream()) {
            BufferedReader in = new BufferedReader(reader);
            String line;
            while ((line = in.readLine()) != null) {
                // My product warranty
                // new line
                // new line again
                System.out.println(line);
            }
        }
        em.getTransaction().commit();

        em.close();
        factory.close();
    }

    @Entity(name = "Product")
    public static class Product {
        @Id
        @GeneratedValue
        Integer id;
        @Lob
        Clob warranty;
    }
}

注意,读取 Clob 的代码应该在事务中,即第 31 行和第 43 行的事务应该是必须的,但在本例中,去掉它们也不影响。

生成的 SQL

drop table if exists Product cascade
drop sequence if exists hibernate_sequence

create sequence hibernate_sequence start 1 increment 1
create table Product (id int4 not null, warranty text, primary key (id))
select nextval ('hibernate_sequence')
insert into Product (warranty, id) values ('My product warranty new line new line again', 1)

select test_produ0_.id as id1_0_0_, test_produ0_.warranty as warranty2_0_0_ from Product test_produ0_ where test_produ0_.id=1

materializing the LOB data - Clob

package org.example.demo.hibernate;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Persistence;

public class Test {
    public static void main(String[] args) throws Exception {
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
        EntityManager em = factory.createEntityManager();

        Product product = new Product();
        product.warranty = "My product warranty";

        em.getTransaction().begin();
        em.persist(product);
        em.getTransaction().commit();

        em.clear();

        Product p = em.find(Product.class, product.id);
        // My product warranty
        System.out.println(p.warranty);

        em.close();
        factory.close();
    }

    @Entity(name = "Product")
    public static class Product {
        @Id
        @GeneratedValue
        Integer id;
        @Lob
        String warranty;
    }
}

生成的 SQL

drop table if exists Product cascade
drop sequence if exists hibernate_sequence

create sequence hibernate_sequence start 1 increment 1
create table Product (id int4 not null, warranty text, primary key (id))
select nextval ('hibernate_sequence')
insert into Product (warranty, id) values ('My product warranty', 1)

select test_produ0_.id as id1_0_0_, test_produ0_.warranty as warranty2_0_0_ from Product test_produ0_ where test_produ0_.id=1

JDBC 处理 LOB data 的方式各不相同, Hibernate 努力做到统一,但还是有些不能统一,例如 PostgreSQL JDBC drivers 这时需要额外的工作。

使用 char[]

package org.example.demo.hibernate;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Persistence;

public class Test {
    public static void main(String[] args) throws Exception {
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
        EntityManager em = factory.createEntityManager();

        Product product = new Product();
        product.warranty = "My product warranty".toCharArray();

        em.getTransaction().begin();
        em.persist(product);
        em.getTransaction().commit();

        em.clear();

        Product p = em.find(Product.class, product.id);
        // My product warranty
        System.out.println(p.warranty);

        em.close();
        factory.close();
    }

    @Entity(name = "Product")
    public static class Product {
        @Id
        @GeneratedValue
        Integer id;
        @Lob
        char[] warranty;
    }
}

生成的 SQL

drop table if exists Product cascade
drop sequence if exists hibernate_sequence

create sequence hibernate_sequence start 1 increment 1
create table Product (id int4 not null, warranty text, primary key (id))
select nextval ('hibernate_sequence')
insert into Product (warranty, id) values ('My product warranty', 1)

select test_produ0_.id as id1_0_0_, test_produ0_.warranty as warranty2_0_0_ from Product test_produ0_ where test_produ0_.id=1

JDBC locator types - Blob

package org.example.demo.hibernate;

import java.io.BufferedInputStream;
import java.io.InputStream;
import java.sql.Blob;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Persistence;

import org.hibernate.engine.jdbc.BlobProxy;

public class Test {
    public static void main(String[] args) throws Exception {
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
        EntityManager em = factory.createEntityManager();

        Product product = new Product();
        product.image = BlobProxy.generateProxy(new byte[] { 1, 2, 3 });

        em.getTransaction().begin();
        em.persist(product);
        em.getTransaction().commit();

        em.clear();

        em.getTransaction().begin();
        Product p = em.find(Product.class, product.id);
        try (InputStream in = p.image.getBinaryStream()) {
            BufferedInputStream bis = new BufferedInputStream(in);
            int i;
            while ((i = bis.read()) != -1) {
                // 1,2,3,
                System.out.print(i + ",");
            }
        }
        System.out.println();
        em.getTransaction().commit();

        em.close();
        factory.close();
    }

    @Entity(name = "Product")
    public static class Product {
        @Id
        @GeneratedValue
        Integer id;
        @Lob
        Blob image;
    }
}

注意,读取 Blob 的代码必须在事务中,即第 31 行和第 42 行的事务是必须的,否则抛异常 org.postgresql.util.PSQLException: 大型对象无法被使用在自动确认事物交易模式。

生成的 SQL

drop table if exists Product cascade
drop sequence if exists hibernate_sequence

create sequence hibernate_sequence start 1 increment 1
create table Product (id int4 not null, image oid, primary key (id))
select nextval ('hibernate_sequence')
insert into Product (image, id) values ('org.hibernate.engine.jdbc.BlobProxy@6548bb7d', 1)

select test_produ0_.id as id1_0_0_, test_produ0_.image as image2_0_0_ from Product test_produ0_ where test_produ0_.id=1

materializing the LOB data - Blob

package org.example.demo.hibernate;

import java.util.Arrays;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Persistence;

public class Test {
    public static void main(String[] args) throws Exception {
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
        EntityManager em = factory.createEntityManager();

        Product product = new Product();
        product.image = new byte[] { 1, 2, 3 };

        em.getTransaction().begin();
        em.persist(product);
        em.getTransaction().commit();

        em.clear();

        Product p = em.find(Product.class, product.id);
        // [1, 2, 3]
        System.out.println(Arrays.toString(p.image));

        em.close();
        factory.close();
    }

    @Entity(name = "Product")
    public static class Product {
        @Id
        @GeneratedValue
        Integer id;
        @Lob
        byte[] image;
    }
}

生成的 SQL

drop table if exists Product cascade
drop sequence if exists hibernate_sequence

create sequence hibernate_sequence start 1 increment 1
create table Product (id int4 not null, image oid, primary key (id))
select nextval ('hibernate_sequence')
insert into Product (image, id) values ('org.hibernate.engine.jdbc.BlobProxy@35e52059', 1)

select test_produ0_.id as id1_0_0_, test_produ0_.image as image2_0_0_ from Product test_produ0_ where test_produ0_.id=1

results matching ""

    No results matching ""