Подтвердить что ты не робот

Как я могу получить внешний ключ из сопоставления 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, доступ к объекту edge для получения идентификаторов двух узлов вызывает два оператора SQL на каждый край, когда поставщик JPA lazily * загружает связанные узлы. Поскольку у меня уже есть объекты node, и идентификаторы уже загружены из таблицы краев, я хочу пропустить эти запросы, поскольку они занимают очень много времени для больших графиков.

Я попытался добавить эти строки в класс Edge, но затем мой поставщик JPA хочет, чтобы я сделал одно сопоставление только для чтения, и я не могу найти способ, как это сделать:

@Column(name = "ixNodeTo")
private long _ixNodeTo;

@Column(name = "ixNodeFrom")
private long _ixNodeFrom;

Я использую Eclipselink и MySQL, если это имеет значение.


** Поведение по умолчанию для @ManyToOne на самом деле является активной загрузкой, см. ответ Pascal *

4b9b3361

Ответ 1

У меня было три хороших ответа, которые были в равной степени полезны, и к настоящему времени никто не пронизан вершиной публичным голосованием, поэтому я объединяю их вместе для получения единого исчерпывающего ответа:

a) Измените запрос

Вы можете загрузить весь график сразу, изменив запрос, тем самым предоставив провайдеру JPA возможность понять, что он уже имеет все в памяти и не нуждается в возврате в БД:

List<Node> nodes = em.createQuery(
        "SELECT DISTINCT n FROM Node n LEFT JOIN FETCH n._rgOutbound")
        .getResultList();

(через axtavt)

b) Используйте поля только для чтения для FKs

Загрузка FK в свои собственные поля, как описано в вопросе, также будет работать, если, по мере того как поставщик JPA требует, поля объявляются как readonly, что делается следующим образом:

@Column(name = "ixNodeTo", insertable = false, updatable = false)

(через bravocharlie)

c) Использовать доступ к свойствам

Если вы используете доступ к свойствам вместо доступа к полю, у провайдера JPA также появляется возможность понять, что у него уже есть FK, и ему не нужно извлекать связанный объект. Короче говоря, доступ к ресурсам означает, что вы помещаете аннотации JPA на геттер, тем самым "обещая" провайдеру JPA, что ваш получатель не пойдет и не получит доступ к остальной части объекта. Подробнее в этот вопрос. Это будет работать для Hibernate, и для Eclipselink он будет работать (предполагается в исходном ответе, экспериментально подтвержденном мной) с включенным плетением. (через Pascal Thivent)


Кроме того, как Паскаль указывает в своем ответе, @ManyToOne, вопреки моему первоначальному сообщению, не является ленивой загрузкой, но по-прежнему загружается по умолчанию, и изменение, которое потребует также плетения.

Ответ 2

Вы пробовали

@Column(name = "ixNodeTo", insertable = false, updatable = false)

Ответ 3

Как получить внешний ключ из сопоставления JPA ManyToOne без попадания в целевую таблицу?

Теоретически, поставщик JPA должен иметь возможность не вызывать запрос при вызове

someEdge.getNodeFrom().getId()

поскольку он уже имеет идентификатор (как FK).

Я на 100% уверен, что Hibernate может (при условии, что вы используете доступ к свойствам). В случае с EclipseLink я не знаю (если это так, то, вероятно, потребуется ткачество).

Поскольку я определил связь между двумя таблицами в JPA, доступ к объекту edge для получения идентификаторов двух узлов запускает два оператора SQL на каждый край, когда поставщик JPA лениво загружает связанные узлы. Поскольку у меня уже есть объекты node, и идентификаторы уже загружены из таблицы краев, я хочу пропустить эти запросы, поскольку они занимают очень много времени для больших графиков.

Обратите внимание, что @ManyToOne по умолчанию использует стратегию EAGER. Если вы хотите сделать это LAZY, вы должны явно его декальтировать (но опять же, это потребует плетения ваших классов с помощью EclipseLink).

Ответ 4

Я думаю, вам следует попытаться оптимизировать ваш запрос, а не изменять сопоставление. Например, следующий запрос выводит весь граф сразу (проверяется в Hibernate):

List<Node> nodes = em.createQuery(
            "SELECT DISTINCT n FROM Node n LEFT JOIN FETCH n._rgOutbound")
            .getResultList();

Ответ 5

Как использовать getReference()?

Например:

Node fkNode = em.getReference(edge.getNodeFrom()); // [1]
fkNode.getId()

[1] Это не вызовет SQL-запрос для извлечения узла из