Что не так с моей конфигурацией управления транзакциями Spring Hibernate? - PullRequest
2 голосов
/ 03 декабря 2010

Я использую 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?

Ответы [ 2 ]

0 голосов
/ 18 июня 2011

Простой способ: измените метод getUserForUserId() в вашем DAO, чтобы выполнить выборочное соединение по двум адресам, или просто получите доступ к двум объектам адреса хотя бы один раз, находясь в транзакции, которая вызвала getUserForUserId().

Сложный способ: прочитайте об отделенных объектах в главе 13 руководства Hibernate и используйте низкоуровневый метод Hibernate для повторного присоединения объекта User (например, merge()). Конечно, для этого вам понадобится сеанс, и в вашем сообщении об ошибке говорится, что сеанса у вас нет.

Простой способ поддерживает то, что ваш код, вероятно, предполагает: он вызывает user.getShippingAddress() в любое время после вызова getUserForUserId().

0 голосов
/ 03 декабря 2010

Вы извлекаете свой объект Country из БД одним вызовом OrderService и затем пытаетесь получить к нему доступ при другом вызове OrderService? Я думаю, что вы можете использовать прокси, только если вы поддерживаете тот же сеанс.

В любом случае вы должны предоставить полный код.

...