Проблема с Hibernate One Way Ассоциации - PullRequest
4 голосов
/ 18 февраля 2010

Я пытаюсь реализовать уровень персистентности Hibernate в приложении Java, и у меня возникли некоторые проблемы с ним.Я сталкиваюсь с ошибкой отсутствия прокси-сервера каждый раз, когда пытаюсь получить доступ к простой односторонней ассоциации.Я не совсем правильно реализовал Hibernate - я использую потоковый метод управления сессиями, который, как они предполагают, не используется для производства.Тем не менее, они используют его в своих уроках.Я все еще пытаюсь получить основы работы, поэтому я решил, что после обучения будет хорошо.Однако я не могу заставить работать простую ассоциацию.У меня есть класс, который выглядит примерно так:

public class Foo {
    private Long id;
    private String name;
    private Bar bar;

    // Static Handler Methods - I'll flesh these out further down in the question.
    public static List getAllFoo();


    // Convenience methods
    public String getBarName() {
        return bar.getName();
    }

    // Accessor methods
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Bar getBar() {
        return bar;
    }

    public void setBar(Bar bar) {
        this.bar = bar;
    }
}

Класс Foo's Bar реализован как простая односторонняя ассоциация.Это в .hbm.xml примерно так:

<many-to-one name="bar" column="bar_id" not-null="true" />

Я создаю Foo в статическом методе внутри Foo примерно так:

public static List getAllFoo() {
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    Transaction t = session.beginTransaction();
    List foos = session.createCriteria(Foo.class).list();
    t.commit();
    return foos;
}

Экземпляр hibernate настроен на использование соединенияpool 1 и использует метод потоков для обработки сеанса следующим образом:

<property name="connection.pool_size">1</property>
<property name="current_session_context_class">thread</property>

Я сталкиваюсь с исключением каждый раз, когда пытаюсь получить доступ к ассоциации с помощью функции getBarName() на одном из созданных объектов.Это ошибка прокси-сервера, она выглядит следующим образом:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:132)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:174)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
    at Bar_$$_javassist_7.getName(Bar_$$_javassist_7.java)
    at Foo.getBarName(Bar.java:126)

Короче говоря, это то, с чего я начал, и ошибка, с которой я впервые столкнулся.С тех пор я провел последние несколько дней, читая здесь документы и посты Hibernate, чтобы попытаться выяснить, как заставить это работать.

То, что я узнал - я думаю, - это то, что объекты Hibernate должны быть связаны с Сессией.А именно, сессия, в которой они созданы.Когда я создаю свои объекты, в getAllFoo() это Сессия, к которой относятся эти объекты.В этом сеансе Bar Bar Proxy существует и имеет смысл.Однако, когда я вызываю t.commit() - потому что я использую метод Thread обработки сеанса - я заканчиваю этот сеанс.В результате, когда я звоню bar.getName() позже, бар теперь является потерянным прокси.Это прокси, сессия которого была закрыта.

Я нашел два возможных решения:

1) не закрывать начальный сеанс.Оставьте его открытым, не вызывая t.commit() в getAllFoo().

Это сработало - однако, я не думаю, что смогу его использовать.Я собираюсь загрузить тысячи этих объектов одновременно.Им нужно будет оставаться открытым в течение длительного периода времени, пока у пользователя есть время на обдумывание, но мне нужно иметь возможность свободно получать доступ к ассоциациям.И в будущем могут возникнуть проблемы параллелизма с блокировками базы данных.

2) повторно присоедините объект к новому сеансу перед вызовом ассоциации.

Это не сработало.Возможно я сделал это неправильно - документация, которую я нашел, не ясна.Я попытался начать новый сеанс и позвонить session.load(this, this.id), прежде чем я получил доступ к ассоциации.Например:

public Bar getBar() {
    Bar tmp = null;
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    Transaction t = session.beginTransaction();
    session.load(this, this.id);
    tmp = bar;
    t.commit();
    return tmp;
}

Затем я изменил getBarName(), чтобы вызывать getBar() вместо доступа к панели.Это привело к новой ошибке:

org.hibernate.PersistentObjectException: attempted to load into an instance that was already associated with the session: [Foo#1]

Я думаю, даже после нескольких дней чтения учебников, постов и документации по спящему режиму я все еще не могу обернуть голову вокруг этой структуры.Поэтому я прошу StackOverflow пролить некоторый свет на это.

Во-первых, если вы можете понять, что происходит в коде, который у меня сейчас есть? Сессия открыта или закрыта?Почему в первом методе есть ошибка прокси, но утверждается, что сеанс все еще открыт, а объект все еще присоединен во втором?

Во-вторых, как правильно обрабатывать сеансы - как это сделать?Я на самом деле делаю эту работу? Я думаю, что хочу использовать сеанс для запроса - это, кажется, самый популярный и подходящий для моей ситуации.Но тогда как это на самом деле реализуется с помощью ассоциаций и отложенной загрузки?

Да, я знаю, что не должен использовать метод потока - но у меня есть целый ряд других вопросов, связанных с JTA против Thread против Managed иэтот вопрос уже слишком длинный.

Ответы [ 2 ]

1 голос
/ 18 февраля 2010

Я полагаю, что Hibernate по умолчанию использует ленивую инициализацию.Поэтому, пока вы загружаете список Foos в сеансе, ни одна из связанных с ними панелей не загружается до тех пор, пока не будет предпринята попытка их использования.К тому времени вы уже закрыли сеанс, что приводит к ошибке.

Некоторые потенциальные решения:

  • включить активную выборку при ассоциации Foo-Bar
  • "присоединиться" к извлечению (фактически к нетерпению)
  • попытаться получить доступ к панели в течение сеанса (это будет работать, но вы вызываете Foo.getBar () просто для загрузки соответствующего объекта Bar.)

Обновление по комментариям:

Идиома для управления сессиями / транзакциями:

Session sess = factory.openSession();
Transaction tx;
  try {
    tx = sess.beginTransaction();
    //do some work
    ...
    tx.commit();
  }
  catch (Exception e) {
    if (tx!=null) tx.rollback();
      throw e;
  }
  finally {
    sess.close();
  }

Для повторного присоединения «отсоединенных» объектов (на основе справочных документов ):

, если объект был изменен: обновить объект в новом сеансе

// in the first session
Cat cat = (Cat) firstSession.load(Cat.class, catId);
Cat potentialMate = new Cat();
firstSession.save(potentialMate);

// in a higher layer of the application
cat.setMate(potentialMate);

// later, in a new session
secondSession.update(cat);  // update cat
secondSession.update(mate); // update mate

, если объект не изменен, может использовать lock ():

//just reassociate:
sess.lock(fritz, LockMode.NONE);
//do a version check, then reassociate:
sess.lock(izi, LockMode.READ);
//do a version check, using SELECT ... FOR UPDATE, then reassociate:
sess.lock(pk, LockMode.UPGRADE);
0 голосов
/ 18 февраля 2010
  1. Исключение вызвано тем, что объект не отделен от другого сеанса. Предполагалось, что вы не закрыли другую сессию.

  2. Предпочтительным способом решения вашей проблемы является поддержание сеанса до завершения работы. Прочтите это подробно .

...