Как я могу получить внешний ключ из сопоставления JPA ManyToOne, не обращаясь к целевой таблице?
У меня есть следующие два аннотированных класса, которые я использую для построения графика:
@Entity
@Table(name = "Edge")
public class Edge
{
/* some code omitted for brevity */
@ManyToOne
@JoinColumn(name = "ixNodeFrom", nullable = false)
private Node _nodFrom;
@ManyToOne
@JoinColumn(name = "ixNodeTo", nullable = false)
private Node _nodTo;
/* some code omitted for brevity */
}
@Entity
@Table(name = "Node")
public class Node
{
/* some code omitted for brevity */
@OneToMany(mappedBy = "_nodTo")
private Set<Edge> _rgInbound;
@OneToMany(mappedBy = "_nodFrom")
private Set<Edge> _rgOutbound;
/* some code omitted for brevity */
}
Теперь, когда я строю график, я запускаю два запроса, чтобы извлечь все строки из любой таблицы и установить дочерние / родительские ссылки, для которых мне нужны идентификаторы, хранящиеся в Edge
Таблица.
Поскольку я определил отношение между двумя таблицами в JPA, доступ к объекту ребра для получения идентификаторов двух узлов запускает два оператора SQL на ребро, когда поставщик JPA лениво * загружает связанные узлы. Поскольку у меня уже есть объекты узлов, а идентификаторы уже загружены из таблицы ребер, я хочу пропустить эти запросы, так как они занимают очень много времени для больших графов.
Я пытался добавить эти строки в Edge
класс, но затем мой провайдер JPA хочет, чтобы я сделал одно отображение только для чтения, и я не могу найти способ сделать это:
@Column(name = "ixNodeTo")
private long _ixNodeTo;
@Column(name = "ixNodeFrom")
private long _ixNodeFrom;
Я использую Eclipselink и MySQL, если это имеет значение.
** Поведение по умолчанию для @ManyToOne
на самом деле загружается, см . ответ Паскаля*
5 ответов
Я получил три хороших ответа, которые были в равной степени полезны, и к настоящему моменту ни один из них не проскользнул на вершину публичным голосованием, поэтому я объединяю их здесь для единого всеобъемлющего ответа:
а) Изменить запрос
Вы можете загрузить весь график сразу, изменив запрос, тем самым предоставив провайдеру JPA возможность понять, что у него уже есть все в памяти и нет необходимости возвращаться в БД:
List<Node> nodes = em.createQuery(
"SELECT DISTINCT n FROM Node n LEFT JOIN FETCH n._rgOutbound")
.getResultList();
(через аксавт)
б) Используйте поля только для чтения для FKs
Загрузка FK в их собственные поля, как описано в вопросе, также будет работать, если, как требует поставщик JPA, поля объявлены как доступные только для чтения, что делается следующим образом:
@Column(name = "ixNodeTo", insertable = false, updatable = false)
(через Бравочарли)
в) использовать доступ к собственности
Если вы используете доступ к свойству вместо доступа к полю, поставщик JPA также получает шанс понять, что у него уже есть FK и ему не нужно извлекать объект, на который ссылаются. Короче говоря, доступ к свойству означает, что вы размещаете аннотации JPA на получателе, тем самым "обещая" провайдеру JPA, что ваш получатель не пойдет и получит доступ к остальной части объекта. Подробнее в этом вопросе. Это будет работать для Hibernate, и для Eclipselink, это будет работать (предполагается в оригинальном ответе, подтвержденном мной экспериментально) с включенным ткачеством. (через Паскаля Тивента)
Кроме того, как указывает Паскаль в своем ответе, @ManyToOne
В отличие от моего первоначального поста, это не ленивая загрузка, а полная загрузка по умолчанию, и изменение, которое также потребует ткачества.
Ты пытался
@Column(name = "ixNodeTo", insertable = false, updatable = false)
Как я могу получить внешний ключ из сопоставления JPA ManyToOne, не обращаясь к целевой таблице?
Теоретически, поставщик JPA должен иметь возможность не запускать запрос при вызове
someEdge.getNodeFrom().getId()
как это уже имеет идентификатор (как FK).
Я на 100% уверен, что Hibernate может (при условии, что вы используете доступ к свойству). В случае EclipseLink я не знаю (если это произойдет, вероятно, потребуется ткачество).
Поскольку я определил отношение между двумя таблицами в JPA, доступ к объекту ребра для получения идентификаторов двух узлов запускает два оператора SQL на ребро, когда поставщик JPA лениво загружает связанные узлы. Поскольку у меня уже есть объекты узлов, а идентификаторы уже загружены из таблицы ребер, я хочу пропустить эти запросы, так как они занимают очень много времени для больших графов.
Обратите внимание, что @ManyToOne
использует EAGER
стратегия по умолчанию. Если вы хотите сделать это LAZY
Вы должны явно его декальзовать (но опять же, это потребует создания ваших классов с EclipseLink).
Я думаю, что вы должны попытаться оптимизировать свой запрос, а не менять отображение. Например, следующий запрос извлекает весь граф за один раз (проверено в Hibernate):
List<Node> nodes = em.createQuery(
"SELECT DISTINCT n FROM Node n LEFT JOIN FETCH n._rgOutbound")
.getResultList();
Как насчет использования getReference()?
Например:
Node fkNode = em.getReference(edge.getNodeFrom()); // [1]
fkNode.getId()
[1] Это не вызовет SQL-запрос для получения nodeFrom