Java Spring、Hibernate、Blob 延迟加载

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2605477/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 09:47:00  来源:igfitidea点击:

Spring, Hibernate, Blob lazy loading

javamysqlhibernatelazy-loadingblob

提问by Alexey Khudyakov

I need help with lazy blob loading in Hibernate. I have in my web application these servers and frameworks: MySQL, Tomcat, Spring and Hibernate.

我需要有关 Hibernate 中延迟 blob 加载的帮助。我的 Web 应用程序中有这些服务器和框架:MySQL、Tomcat、Spring 和 Hibernate。

The part of database config.

数据库配置部分。

<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>

The part of entity class

实体类部分

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

The problem description. I'm trying to display on a web page database records related to files, which was saved in MySQL database. All works fine if a volume of data is small. But the volume of data is big I'm recieving an error java.lang.OutOfMemoryError: Java heap spaceI've tried to write in blobFields null values on each row of table. In this case, application works fine, memory doesn't go out of. I have a conclusion that the blob field which is marked as lazy (@Basic(fetch=FetchType.LAZY)) isn't lazy, actually!

问题描述。我试图在网页上显示与文件相关的数据库记录,这些记录保存在 MySQL 数据库中。如果数据量很小,一切正常。但是数据量很大我收到一个错误,java.lang.OutOfMemoryError: Java heap space我试图在表的每一行的 blobFields 空值中写入。在这种情况下,应用程序工作正常,内存不会耗尽。我有一个结论,标记为惰性 ( @Basic(fetch=FetchType.LAZY))的 blob 字段实际上并不惰性!

回答by Darin Dimitrov

I would suggest you to use inheritance to handle this scenario. Have a base class without the blob and a derived class containing the byte array. You would use the derived class only when you need to display the blob on the UI.

我建议您使用继承来处理这种情况。有一个没有 blob 的基类和一个包含字节数组的派生类。仅当需要在 UI 上显示 blob 时才使用派生类。

回答by Pascal Thivent

I'm confused. Emmanuel Bernard wrote in ANN-418that @Lobare lazy by default (i.e. you don't even need to use the @Basic(fetch = FetchType.LAZY)annotation).

我糊涂了。Emmanuel Bernard 在ANN-418中写道,@Lob默认情况下是惰性的(即您甚至不需要使用@Basic(fetch = FetchType.LAZY)注释)。

Some users report that lazy loading of a @Lobdoesn't work with all drivers/database.

一些用户报告说,延迟加载 a@Lob不适用于所有驱动程序/数据库

Some users report that it works when using bytecode instrumentation(javassit? cglib?).

一些用户报告说它在使用字节码检测(javassit?cglib?)时有效。

But I can't find any clear reference of all this in the documentation.

但是我在文档中找不到所有这些的明确参考。

At the end, the recommended workaroundis to use a "fake" one-to-one mappings instead of properties. Remove the LOB fields from your existing class, create new classes referring to the same table, same primary key, and only the necessary LOB fields as properties. Specify the mappings as one-to-one, fetch="select", lazy="true". So long as your parent object is still in your session, you should get exactly what you want.(just transpose this to annotations).

最后,推荐的解决方法是使用“假”一对一映射而不是属性。从现有类中删除 LOB 字段,创建引用相同表、相同主键和仅作为属性的必要 LOB 字段的新类。将映射指定为一对一,fetch="select",lazy="true"。只要你的父对象还在你的会话中,你就应该得到你想要的。(只需将其转换为注释)。

回答by Hons

Of course you could extract that value and put it into a new table with a "@OneToOne" relation that is lazy, however in our application the LOBs are loaded lazily on demand using just this configuration

当然,您可以提取该值并将其放入具有惰性的“@OneToOne”关系的新表中,但是在我们的应用程序中,仅使用此配置即可按需延迟加载 LOB

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

This is tested in our project simultaneously on PostgreSQL, MySQL, SQLServer and Oracle, so it should work for u

这在我们的项目中同时在 PostgreSQL、MySQL、SQLServer 和 Oracle 上进行了测试,因此它应该适用于您

回答by Ben George

Lazy property loading requires buildtime bytecode instrumentation.

延迟属性加载需要构建时字节码检测。

Hibernate docs: Using lazy property fetching

Hibernate 文档:使用惰性属性获取

If you want to avoid bytecode instrumentation one option is to to create two entities that use same table, one with the blob one without. Then only use the entity with blob when you need the blob.

如果你想避免字节码检测,一种选择是创建两个使用同一个表的实体,一个有 blob,一个没有。然后仅在需要 blob 时才使用带有 blob 的实体。

回答by Gina De Beukelaer

I had the same issue and this was my fix:

我有同样的问题,这是我的修复:

My Entity:

我的实体:

@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;

...

Added plugin to pom.xml:

向 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>

回答by deFreitas

For me lazy load only worked by compiling and then running it, didn't work on eclipse or intellij for example.

对我来说,延迟加载只能通过编译然后运行它来工作,例如在 eclipse 或 intellij 上不起作用。

I'm using gradle then I did the following to get it working

我正在使用 gradle 然后我做了以下工作来让它工作

  • Annotate entity
  • Setup Hibernate gradle plugin
  • 注释实体
  • 设置 Hibernate gradle 插件

build.gradle

构建.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

实体.java

@Entity
public class Person {

    @Id
    @GeneratedValue
    private Integer id;

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

Testing

测试

./gradlew run

Full working example

完整的工作示例