web-dev-qa-db-ja.com

Spring、Hibernate、Blobの遅延読み込み

HibernateでのレイジーなBlob読み込みについてサポートが必要です。私のウェブアプリケーションには、MySQL、Tomcat、Spring、Hibernateなどのサーバーとフレームワークがあります。

データベース構成の一部。

_<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="user" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
    <property name="driverClass" value="${jdbc.driverClassName}"/>
    <property name="jdbcUrl" value="${jdbc.url}"/>

    <property name="initialPoolSize">
        <value>${jdbc.initialPoolSize}</value>
    </property>
    <property name="minPoolSize">
        <value>${jdbc.minPoolSize}</value>
    </property>
    <property name="maxPoolSize">
        <value>${jdbc.maxPoolSize}</value>
    </property>
    <property name="acquireRetryAttempts">
        <value>${jdbc.acquireRetryAttempts}</value>
    </property>
    <property name="acquireIncrement">
        <value>${jdbc.acquireIncrement}</value>
    </property>
    <property name="idleConnectionTestPeriod">
        <value>${jdbc.idleConnectionTestPeriod}</value>
    </property>
    <property name="maxIdleTime">
        <value>${jdbc.maxIdleTime}</value>
    </property>
    <property name="maxConnectionAge">
        <value>${jdbc.maxConnectionAge}</value>
    </property>
    <property name="preferredTestQuery">
        <value>${jdbc.preferredTestQuery}</value>
    </property>
    <property name="testConnectionOnCheckin">
        <value>${jdbc.testConnectionOnCheckin}</value>
    </property>
</bean>


<bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" />

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="/WEB-INF/hibernate.cfg.xml" />
    <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">${hibernate.dialect}</prop>
        </props>
    </property>
    <property name="lobHandler" ref="lobHandler" />
</bean>

<tx:annotation-driven transaction-manager="txManager" />

<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
_

エンティティークラスの一部

_@Lob
@Basic(fetch=FetchType.LAZY)
@Column(name = "BlobField", columnDefinition = "LONGBLOB")
@Type(type = "org.springframework.orm.hibernate3.support.BlobByteArrayType")
private byte[] blobField;
_

問題の説明。 MySQLデータベースに保存されたファイルに関連するデータベースレコードをWebページに表示しようとしています。データ量が少ない場合は、すべて正常に機能します。しかし、データ量が多いエラーを受け取っています_Java.lang.OutOfMemoryError: Java heap space_テーブルの各行にblobFields null値を書き込もうとしました。この場合、アプリケーションは正常に動作し、メモリが不足することはありません。レイジー(@Basic(fetch=FetchType.LAZY))としてマークされているblobフィールドはレイジーではないという結論に達しました。

25

よくわかりません。 Emmanuel Bernardが ANN-418 に書いたところ、_@Lob_はデフォルトでレイジーです(つまり、@Basic(fetch = FetchType.LAZY)アノテーションを使用する必要すらありません)。

一部のユーザーは、_@Lob_ すべてのドライバー/データベースでは機能しない の遅延読み込みを報告しています。

bytecode instrumentation (javassit?cglib?)を使用すると機能するという報告があります。

しかし、私はドキュメントでこれに関するすべての明確な参照を見つけることができません。

最後に、 推奨される回避策 は、プロパティの代わりに「偽の」1対1のマッピングを使用することです。既存のクラスからLOBフィールドを削除し、同じテーブル、同じ主キー、およびプロパティとして必要なLOBフィールドのみを参照する新しいクラスを作成します。マッピングを1対1、fetch = "select"、lazy = "true"として指定します。親オブジェクトがまだセッションにある限り、必要なものを正確に取得する必要があります(これを注釈に置き換えてください)。

32
Pascal Thivent

もちろん、その値を抽出して、「@ OneToOne」関係を持つ遅延テーブルに新しい値を入れることもできますが、このアプリケーションでは、この構成だけを使用して、LOBがオンデマンドで遅延ロードされます。

@Lob
@Fetch(FetchMode.SELECT)
@Type(type="org.hibernate.type.PrimitiveByteArrayBlobType")
byte[] myBlob;

これは、PostgreSQL、MySQL、SQLServer、Oracleで同時にプロジェクトでテストされているため、uで動作するはずです。

5
Hons

このシナリオを処理するには、継承を使用することをお勧めします。 blobのない基本クラスと、バイト配列を含む派生クラスを用意します。 UIにblobを表示する必要がある場合にのみ、派生クラスを使用します。

5
Darin Dimitrov

遅延プロパティの読み込みには、ビルド時のバイトコードインストルメンテーションが必要です。

Hibernateドキュメント:レイジープロパティフェッチの使用

バイトコードのインストルメンテーションを回避する場合、1つのオプションは、同じテーブルを使用する2つのエンティティを作成することです。次に、ブロブが必要なときにのみ、エンティティをブロブで使用します。

4
Ben George

私は同じ問題を抱えていましたが、これが私の修正でした:

私のエンティティ:

@Entity
@Table(name = "file")
public class FileEntity {

@Id
@GeneratedValue
private UUID id;

@NotNull
private String filename;

@NotNull
@Lob @Basic(fetch = FetchType.LAZY)
private byte[] content;

...

プラグインをpom.xmlに追加:

        <plugin>
            <groupId>org.hibernate.orm.tooling</groupId>
            <artifactId>hibernate-enhance-maven-plugin</artifactId>
            <executions>
                <execution>
                    <phase>compile</phase>
                    <configuration>
                        <failOnError>true</failOnError>
                        <enableLazyInitialization>true</enableLazyInitialization>
                    </configuration>
                    <goals>
                        <goal>enhance</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
3

私にとって、遅延ロードはコンパイルしてから実行することでのみ機能し、Eclipseやintellijなどでは機能しませんでした。

私はgradleを使用していますが、それを機能させるために次のことを行いました

  • エンティティに注釈を付ける
  • Hibernate Gradleプラグインのセットアップ

build.gradle

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "org.hibernate:hibernate-gradle-plugin:5.4.0.Final"
    }
}

apply plugin: 'Java'
apply plugin: 'application'
apply plugin: 'org.hibernate.orm'
hibernate {
    enhance {
        enableLazyInitialization = true
        enableDirtyTracking = true
        enableAssociationManagement = true
    }
}

Entity.Java

@Entity
public class Person {

    @Id
    @GeneratedValue
    private Integer id;

    @Lob
    @Basic(fetch = FetchType.LAZY)
    @Column(length = 255, nullable = false)
    private String name;

テスト中

./gradlew run

完全な動作例

1
deFreitas