Ленивая выборка одного столбца (атрибута класса) с помощью Hibernate
у меня есть старая таблица, с которой я работаю, которая выглядит так:
+------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------+--------------+------+-----+---------+-------+
| BINARY_DATA_ID | varchar(255) | NO | PRI | | |
| BINARY_DATA | longblob | YES | | NULL | |
| BINARY_DATA_NAME | varchar(255) | YES | | NULL | |
+------------------+--------------+------+-----+---------+-------+
основная проблема с этим состоит в том, что BinaryData
класс Java загружает BINARY_DATA
столбец, даже если мне требуется только BINARY_DATA_NAME
. Я знаю, что лучший способ создать это-разделить данные из метаданных (например, имя файла), чтобы они жили в отдельных таблицах. Оттуда тривиально сделать данные лениво загруженными. Вот как это должно было быть сделано в первом место.
к сожалению, это может быть невозможно для меня сделать выше из-за организационных ограничений. В качестве обходного пути можно ли сделать этот столбец ленивым, используя некоторые аннотации вместо разделения вещей на отдельные таблицы? Я изменил BinaryData
класс, чтобы он имел внутренний статический BinaryDataData
класс @Embedded
и атрибут @Basic(fetch=FetchType.LAZY)
:
@Entity
@Table
@Proxy(lazy=false)
@Inheritance(strategy=InheritanceType.JOINED)
public class BinaryData implements Serializable, Persistable<BinaryData>, Cloneable {
private static final long serialVersionUID = /** blah */;
@Id @Column @GeneratedValue(generator="uuid") @GenericGenerator(name="uuid", strategy="uuid")
private String id;
@Column
private String binaryDataName;
@Embedded
@Basic(fetch = FetchType.LAZY)
private BinaryDataData binaryData;
@Transient
private String cacheId;
/**
* Hibernate constructor
*/
public BinaryData() { /* Creates a new instance of Attachment. */}
public BinaryData(byte[] binaryData, String binaryDataName) {
this.binaryData = new BinaryDataData(ArrayUtils.clone(binaryData));
this.binaryDataName = binaryDataName;
}
/**
* Returns the BinaryData byte stream.
*
* @return binaryData byte stream
*/
@Embedded
@Basic(fetch = FetchType.LAZY)
public byte[] getBinaryData() {
if (this.binaryData == null) {
return new byte[0];
}
return binaryData.getActualData();
}
@Embeddable
public static class BinaryDataData implements Serializable {
@Column(length=32*1024*1024, columnDefinition="longblob", name="BINARY_DATA") @Lob
private byte[] actualData;
public BinaryDataData() { }
public BinaryDataData(byte[] data) {
this.actualData = data;
}
public byte[] getActualData() {
if (this.actualData == null) {
return new byte[0];
}
return this.actualData;
}
public void setBinaryData(byte[] newData) {
this.actualData = newData;
}
@Override public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof BinaryDataData)) {
return false;
}
final BinaryDataData other = (BinaryDataData) obj;
if (!Arrays.equals(actualData, other.actualData)) {
return false;
}
return true;
}
}
/** onwards... */
к сожалению, это не работает. SQL, который я вижу, все еще показывает полный извлечение объекта, даже если двоичные данные не запрашиваются:
select ideaattach0_.BINARY_DATA_ID as BINARY1_9_, ideaattach0_1_.BINARY_DATA as BINARY2_9_, ideaattach0_1_.BINARY_DATA_NAME as BINARY3_9_, ideaattach0_.IDEA_BUCKET_ID as IDEA2_136_ from IDEA_ATTACHMENT ideaattach0_ inner join BINARY_DATA ideaattach0_1_ on ideaattach0_.BINARY_DATA_ID=ideaattach0_1_.BINARY_DATA_ID where ideaattach0_.BINARY_DATA_ID=?
какие идеи? Спасибо.
3 ответов
с Hibernate, Глава 19. Повышение производительности:
Lazy attribute fetching: при доступе к переменной экземпляра извлекается атрибут или однозначная ассоциация. этот подход требует инструментария байт-кода buildtime и редко необходимо.
для проекта maven необходимо добавить следующую зависимость плагина в pom.XML-код:
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<version>${hibernate.version}</version>
<executions>
<execution>
<configuration>
<failOnError>true</failOnError>
<enableLazyInitialization>true</enableLazyInitialization>
</configuration>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
Я проверил его в своем проекте, и он работает, пример сущности:
@Entity(name = "processing_record")
public class ProcessingRecord {
/**
* Why uuid: https://www.clever-cloud.com/blog/engineering/2015/05/20/why-auto-increment-is-a-terrible-idea/
*/
@Id
@Column(name = "record_id")
@org.hibernate.annotations.Type(type = "pg-uuid")
private UUID id;
...
/**
* Processing result.
*/
@Column(name = "result")
@Basic(fetch = FetchType.LAZY)
private String result;
...
для получения более подробной информации взгляните на следующие статьи: ссылке
Я знаю дату этого запроса, однако я также попытался бы использовать класс значения проекции, который сопоставляется как подмножество столбцов, и использовать эту проекцию с указанным именованным запросом, который создает экземпляр этого объекта значения проекции вместо базового объекта, на который здесь ссылаются.
Я работаю над решением, которое использует этот метод, поэтому в настоящее время у меня нет полного примера. Однако основная идея заключается в том, что вы создадите запрос JPA, который использует синтаксис" select NEW Projection_Object_Target", где поля непосредственно ссылаются в конструкторе "Projection_Object_Target".
I. E. используйте выражение конструктора следующим образом:
SELECT NEW fully.qualified.package.name.ProjectionObject(baseObject.column_target_0,baseObject.column_target_1,...,baseObject.column_target_n) FROM BaseObjectMappedInDBTable AS baseObject
общий пример использования:
String queryStr =
"SELECT NEW fully.qualified.package.name.ProjectionObject(baseObject.column_target_0) " +
"FROM BaseObjectMappedInTable AS baseObject";
TypedQuery<ProjectionObject> query =
em.createQuery(queryStr, ProjectionObject.class);
List<ProjectionObject> results = query.getResultList();