Hibernate OneToOne ленивая загрузка и каскадирование - PullRequest
7 голосов
/ 09 июня 2011

Вот что я пытаюсь сделать.

  1. Создать родителя с отношением OneToOne к ребенку
  2. Родитель должен выбрать детей, используя ленивую загрузку
  3. Если родитель удален, то и дочерний элемент
  4. Если дочерний элемент удален, родитель не должен быть затронут
  5. Каскадное обновление и удаление должны быть переведены в DDL

класс Родитель

@OneToOne(mappedBy = "parent", cascade = CascadeType.ALL)
public Child getChild()

класс Ребенок

@OneToOne(fetch = FetchType.LAZY)
@OnDelete(action = OnDeleteAction.CASCADE)
@JoinColumn(name="parent_id")
public Parent getParent()

У меня точка 1,3, 4 полностью работает и Точка 5 частично работает, еще нужно решить, как перевести часть обновления в DDL.

Точка 2 большаяпроблема здесь, с моим текущим решением родитель не загружает ребенка лениво.Однако ребенок загружает родителя лениво, но инвертирование аннотаций может привести к путанице в каскаде (, пункты 3, 4 и 5 ).

Я сейчас очень смущен, надеясь, что я 'мы упустили что-то очевидное, поэтому любая помощь будет принята с благодарностью.

РЕДАКТИРОВАТЬ: Код, запрошенный Adeel Ansari

'fetch = FetchType.LAZY' был добавлен в классParent, в остальном такой же, как указано выше.

IParentDAO parentDAO = DAOFactory.getFactory().getParentDAO();

parentDAO.beginTransaction();
//findByPrimaryKey uses 'org.hibernate.Session.get(Class clazz, Serializable id)'
parentDAO.findByPrimaryKey(1l);
parentDAO.commitTransaction();

Полученные запросы гибернации, одна выборка Parent и одна выборка Child:

Hibernate: select parent0_.id as id0_0_ from parents parent0_ where parent0_.id=?
Hibernate: select child0_.id as id1_0_, child0_.parent_id as parent2_1_0_ from childs child0_ where child0_.parent_id=?

Вот код для findByPrimaryKey:

public class HibernateParentDAO extends HibernateDAO<Parent, Long> implements IParentDAO {

    public HibernateParentDAO() {
        super(Parent.class);
    }
}

public abstract class HibernateDAO<T, ID extends Serializable> implements IGenericDAO<T, ID> {
    private Class<T> persistentClass;

    public HibernateDAO(Class<T> c) {
        persistentClass = c;
    }

    @SuppressWarnings("unchecked")
    public T findByPrimaryKey(ID id) {
        return (T) HibernateUtil.getSession().get(persistentClass, id);
    }
}

Ответы [ 4 ]

20 голосов
/ 09 июня 2011

У меня была похожая проблема. Есть несколько разных решений, но все они обходные пути.

Краткий ответ: Hibernate НЕ поддерживает ленивые отношения один-к-одному.

Длинный ответ (обходной путь):

  1. Объявите отношения взаимно-однозначного отношения с одной стороны (дочерние) и взаимно-однозначного отношения с другой стороны (родительские). Таким образом, parent.getchild() возвращает набор, но он может использовать отложенную загрузку.

  2. Вы можете попытаться, чтобы родитель и потомки поделились первичным ключом, но для этого потребуется изменить схему.

  3. Вы можете попытаться настроить представление в своей базе данных, отражающее это отношение один к одному.

11 голосов
/ 09 июня 2011

[Эта часть больше не хранится]

Измените это в вашем Parent, как показано ниже,

@OneToOne(mappedBy = "parent", cascade = CascadeType.ALL, fetch=FetchType.LAZY)
public Child getChild()

Должно работать.


[Отредактировано, чтобы объяснить, почему это не будет работать]

Сразу после загрузки B вы можете позвонить getCee (), чтобы получить C. Но посмотрите, getCee () - это метод ВАШЕГО класса и Hibernate не имеет никакого контроля над этим. Hibernate не знает, когда кто-то собирается вызвать getCee (). Это означает Hibernate должен поставить соответствующий значение в "cee" собственности на момент, когда он загружает B из базы данных.

Если для C включен прокси, Hibernate может поставить объект C-прокси, который не загружен еще, но будет загружен, когда кто-то использует это. Это дает ленивый загрузка для одного к одному.

Но теперь представьте, что ваш объект B может или может не иметь связанных C (Ограниченно = "ложь"). Что должно getCee () возвращает, когда конкретный B делает нет C? Ноль. Но помни, Hibernate должен установить правильное значение "Сии" в данный момент он установил B (потому что он не знает, когда кто-то позвонит getCee ()). Прокси тут не помогает потому что сам прокси в уже ненулевой объект.

Если ваше отображение B-> C обязательный (с ограничением = истина), Hibernate будет использовать прокси для C приводя к ленивой инициализации. Но если вы разрешите B без C, Hibernate просто нужно проверить наличие C на момент он загружает B. Но ВЫБРАТЬ проверить наличие просто неэффективно потому что тот же SELECT может не просто проверить наличие, но загрузить весь объект. Так что ленивая загрузка уходит.

Ссылка: http://community.jboss.org/wiki/Someexplanationsonlazyloadingone-to-one


[Отредактировано, чтобы включить обходной путь]

Вы можете использовать optional=false и @LazyToOne для отношений, которые не являются обязательными. Не забудьте включить cascade={CascadeType.PERSIST,CascadeType.REMOVE}. Поскольку это очевидно для не необязательных отношений. Ниже приведен пример

@OneToOne(mappedBy="parent", optional=false, fetch=FetchType.LAZY, cascade={CascadeType.PERSIST,CascadeType.REMOVE})
@LazyToOne(LazyToOneOption.PROXY)
public Child getChild(){...}

Это должно сработать для вас, так как я вижу, что вы используете cascade=CascadeType.ALL, что означает не-опционально. Не так ли? Но для необязательных отношений вы можете рассмотреть обходной путь, заданный iliaden, здесь .

7 голосов
/ 09 июня 2011

Вы пробовали @OneToOne(fetch = FetchType.LAZY, optional=false)?Также проверьте этот блог и этот поток.

0 голосов
/ 16 апреля 2014

@ Отношение один к одному не поддерживает отложенную инициализацию .Чтобы получить объект, вы не помещаете FetchType.LAZY в дочерний класс и получаете все дочерние объекты.

class Parent

@OneToOne(mappedBy = "parent", cascade = CascadeType.REMOVE)
public Child getChild()

class Child

@OneToOne(cascade = CascadeType.REMOVE)
public Parent getParent()
...