Я использую Spring 2.5.5. Hibernate3.2. и MySQL.
Я получаю:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:57)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:111)
at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:150)
at beans.Country$$EnhancerByCGLIB$$e757f40e.getStringId(<generated>)
at beans.Address.getCountryStringId(Address.java:137)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:157)
at beans.Address$$EnhancerByCGLIB$$ef3e9848.getCountryStringId(<generated>)
at checkout.OrderService.ECSetExpressCheckoutCode(OrderService.java:274)
Эта часть, которую я понимаю ( Hibernate помещает прокси-сервер для адреса вместо реальной цели, и когда я пытаюсь получить адрес от постоянного объекта User в методе checkout.OrderService.ECSetExpressCheckoutCode, я получаю исключение LazyInitializationException ).
Именно поэтому я начал читать об управлении транзакциями в Spring и Hibernate. Я прочитал несколько потоков в stackoverflow, но я не встречал никакой реализации. Я также читаю:
http://community.jboss.org/wiki/Sessionsandtransactions
http://community.jboss.org/wiki/OpenSessioninView
http://community.jboss.org/wiki/SessionhandlingwithAOP
Весенняя ссылка - 9,5. Декларативное управление транзакциями
Пружинная ссылка - 12.2.7. Декларативная демаркация транзакции
и больше.
Моя конфигурация управления транзакциями (следуя инструкциям) выглядит следующим образом:
<!-- =============================== TRANSACTION MANAGMENT ============================= -->
<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="orderServiceOperation" expression="execution(* checkout.OrderService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="orderServiceOperation"/>
</aop:config>
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
</bean>
<!-- ============================ END OF TRANSACTION MANAGMENT ========================= -->
Я снова получаю ту же ошибку!
Я не уверен, сталкивается ли эта конфигурация с реализацией уровня Dao, которая у меня есть (но она не должна этого делать):
public class AccountDao implements AccountDaoInterface {
private HibernateTemplate hibernateTemplate;
public void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory);
}
public User getUserForUserId(final long ruserId) throws DataAccessException {
return (User) this.hibernateTemplate.execute(new HibernateCallback() {
public Object doInHibernate(Session session) {
List objList = session.createQuery("from User where id=" + userId).list();
User user = null;
if (!objList.isEmpty()){
user = (User) objList.get(0);
}
return user;
}
});
}
//other methods
}
Я понимаю, что для прокси-сервера адресов, чтобы получить "реальную" цель от БД, мне нужно предоставить сеанс и транзакцию Hibernate. Пока я читаю несколько шаблонов, но наиболее удобный для меня - декларативный подход, оставляющий мои методы разграниченными («чистыми» от логики транзакций). Так что мне не хватает в моей конфигурации? Помните, что мне нужно не только управление транзакциями на уровне DAO, но и уровень обслуживания (и, возможно, контроллеры). Как мне сделать эту работу?
С уважением,
Деспот
EDIT1: (из-за запроса дополнительной информации) Прежде всего посмотрите на AccountDao, метод getUserForUserId. Это метод, который я вызываю из одной точки приложения. Чтобы вы могли понять, что такое пользователь и как он отображается, я предоставляю вам следующие POJO:
public class User implements Serializable{
private long id;
private Address shippingAddress;
private Address billingAddress;
private String otherData;
//default constructor, constructor using fields, getters, setters.
}
public class Address implements Serializable{
private long id;
private Country country;
private String otherData;
//default constructor, constructor using fields, getters, setters.
}
public class Country implements Serializable {
int id;
String stringId;
String name;
}
и файлы сопоставления:
<class name="User" table="User">
<id name="id" column="id" type="long" unsaved-value="-1">
<generator class="native" />
</id>
<many-to-one name="shippingAddress" class="beans.Address" column="shippingAddressId" not-null="true" cascade="persist,merge,save-update"/>
<many-to-one name="billingAddress" class="beans.Address" column="billingAddressId" not-null="true" cascade="persist,merge,save-update"/>
</class>
<class name="Address" table="Address">
<id name="id" column="id" type="long">
<generator class="native" />
</id>
<many-to-one name="country" class="Country" column="countryId" not-null="true" cascade="none" />
</class>
<class name="Country" table="Country">
<id name="id" column="id" type="integer">
<generator class="native" />
</id>
<property name="stringId" type="string" not-null="true" length="4"/>
<property name="name" type="string" not-null="true" length="100"/>
</class>
После того, как я ввел пользователя в одну точку приложения, обработка идет нормально, так как я не получаю доступ к адресу в объекте пользователя. Итак, представьте, что несколько запросов и ответов идут туда-сюда Когда приходит определенный запрос (не то, что это важно, но пользователь выбирает опцию и нажимает кнопку отправки), и обработка приводит к методу checkout.OrderService.ECSetExpressCheckoutCode, где в строке OrderService.java:274 у меня есть :
user.getShippingAddress().getCountryStringId().equals(AddressConst.COUNTRY_US_STRING_ID)
Как только я пытаюсь ввести эту строку, я получаю LazYInitException, поскольку класс Address сопоставляется с атрибутом по умолчанию lazy = "true".
Достаточно ли этого объяснения? Вопрос в том, какова правильная конфигурация управления декларативными транзакциями Spring Hibernate?