Spring、Hibernate、Blob 延迟加载
我需要有关 Hibernate 中延迟 blob 加载的帮助。 我的 Web 应用程序中有这些服务器和框架: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 数据库中。如果数据量很小,一切都可以正常工作。但数据量很大,我收到错误 java.lang.OutOfMemoryError: Java heap space
我尝试在表的每一行上的 blobFields 中写入空值。在这种情况下,应用程序工作正常,内存不会耗尽。我得出的结论是,标记为惰性的 blob 字段 (@Basic(fetch=FetchType.LAZY)
) 实际上并不惰性!
I need help with lazy blob loading in Hibernate.
I have in my web application these servers and frameworks: MySQL, Tomcat, Spring and 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 space
I'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!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
我很困惑。 Emmanuel Bernard 在 ANN-418 中写道,
@Lob
很懒惰默认情况下(即您甚至不需要使用@Basic(fetch = FetchType.LAZY)
注释)。一些用户报告
@Lob
的延迟加载不起作用与所有驱动程序/数据库。一些用户报告说,它在使用字节码工具(javassit?cglib?)时有效。
但我在文档中找不到所有这些的明确参考。
最后,推荐的解决方法是使用a "假的”一对一映射而不是属性。从现有类中删除 LOB 字段,创建引用相同表、相同主键且仅将必要的 LOB 字段作为属性的新类。将映射指定为一对一、fetch="select"、lazy="true"。只要你的父对象仍在你的会话中,你就应该得到你想要的。(只需将其转置为注释)。
I'm confused. Emmanuel Bernard wrote in ANN-418 that
@Lob
are lazy by default (i.e. you don't even need to use the@Basic(fetch = FetchType.LAZY)
annotation).Some users report that lazy loading of a
@Lob
doesn't work with all drivers/database.Some users report that it works when using bytecode instrumentation (javassit? cglib?).
But I can't find any clear reference of all this in the documentation.
At the end, the recommended workaround is 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).
Hibernate 文档:使用惰性属性获取
如果您想避免字节码检测,一种选择是创建两个使用同一表的实体,一个包含 blob,一个不包含 blob。然后仅在需要 blob 时才将实体与 blob 一起使用。
Hibernate docs: Using lazy property fetching
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 的基类和一个包含字节数组的派生类。仅当需要在 UI 上显示 blob 时,才使用派生类。
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.
当然,您可以提取该值并将其放入具有惰性“@OneToOne”关系的新表中,但是在我们的应用程序中,仅使用此配置按需惰性加载 LOB
这在我们的项目中同时在 PostgreSQL 上进行了测试, MySQL、SQLServer 和 Oracle,所以它应该适合你
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
This is tested in our project simultaneously on PostgreSQL, MySQL, SQLServer and Oracle, so it should work for u
我遇到了同样的问题,这是我的修复:
我的实体:
向 pom.xml 添加了插件:
I had the same issue and this was my fix:
My Entity:
Added plugin to pom.xml:
如果我使用
Blob
类型而不是byte[]
类型,则延迟加载对我有用。这个是延迟加载的,如果您需要检索其值,请访问此字段:
Lazy loading works for me if I use
Blob
type instead ofbyte[]
.This one gets lazily loaded and if you need to retrieve its value access this field:
对我来说,延迟加载只能通过编译然后运行它来工作,例如在 eclipse 或 intellij 上不起作用。
我正在使用 gradle 然后我执行了以下操作以使其正常工作
build.gradle
Entity.java
测试
完整工作示例
For me lazy load only worked by compiling and then running it, didn't work on eclipse or intellij for example.
I'm using gradle then I did the following to get it working
build.gradle
Entity.java
Testing
Full working example
基于 @MohammadReza Alagheband 的响应,使用 @OneTone 表示法的简单解决方法(为什么 @Basic(fetch=lazy) 在我的情况下不起作用?)但不需要为每个所需的惰性属性创建一个新表,如下所示:
A simple workarround using @OneTone notation based on the response of @MohammadReza Alagheband (Why does @Basic(fetch=lazy) doesn't work in my case?) but without the requirement of create a new table for each required lazy attribute is the following: