Java hibernate entity: разрешить установку связанного объекта как с помощью id, так и с самого объекта
У меня есть следующий Java-класс, который также является объектом Hibernate:
@Entity
@Table(name = "category")
public class Category {
@ManyToOne
@JoinColumn(name="parent_id")
private Category parent;
public Category getParent() {
return parent;
}
public void setParent(Category parent) {
this.parent = parent;
}
Категория представляет собой node в дереве категорий. Я реализую веб-сервис, который позволяет категориям CRUD. Например, интерфейс имеет возможность создавать дерево категорий node и передает идентификатор категории в качестве параметра.
Я хочу просто создать новый объект Category и сохранить его в базе данных без извлечения родительского объекта. Мой класс поставщика данных выглядит следующим образом:
public void createCategory(int parent_id, String name, CategoryType type) {
Category category = new Category();
category.setName(name);
// category.setParent(?); <- I don't have this object here
// category.setParentId(id); <- but I do have the id
category.setType(type);
this.categoryDao.save(category);
}
Мой вопрос: что я могу сделать для создания нового объекта Category с помощью родительского набора, если предположить, что я не буду вызывать hibernate для извлечения родителя для меня (это было бы глупо)? Могу ли я предоставить метод setParentId/getParentId для класса категории? Какие аннотации для спящего режима будут иметь?
Ответы
Ответ 1
Hibernate предоставляет метод, называемый (довольно смутно) Session.load()
для этого сценария.
Session.load()
возвращает ленивый прокси с данным идентификатором без запроса базы данных (если объект с данным идентификатором уже загружен в текущий Session
, он возвращает сам объект).
Вы можете использовать этот прокси для инициализации отношений в сохраняемых вами сущностях:
category.setParent(session.load(Category.class, parent_id));
Обратите внимание, что этот код не проверяет существование Category
с данным идентификатором. Однако, если у вас есть ограничение внешнего ключа в вашей схеме БД, вы получите ошибку нарушения ограничений при передаче недопустимого id.
JPA эквивалент этого метода называется EntityManager.getReference()
.
Ответ 2
Есть еще одно решение проблемы - с использованием конструктора по умолчанию для объекта, для которого вы хотите ссылаться, и для установки идентификатора и версии (если это версия).
Constructor<S> c = entityClass.getDeclaredConstructor();
c.setAccessible(true);
S instance = c.newInstance();
Fields.set(instance, "id", entityId.getId());
Fields.set(instance, "version", entityId.getVersion());
return instance;
Мы можем использовать такой подход, потому что мы не используем ленивую загрузку, а вместо этого имеем "представления" объектов, которые используются в графическом интерфейсе. Это позволяет нам уйти от всех объединений, которые использует Hibernate для заполнения всех нетерпеливых отношений. В представлениях всегда есть id и версия объекта. Следовательно, мы можем заполнить ссылку, создав объект, который будет выглядеть как Hibernate как не преходящий.
Я пробовал и этот подход, и тот, у кого есть session.load(). Оба работали нормально. Я вижу некоторое преимущество в моем подходе, поскольку Hibernate не будет течь со своими прокси-серверами в другом месте кода. Если он не используется должным образом, я просто получаю NPE вместо исключения "no session bound to thread".
Ответ 3
Вы можете определить ленивый init
@ManyToOne(fetch=FetchType.LAZY)
Угадайте, что столбец имеет значение NULL, поэтому не проблема сохранения категории без родителя (root)