Spring, Hibernate, Blob lazy loading
Мне нужна помощь с отложенной загрузкой BLOB-объектов в Hibernate. В моем веб-приложении есть такие серверы и платформы: 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)
), на самом деле не ленивое!
Ответы
Ответ 1
Я не совсем понимаю. Эммануэль Бернард писал в ANN-418, что @Lob
по умолчанию ленив (т.е. вам даже не нужно использовать @Basic(fetch = FetchType.LAZY)
).
Некоторые пользователи сообщают, что @Lob
загрузка @Lob
не работает со всеми драйверами/базой данных.
Некоторые пользователи сообщают, что это работает при использовании инструментария байт-кода (javassit? Cglib?).
Но я не могу найти четкую ссылку на все это в документации.
В конце, рекомендуемый обходной путь - использовать "поддельные" взаимно-однозначные сопоставления вместо свойств. Удалите поля больших объектов из существующего класса, создайте новые классы, ссылаясь на ту же таблицу, тот же первичный ключ и только необходимые поля больших объектов в качестве свойств. Укажите сопоставления как один-к-одному, fetch = "select", lazy = "true". Пока ваш родительский объект все еще находится в вашем сеансе, вы должны получать именно то, что вы хотите. (просто перенести это в аннотации).
Ответ 2
Конечно, вы можете извлечь это значение и поместить его в новую таблицу с отношением "@OneToOne", которое лениво, однако в нашем приложении LOB загружаются лениво по требованию, используя только эту конфигурацию
@Lob
@Fetch(FetchMode.SELECT)
@Type(type="org.hibernate.type.PrimitiveByteArrayBlobType")
byte[] myBlob;
Это тестирование в нашем проекте одновременно на PostgreSQL, MySQL, SQLServer и Oracle, поэтому оно должно работать для u
Ответ 3
Для загрузки Lazy требуется встроенная инструментария для байт-кодов.
Hibernate docs: использование lazy fetching свойств
Если вы хотите избежать инструментария байт-кодов, одним из вариантов является создание двух объектов, которые используют одну и ту же таблицу, одну с блобом без нее. Тогда используйте объект с blob, когда вам нужен blob.
Ответ 4
Я бы предложил вам использовать наследование для обработки этого сценария. Имейте базовый класс без blob и производного класса, содержащего массив байтов. Вы должны использовать производный класс только тогда, когда вам нужно отобразить blob в пользовательском интерфейсе.
Ответ 5
У меня была та же проблема, и это было мое исправление:
Моя сущность:
@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>
Ответ 6
Для меня ленивая загрузка работала только путем компиляции и последующего запуска, но не работала, например, на 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
Полный рабочий пример