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