Я знаю, что подобные вопросы задавались ранее, но я думаю, что делаю правильные вещи, и, несмотря на это, я все еще получаю вышеуказанную ошибку.Прежде чем перейти к подробным сведениям в качестве сводки, в моем проекте есть несколько классов услуг (помеченных org.springframework.stereotype.Service ), и на некоторых из них у меня есть запланированные методы (помеченные org.springframework.scheduling.annotation.Scheduled ), которые выполняют операции с БД, и поэтому они также помечены org.springframework.transaction.annotation.Transactional .Странно, они работают нормально во всех случаях, но одна, где я получаю сообщение об ошибке «Не удалось получить синхронизированный транзакцией сеанс для текущего потока» при доступе к БД.Запланированные и транзакционные методы всегда общедоступны и являются частью интерфейса, который реализуют службы, и, по-видимому, все места, где у меня есть запланированные и транзакционные методы, настроены одинаково, поэтому странно, что ошибка возникает только в одном месте.
Вот как настраиваются транзакции:
В контексте Spring xml:
<context:annotation-config/>
Класс конфигурации Hibernate:
@Configuration
@EnableTransactionManagement
public class HibernateConfig {
@Autowired
private ApplicationContext context;
@Bean
public LocalSessionFactoryBean getSessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setConfigLocation(context.getResource("classpath:hibernate.cfg.xml"));
sessionFactory.setPackagesToScan("mydomain.model.persisted");
return sessionFactory;
}
@Bean
public HibernateTransactionManager getTransactionManager() {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(getSessionFactory().getObject());
return transactionManager;
}
}
Сбой метода и класса:
@Service
public class ProfileServiceImpl implements ProfileService {
@Override
@Scheduled(fixedDelay = 60000)
@Transactional(readOnly = true)
public void scheduledProfilesReinit() {
ProfileListVO[] recentProfiles = getRecentProfilesFromDB();
... // do something with those profiles
}
private Profile[] getRecentProfilesFromDB() {
return profileDAO.getRecentProfiles();
}
}
public interface ProfileService {
void scheduledProfilesReinit();
... other methods
}
@Repository
public class ProfileDAOImpl implements ProfileDAO {
@Override
public List<Profile> getRecentProfiles() {
TypedQuery<Profile> query = sessionFactory.getCurrentSession()
.createQuery("from Profile as p " +
"where p.deactivated = :deactivated " +
"and p.published = :published " +
"order by p.createdOn desc")
.setParameter("deactivated", false)
.setParameter("published", true)
.setFirstResult(0)
.setMaxResults(AppConfig.RECENT_PROFILES_MAX_ITEMS);
return query.getResultList();
}
}
Когда этот код выше запускает запланированный метод: scheduleProfilesReinit пытается получить последние профили из БД и завершается с ошибкой в sessionFactory.getCurrentSession () вDAO.Тот же метод в DAO используется из других мест, где он работает без проблем.Также у меня точно такая же структура в других классах обслуживания снова без проблем.Я не описываю явно bean-компоненты в контексте комплекта, а вместо этого я обнаруживаю их с помощью:
<context:component-scan base-package="mydomain.package"/>
При отладке я обнаружил, что при вызове метода scheduleProfilesReinit он вызываетсяне на Spring прокси ( JdkDynamicAopProxy ), как это должно быть, а на обычном экземпляре ProfileServiceImpl.Мне удалось решить эту проблему, когда я в явном виде получил прокси-сервер Spring этого класса, изменил закрытый метод, который обращается к БД, на общедоступный и аннотировал его с помощью Transactional:
@Service
public class ProfileServiceImpl implements ProfileService {
@Autowired
private ProfileService _self; // proxy reference injected
@Override
@Scheduled(fixedDelay = 60000)
@Transactional(readOnly = true)
public void scheduledHomePageRecentProfilesReinit() {
ProfileListVO[] recentProfiles = _self.getRecentProfilesFromDB();
}
@Override
@Transactional(readOnly = true)
public ProfileListVO[] getRecentProfilesFromDB() {
return profileDAO.getRecentProfiles();
}
}
В то время как приведенный выше код решаетпроблема в том, что это обходной путь, и я не уверен, почему он был нужен для этого класса, но не нужен для других классов в моем коде.Я не вижу никакой разницы, поэтому я не показываю рабочие примеры.Почему scheduleHomePageRecentProfilesReinit метод не вызывается Spring на его (Spring's) прокси и вместо этого вызывается для простого объекта, который не поддерживает транзакции?Почему это не так в других случаях - я также проверил, что этот метод и другие запланированные методы, которые правильно работают, вызываются из того же потока из ScheduledThreadPoolExecutor, который предположительно является пулом управляемых потоков Spring.
Примечание: Iтакже попробовал обходной путь выше с небольшой разницей - не вызывая его для объекта _self autowired, а только для этого, и он больше не работает.