Страницы

Поиск по вопросам

воскресенье, 12 января 2020 г.

Возможно ли в Hibernate при связывании таблиц (@OneToMany @ManyToOne) получить вместо объекта только поле Id?

#java #spring #hibernate


Суть проблемы в том что у меня Класс связанный в древовидную структуру по принципу
предок/потомки, но когда я получаю объект у которого есть предок и потомки из БД вытягиваются
не только эти объекты, но и объекты связанные с предком и потомками, а мне достаточно
было бы получать только Id предка и списки List Id потомков, возможно ли связывать
таблицы и получать только их Id, не вытягивая из БД весь остальной фарш?

сейчас так:

@Entity
@Table(name="testpage")
public class TestPage {
    @Id
    @Column(name="id")
    @GeneratedValue(strategy=GenerationType.AUTO)
    private long Id;
    @Column(unique=true,nullable=false)
    private String Name;
    private int position;
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn()
    @JoinTable(name="parent_testpage",joinColumns=@JoinColumn(name="page_id", referencedColumnName="id"),
    inverseJoinColumns=@JoinColumn(name="parent_id", referencedColumnName="id"))
    private TestPage Parent;
    @OneToMany(fetch = FetchType.EAGER)
    @JoinTable(name="parent_testpage",joinColumns=@JoinColumn(name="parent_id", referencedColumnName="id"),
    inverseJoinColumns=@JoinColumn(name="page_id", referencedColumnName="id"))
    private List Child;

    ...getters and setters
}


хотелось бы так:

@Entity
@Table(name="testpage")
public class TestPage {
    @Id
    @Column(name="id")
    @GeneratedValue(strategy=GenerationType.AUTO)
    private long Id;
    @Column(unique=true,nullable=false)
    private String Name;
    private int position;
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn()
    @JoinTable(name="parent_testpage",joinColumns=@JoinColumn(name="page_id", referencedColumnName="id"),
    inverseJoinColumns=@JoinColumn(name="parent_id", referencedColumnName="id"))
    private long ParentId;

    @OneToMany(fetch = FetchType.EAGER)
    @JoinTable(name="parent_testpage",joinColumns=@JoinColumn(name="parent_id", referencedColumnName="id"),
    inverseJoinColumns=@JoinColumn(name="page_id", referencedColumnName="id"))
    private List ChildIds;

...getters and setters

}

    


Ответы

Ответ 1



Сразу оговорюсь, что это касается JPA. У Hibernate есть ещё какие-то свои родные механизмы - я их не знаю. Только отдельный запрос, как говорит Ruslan Nekura, поможет получить id child-ов. (Но есть ещё один вариант. О нём чуть ниже) Не работает JPA по-другому. Не ссылается он на отдельные поля других entity. Только на объект целиком. parent_Id - это поле в того же самого entity. Оно может быть использовано и так и сяк и даже одновременно: ... @JoinTable(name="parent_testpage", joinColumns=@JoinColumn(name="page_id", referencedColumnName="id"), inverseJoinColumns=@JoinColumn(name="parent_id", referencedColumnName="id")) private TestPage Parent; @Column(name="parent_id", insertable=false, updatable=false) private Long parentId; ... Если используются одновременно, то одно отображение обязательно отметить как "только для чтения". В примере это parentId с параметрами аннотации insertable, updatable = false. Со списком child-ов, как уже сказано выше, такой номер не пройдёт. И вот ещё одна метода. Можно сделать view в базе с полями parent_id и child_id. CREATE VIEW view_child_ids (long parent_id, long child_id) AS SELECT parent_id, id FROM testpage WHERE parent_id IS NOT NULL; Отобразить эту view в виде Basic ElementCollection: ... @ElementCollection @CollectionTable(name = "view_child_ids", joinColumns=@JoinColumn(name = "parent_id")) @Column(name = "child_id") private List childIds; ... Для этой view ещё надо будет всяких правил/триггеров определить на случай каскадных удаления/изменения. Есть в этом свои недостатки, но не буду их упоминать. Они проявляются при определённых обстоятельствах. Может Вам повезёт, ведь никто не знает всей Вашей задумки. Сам предпочитаю запросы. Радикальный вариант - уйти с JPA/Hibernate на альтернативный способ доступа к данным, работающий на других принципах. Active-record какой-нибудь.

Ответ 2



В hibernate есть такой параметр как lazy, он то и отвечает за неполную загрузку. В xml он указывается параметр: lazy="true" Пример: Можно отмечать как всю таблицу, так определенный столбец. В аннотациях вот так: @Basic(fetch = FetchType.LAZY) Другими словами объекты с такой пометкой будут загружены ТОЛЬКО когда вы обратитесь к ним напрямую! P.S. Хотя мне вот тоже зависимости не нужно загружать (точнее очень редко), так я в обще зависимости не стал указывать. Он мне всегда одни id дает, а я уж ручками загружу, если в этом возникнет необходимость.

Ответ 3



У вас в коде @ManyToOne(fetch = FetchType.EAGER), то есть вы явно просите hibernate вытягивать вообще все до разрешенного в конфигурации уровня вложений hibernate.max_fetch_depth при абсолютно любом запросе. Попробуйте указать FetchType.LAZY, а в случае, когда явно нужны связанные потомки использовать NamedQuery типа такого @NamedQuery(name = TestPage.GET_PAGE_WITH_CHILD, query = "select distinct p " + "from TestPage p left join fetch p.Child " + "where p.Parent = :parent") В этом случае вы получите объект с явно инициализированными зависимостями первого уровня, у которых в свою очередь зависимости будут заменены прокси. Ну и отвечая на вопрос в том виде, в котором вы его поставили. Да, можно получить Long[] id, если запросить только его через NamedQuery как-нибудь так @NamedQuery(name = Person.GET_CHILD_ID_BY_PARENT, query = "select p.id from TestPage p where p.parent = :parent) И маленькая ремарка в конце. По стандарту имена переменных, полей и методов пишутся с маленькой буквы, а имена классов - с большой.

Комментариев нет:

Отправить комментарий