Я пытаюсь реализовать уровень персистентности 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 иэтот вопрос уже слишком длинный.