gpt4 book ai didi

java - 如何使用 Hibernate 在 Oracle 中保留 LARGE BLOB (>100MB)

转载 作者:IT老高 更新时间:2023-10-28 13:44:06 25 4
gpt4 key购买 nike

我正在努力寻找一种使用 BLOB 列在我的 Oracle 数据库中插入大图像(> 100MB,主要是 TIFF 格式)的方法。

我已经在网络上甚至在 StackOverflow 中进行了彻底的搜索,但没有找到这个问题的答案。
首先,问题......然后是相关代码(java类/配置)的一小部分,最后是第三部分,我展示了我为测试图像持久性而编写的junit测试(我在junit期间收到错误测试执行)

编辑:我在问题的末尾添加了一个部分,在那里我描述了一些使用 JConsole 的测试和分析

问题

我收到了 java.lang.OutOfMemoryError: Java heap space使用 hibernate 并尝试保留非常大的图像/文档时出错:

java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2786)
at java.io.ByteArrayOutputStream.toByteArray(ByteArrayOutputStream.java:133)
at org.hibernate.type.descriptor.java.DataHelper.extractBytes(DataHelper.java:190)
at org.hibernate.type.descriptor.java.BlobTypeDescriptor.unwrap(BlobTypeDescriptor.java:123)
at org.hibernate.type.descriptor.java.BlobTypeDescriptor.unwrap(BlobTypeDescriptor.java:47)
at org.hibernate.type.descriptor.sql.BlobTypeDescriptor$4$1.doBind(BlobTypeDescriptor.java:101)
at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:91)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:283)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:278)
at org.hibernate.type.AbstractSingleColumnStandardBasicType.nullSafeSet(AbstractSingleColumnStandardBasicType.java:89)
at org.hibernate.persister.entity.AbstractEntityPersister.dehydrate(AbstractEntityPersister.java:2184)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2430)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2874)
at org.hibernate.action.EntityInsertAction.execute(EntityInsertAction.java:79)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:265)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:184)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
at it.paoloyx.blobcrud.manager.DocumentManagerTest.testInsertDocumentVersion(DocumentManagerTest.java:929)

代码(域对象、存储库类、配置)

