Конфигурации диспетчера транзакций Hibernate в Spring

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

Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
.. perform operation here
session.getTransaction().commit();

Теперь я собираюсь реорганизовать свой код с помощью декларативного управления транзакциями. Что я получил сейчас...

  <context:component-scan base-package="package.*"/>
  
  <bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
    <property name="configurationClass">
        <value>org.hibernate.cfg.AnnotationConfiguration</value>
    </property>
  </bean>
  
  <tx:annotation-driven transaction-manager="txManager"/>
  
  
  <bean id="txManager"  class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory">
         <ref local="mySessionFactory"/>
    </property>
  </bean>

Класс обслуживания:

@Service
public class TransactionalService {

    @Autowired
    private SessionFactory factory;
    
    @Transactional
    public User performSimpleOperation() {
        return (User)factory.getCurrentSession().load(User.class, 1L);
    }
}

И простой тест -

@Test
public void useDeclarativeDem() {
    FileSystemXmlApplicationContext ctx = new FileSystemXmlApplicationContext("spring-config.xml");
    TransactionalService b = (TransactionalService)ctx.getBean("transactionalService");
     User op = b.performSimpleOperation();
     op.getEmail();

Когда я пытаюсь получить электронную почту пользователя за пределами метода Transactional, я получаю исключение ленивой инициализации, электронная почта в моем случае представляет собой простую строку. Hibernate даже не выполняет sql-запрос, пока я не вызову какие-либо геттеры на своем POJO.

  1. что я делаю неправильно здесь?

  2. Правомерен ли этот подход?

  3. Можете ли вы предложить какой-либо проект с открытым исходным кодом, который работает с Spring/Hibernate с конфигурацией на основе аннотаций?

Обновить

По какой-то причине, если я заменю getCurrentSession на openSession, этот код будет работать нормально. Может кто-нибудь объяснить это, пожалуйста?


person user12384512    schedule 27.02.2011    source источник
comment
Электронная почта для пользователя находится в другой таблице?   -  person Ian Dallas    schedule 27.02.2011


Ответы (3)


Хорошо, наконец я понял, в чем была проблема. В приведенном выше коде я использовал load вместо get. Session.load на самом деле не попал в базу данных. Вот почему я получаю исключение отложенной инициализации вне метода @Transactional

Если я использую openSession вместо getCurrentSession, сеанс открывается за пределами контейнера Spring. В результате сессия не была закрыта, и это позволило мне читать свойства объекта вне метода @Transactional.

person user12384512    schedule 28.02.2011

Причина, по которой Hibernate не выполняет никаких SQL-запросов, пока вы не вызовете геттеры, заключается в том, что я считаю, что для FetchType установлено значение LAZY. Чтобы решить эту проблему, вам нужно будет изменить FetchType на EAGER в вашем POJO:

@Entity
@Table(name = "user")
public class User {

    /*Other data members*/

    @Basic(fetch = FetchType.EAGER)
    private String email;

}

Мне лично никогда не приходилось указывать базовые типы, чтобы иметь EAGER FetchType, поэтому я не совсем уверен, почему ваша конфигурация требует этого. Если это только в ваших тестах, это может быть связано с тем, как вы настроили свои тесты JUnit. В объявлении класса должно быть что-то вроде этого:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"/test-app-config.xml"})
@Transactional
public class UserServiceTest {

}

Что касается хорошего ресурса, я всегда нахожу SpringByExample полезным.

ИЗМЕНИТЬ

Поэтому я не совсем уверен, что не так с вашей конфигурацией. Это отличается от того, как я настроил свою, поэтому вот моя типичная конфигурация в надежде, что она поможет. hibernate.transaction.factory_class может быть ключевым свойством, которое вам не хватает. Я также использую AnnotationSessionFactoryBean:

<!-- DataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
    p:driverClassName="com.mysql.jdbc.Driver" 
    p:url="jdbc:mysql://localhost/dbname"
    p:username="root"
    p:password="password"/>

<!-- Hibernate session factory -->
<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
    p:dataSource-ref="dataSource">
    <property name="packagesToScan" value="com.beans"/>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</prop>
        </props>
    </property>
</bean> 

<!-- Transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory">
        <ref bean="sessionFactory" />
    </property>
</bean>

<tx:annotation-driven transaction-manager="transactionManager"/>
person Ian Dallas    schedule 27.02.2011
comment
Спасибо за ответ, на самом деле нет ленивой инициализации ни в одном поле пользователя, и, конечно, я не указывал стратегию выборки для простой строки. Я считаю, что проблема в весенней конфигурации - person user12384512; 27.02.2011
comment
Не могли бы вы опубликовать трассировку стека вашего исключения LazyInitializationException? Кажется странным, что не будет какой-то формы ленивой инициализации, если это исключение, которое вы получаете. - person Ian Dallas; 27.02.2011
comment
ОШИБКА org.hibernate.LazyInitializationException - не удалось инициализировать прокси - нет сеанса org.hibernate.LazyInitializationException: не удалось инициализировать прокси - нет сеанса в org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:132) в org.hibernate.proxy .AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:174) в org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190) - person user12384512; 27.02.2011

Из документации Hibernate https://docs.jboss.org/hibernate/orm/4.2/manual/en-US/html/ch11.html#objectstate-loading:

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

Из приведенной выше документации в вашем случае bean-компонент сопоставлен с прокси-сервером spring, поэтому загрузка просто возвращается. Следовательно, вам нужно использовать get() вместо load(), который всегда попадает в базу данных.

person chammu    schedule 13.02.2015