Spring и No session found в потоке (Runnable). scope="prototype" bean

 
 
 
Сообщения:206
Здравствуйте,

Есть бин со scope="prototype". Этот бин является Runnable классом, в который через конструктор (constructor injection) заинжекчены DAO.
Этот бин отправляется на выполнение в
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
</bean>

taskExecutor.execute(myPrototypeBean);


внутри myPrototypeBean при попытке получить данные через DAO получаю org.hibernate.HibernateException: No Session found for current thread

Почему так происходит?

El Gringo
 
 
Сообщения:586
Скорее всего вы используете SessionFactory.getCurrentSession, в вашем контексте сессии просто нет текущей сессии, т.к. По умолчанию хибернат использует контекст сессии managed - вы сами должны положить сессию в контекст, вызвав соответствующий метод класса ManagedSessionContext. Можете переключить контекст сессии на thread (см переменную hibernate.current_session_context_class). Тогда у вас с каждым потоком будет ассоциироваться специальный прокси, доступный через getCurrentSession(), который не позволит ничего делать пока вы на сессии не стартуете транзакцию (самостоятельно или с помощью менеджера транзакций spring). После этого станут доступны и другие операции. Сессия будет автоматически удаляться из контекста по коммиту/откату транзакции.
Изменен:24 мая 2014 20:57
 
 
Сообщения:206
samolisov:
который не позволит ничего делать пока вы на сессии не стартуете транзакцию

Вот да, с транзакциями проблема. В моем приложении используются транзакции:
...
    <aop:config>
        <aop:pointcut id="defaultTransactionPointcut" expression="within(pack.ages.transactional..*)" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="defaultTransactionPointcut"/>
    </aop:config>

    <tx:advice id="txAdvice">
        <tx:attributes>
            <tx:method name="get*" rollback-for="java.lang.Exception" read-only="true" />
            <tx:method name="save*" rollback-for="java.lang.Exception" />
            <tx:method name="delete*" rollback-for="java.lang.Exception" />
        </tx:attributes>
    </tx:advice>
...

Метод, в котором происходит getNamedQuery is not valid without active transaction подпадает под Advice но экзепшон тем не менее происходит. Почему?

El Gringo
Изменен:25 мая 2014 15:09
 
 
Сообщения:586
Покажите настройки менеджера транзакций.
 
 
Сообщения:206
samolisov:
Покажите настройки менеджера транзакций.


Вот:
    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

El Gringo
 
 
Сообщения:206
вот sessionFactory:
<bean id="sessionFactory" name="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="jndiObjectFactoryBean"/>
        <property name="mappingResources">
            <list>
                <value>/fol/ders/Entity.hbm.xml</value>
                ...
                <value>/fol/ders/EntityN.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
                <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
                <prop key="hibernate.connection.release_mode">${hibernate.connection.release_mode}</prop>
                <prop key="hibernate.current_session_context_class">thread</prop>
            </props>
        </property>
    </bean>

El Gringo
 
 
Сообщения:586
В определении txAdvice заполните атрибут transaction-manager, укажите ссылку на ваш transactionManager.
 
 
Сообщения:206
samolisov:
В определении txAdvice заполните атрибут transaction-manager, укажите ссылку на ваш transactionManager.

Указал:
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="get*" rollback-for="java.lang.Exception" read-only="true" />
            <tx:method name="save*" rollback-for="java.lang.Exception" />
            <tx:method name="delete*" rollback-for="java.lang.Exception" />
        </tx:attributes>
    </tx:advice>


Однако, ничего почему то не изменилось. Еще раз перепроверил - подцеплены ли все задействованные методы в транзакции - да, все. IDEA указывает на это через иконки AOP advices напротив названий методов.

El Gringo
 
 
Сообщения:586
Если проект небольшой и не очень секретный, скиньте на почту. Посмотрю весь контекст, пока трудно что-либо сказать.
 
 
Сообщения:586
Посмотрел.

Что сказать, я был не прав, нельзя смешивать управляемые Spring Framework'ом сессии и сессии с hibernate.current_session_context_class = thread, в таком случае Spring Framework не создаст свою обертку, возвращаемую getCurrentSession() и не будет привязывать такие сессии к транзакциям. Лучше не выставлять данный параметр или установить его явно в org.springframework.orm.hibernate3.SpringSessionContext (для 3-го Hibernate) или в org.springframework.orm.hibernate4.SpringSessionContext (для 4-го).

Эксепшн
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
	at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:65)


свидетельствует о проблемах в управлении транзакциями, если ваш сервис не обернут в управляемую Spring Framework'ом транзакцию, то Spring и не помещает сессию в контекст. Нужно разбираться с настройками AOP, у меня заработало так:

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true" rollback-for="java.lang.Exception" propagation="REQUIRED" />
            <tx:method name="save*" rollback-for="java.lang.Exception" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>


Кстати, вам может помочь настройка детального логирования Spring, для этого в classpath нужно добавить:

  • slf4j-api.jar
  • slf4j-log4j12.jar
  • log4j.jar


а так же добавить файл log4j.properties:

log4j.rootLogger=INFO, mainlogger

log4j.appender.mainlogger=org.apache.log4j.ConsoleAppender
log4j.appender.mainlogger.target=System.out
log4j.appender.mainlogger.layout=org.apache.log4j.PatternLayout
log4j.appender.mainlogger.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p [%c{1}] %m%n

#log4j.appender.mainlogger.Threshold=INFO
log4j.logger.org.springframework=DEBUG
 
 
Сообщения:206
Большое спасибо за подробный ответ и такую помощь!
Но,
Quote:
hibernate.current_session_context_class:org.springframework.orm.hibernate4.SpringSessionContext

не помогло. Что то не то видимо у меня накручено в настройках, не помогает. Почитаю больше документации к транзакциям и SpringSessionContext

El Gringo
Изменен:27 мая 2014 18:13
 
 
Сообщения:9831
Судя по приведенному коду ты пытаешься в отдельных потоках обращаться к DAO. Т.е. транзакция началась в одном потоке и привязалась к нему же, а сессию ты пытаешься получить в другом потоке, который ничего про нее не слышал.
 
 
Сообщения:206
Староверъ:
Судя по приведенному коду ты пытаешься в отдельных потоках обращаться к DAO. Т.е. транзакция началась в одном потоке и привязалась к нему же, а сессию ты пытаешься получить в другом потоке, который ничего про нее не слышал.

Да, именно так. Как решается данная проблема? scope="prototype" для DAO - глупо?

El Gringo
 
 
Сообщения:206
Хотя подождите ка. DAO же синглтон. У меня есть абстрактный класс, в который инжектится sessionFactory. От этого класса наследуются все DAO, и получают сессию, следовательно сессия открывается и закрывается в синглтонах. Разве не так?

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

Вам надо либо оборачивать в транзакцию метод run() (или call(), что там у вас) каждого вашего руннайбл-бина, либо если вы хотите их все выполнить по принципу все-или-ничего - пересмотреть подход к организации многопоточности. Добавлю так же, что передавать одну сессию из ДАО между потоками, если вы захотите это делать – плохое решение, т.к. Класс сессии непотокобезопасен.
Изменен:27 мая 2014 20:47
 
Модераторы:wedens
Сейчас эту тему просматривают:Нет