这是我正在使用的技术堆栈(从数据库到业务逻辑层)。我使用JDK6。
  • Oracle 数据库 10g 企业版 10.2.0.4.0 版 - 产品
  • ojdbc6.jar(适用于 11.2.0.3 版本)
  • Hibernate 4.0.1 最终版
  • Spring 3.1.GA 发布

  • 我有两个域类,以一对多的方式映射。一个 DocumentVersion有很多 DocumentData ,每一个都可以代表同一个 DocumentVersion 的不同二进制内容.

    相关摘录自 DocumentVersion类(class):
    @Entity
    @Table(name = "DOCUMENT_VERSION")
    public class DocumentVersion implements Serializable {

    private static final long serialVersionUID = 1L;
    private Long id;
    private Set<DocumentData> otherDocumentContents = new HashSet<DocumentData>(0);


    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    @Column(name = "DOV_ID", nullable = false)
    public Long getId() {
    return id;
    }

    @OneToMany
    @Cascade({ CascadeType.SAVE_UPDATE })
    @JoinColumn(name = "DOD_DOCUMENT_VERSION")
    public Set<DocumentData> getOtherDocumentContents() {
    return otherDocumentContents;
    }

    相关摘录自 DocumentData类(class):
    @Entity
    @Table(name = "DOCUMENT_DATA")
    public class DocumentData {

    private Long id;

    /**
    * The binary content (java.sql.Blob)
    */
    private Blob binaryContent;

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    @Column(name = "DOD_ID", nullable = false)
    public Long getId() {
    return id;
    }

    @Lob
    @Column(name = "DOD_CONTENT")
    public Blob getBinaryContent() {
    return binaryContent;
    }

    这是我的 Spring 和 Hibernate 配置主要参数:
    <bean id="sessionFactory"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="packagesToScan" value="it.paoloyx.blobcrud.model" />
    <property name="hibernateProperties">
    <props>
    <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
    <prop key="hibernate.hbm2ddl.auto">create</prop>
    <prop key="hibernate.jdbc.batch_size">0</prop>
    <prop key="hibernate.jdbc.use_streams_for_binary">true</prop>
    </props>
    </property>
    </bean>
    <bean class="org.springframework.orm.hibernate4.HibernateTransactionManager"
    id="transactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager" />

    我的数据源定义:
    <bean class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close" id="dataSource">
    <property name="driverClassName" value="${database.driverClassName}" />
    <property name="url" value="${database.url}" />
    <property name="username" value="${database.username}" />
    <property name="password" value="${database.password}" />
    <property name="testOnBorrow" value="true" />
    <property name="testOnReturn" value="true" />
    <property name="testWhileIdle" value="true" />
    <property name="timeBetweenEvictionRunsMillis" value="1800000" />
    <property name="numTestsPerEvictionRun" value="3" />
    <property name="minEvictableIdleTimeMillis" value="1800000" />
    <property name="validationQuery" value="${database.validationQuery}" />
    </bean>

    从这里获取属性的地方:
    database.driverClassName=oracle.jdbc.OracleDriver
    database.url=jdbc:oracle:thin:@localhost:1521:devdb
    database.username=blobcrud
    database.password=blobcrud
    database.validationQuery=SELECT 1 from dual

    我有一个服务类,它委托(delegate)给一个存储库类:
    @Transactional
    public class DocumentManagerImpl implements DocumentManager {

    DocumentVersionDao documentVersionDao;

    public void setDocumentVersionDao(DocumentVersionDao documentVersionDao) {
    this.documentVersionDao = documentVersionDao;
    }

    现在是存储库类的相关摘录:
    public class DocumentVersionDaoHibernate implements DocumentVersionDao {

    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public DocumentVersion saveOrUpdate(DocumentVersion record) {
    this.sessionFactory.getCurrentSession().saveOrUpdate(record);
    return record;
    }

    导致错误的 JUnit 测试

    如果我运行以下单元测试,我会遇到上述错误( java.lang.OutOfMemoryError: Java heap space ):
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "classpath*:META-INF/spring/applicationContext*.xml" })
    @Transactional
    public class DocumentManagerTest {

    @Autowired
    protected DocumentVersionDao documentVersionDao;

    @Autowired
    protected SessionFactory sessionFactory;

    @Test
    public void testInsertDocumentVersion() throws SQLException {

    // Original mock document content
    DocumentData dod = new DocumentData();
    // image.tiff is approx. 120MB
    File veryBigFile = new File("/Users/paoloyx/Desktop/image.tiff");
    try {
    Session session = this.sessionFactory.getCurrentSession();
    InputStream inStream = FileUtils.openInputStream(veryBigFile);
    Blob blob = Hibernate.getLobCreator(session).createBlob(inStream, veryBigFile.length());
    dod.setBinaryContent(blob);
    } catch (IOException e) {
    e.printStackTrace();
    dod.setBinaryContent(null);
    }

    // Save a document version linked to previous document contents
    DocumentVersion dov = new DocumentVersion();
    dov.getOtherDocumentContents().add(dod);
    documentVersionDao.saveOrUpdate(dov);
    this.sessionFactory.getCurrentSession().flush();

    // Clear session, then try retrieval
    this.sessionFactory.getCurrentSession().clear();
    DocumentVersion dbDov = documentVersionDao.findByPK(insertedId);
    Assert.assertNotNull("Il document version ritornato per l'id " + insertedId + " è nullo", dbDov);
    Assert.assertNotNull("Il document version recuperato non ha associato contenuti aggiuntivi", dbDov.getOtherDocumentContents());
    Assert.assertEquals("Il numero di contenuti secondari non corrisponde con quello salvato", 1, dbDov.getOtherDocumentContents().size());
    }

    相同的代码适用于 PostreSQL 9 安装。图像正在写入数据库中。
    调试我的代码时,我发现 PostgreSQL jdbc 驱动程序使用缓冲输出流写入数据库......而 Oracle OJDBC 驱动程序尝试一次分配所有 byte[]代表图像。

    从错误堆栈:
    java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:2786)
    at java.io.ByteArrayOutputStream.toByteArray(ByteArrayOutputStream.java:133)

    错误是由于这种行为吗?
    谁能给我一些关于这个问题的见解?

    谢谢大家。

    使用 JConsole 进行内存测试

    感谢收到的对我的问题的建议,我尝试使用两种不同的 jdbc 驱动程序进行一些简单的测试,以显示我的代码的内存使用情况,一种用于 PostgreSQL,一种用于 Oracle。
    测试设置:
  • 该测试是使用上一节中描述的 JUnit 测试进行的。
  • JVM 堆大小已设置为 512MB,使用参数 -Xmx512MB
  • 对于 Oracle 数据库,我使用了 ojdbc6.jar 驱动程序
  • 对于 Postgres 数据库,我使用了 9.0-801.jdbc3 驱动程序(通过 Maven)

  • 第一次测试,文件大小约为 150MB

    在第一次测试中,Oracle 和 Postgres 已通过 测试(这是大新闻)。
    该文件的大小是可用 JVM 堆大小的 1/3。
    这是JVM内存消耗的图片:

    测试 Oracle,512MB 堆大小,150MB 文件
    Testing Oracle, 512MB Heap Size, 150MB file

    测试 PostgreSQL,512MB 堆大小,150MB 文件
    Testing PostgreSQL, 512MB Heap Size, 150MB file

    第二次测试,文件大小约为 485MB

    在第二次测试中,只有 Postgres 通过了测试,而 Oracle 失败 它 。
    该文件的大小非常接近可用 JVM 堆空间的大小。
    这是JVM内存消耗的图片:

    测试 Oracle,512MB 堆大小,485MB 文件
    Testing Oracle, 512MB Heap Size, 485MB file

    测试 PostgreSQL,512MB 堆大小,485MB 文件
    Testing PostgreSQL, 512MB Heap Size, 485MB file

    测试分析:

    似乎 PostgreSQL 驱动程序在不超过某个阈值的情况下处理内存,而 Oracle 驱动程序的行为非常不同。

    我无法诚实地解释为什么 Oracle jdbc 驱动程序在使用大小接近可用堆空间的文件时导致我出错(相同的 java.lang.OutOfMemoryError: Java heap space )。

    有没有人可以给我更多的见解?
    非常感谢你的帮助:)

    最佳答案

    我在尝试使用“blob”类型进行映射时遇到了与您相同的问题。这是我在 hibernate 网站上发表的帖子的链接:https://forum.hibernate.org/viewtopic.php?p=2452481#p2452481

    hibernate 3.6.9
    Oracle 驱动程序 11.2.0.2.0
    Oracle 数据库 11.2.0.2.0

    为了解决这个问题,我使用了为 Blob 提供自定义 UserType 的代码,我将返回类型设为 java.sql.Blob。

    下面是这个 UserType 的关键方法实现:

    public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {

    Blob blob = rs.getBlob(names[0]);
    if (blob == null)
    return null;

    return blob;
    }

    public void nullSafeSet(PreparedStatement st, Object value, int index)
    throws HibernateException, SQLException {
    if (value == null) {
    st.setNull(index, sqlTypes()[0]);
    }
    else {
    InputStream in = null;
    OutputStream out = null;
    // oracle.sql.BLOB
    BLOB tempBlob = BLOB.createTemporary(st.getConnection(), true, BLOB.DURATION_SESSION);
    tempBlob.open(BLOB.MODE_READWRITE);
    out = tempBlob.getBinaryOutputStream();
    Blob valueAsBlob = (Blob) value;
    in = valueAsBlob.getBinaryStream();
    StreamUtil.toOutput(in, out);
    out.flush();
    StreamUtil.close(out);
    tempBlob.close();
    st.setBlob(index, tempBlob);
    StreamUtil.close(in);
    }
    }

    关于java - 如何使用 Hibernate 在 Oracle 中保留 LARGE BLOB (>100MB),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9253323/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com