Ошибка: «Не удалось получить сеанс с синхронизацией транзакций для текущего потока» при использовании аннотаций @Transactional и @Scheduled Spring - PullRequest
0 голосов
/ 21 сентября 2019

Я знаю, что подобные вопросы задавались ранее, но я думаю, что делаю правильные вещи, и, несмотря на это, я все еще получаю вышеуказанную ошибку.Прежде чем перейти к подробным сведениям в качестве сводки, в моем проекте есть несколько классов услуг (помеченных 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, а только для этого, и он больше не работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...