Менеджер транзакций spring mvc, определенный в корневом контексте, не открывает транзакции в dao, определенном в дочернем контексте

Я столкнулся с реальной проблемой и решил ее, но не мог понять, что произошло.

Я определил компоненты transactionManager и sessionFactory в корневом контексте, а мой класс dao - с методами @Transactional в контексте диспетчера. И это все. Когда я пытался использовать getCurrentSession() в dao, я получал «не могу получить текущий сеанс».

Но, насколько я помню, контекст диспетчера знает о корневом контексте и имеет доступ ко всем bean-компонентам в корневом контексте.

Может кто-нибудь объяснить мне, почему транзакции не открываются перед методом @Transactional, если transactionManager и sessionFactory были определены в корневом контексте, а класс с @Transactional в дочернем контексте?

Класс конфигурации базы данных

@Configuration
@EnableTransactionManagement
public class DatabaseConfig {
    @Bean
    public LocalSessionFactoryBean sessionFactory() throws IOException {
        LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
        sessionFactoryBean.setDataSource(getDatabaseDataSource());
        sessionFactoryBean.setPackagesToScan("com.varguss.domain");

        Properties properties = new Properties();
        properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL57Dialect");
        properties.setProperty("hibernate.show_sql", "true");
        properties.setProperty("hibernate.hbm2ddl.auto", "update");
        properties.setProperty("hibernate.connection.useUnicode", "true");
        properties.setProperty("hibernate.connection.characterEncoding", "utf8");
        properties.setProperty("hibernate.connection.charSet", "utf8");

        sessionFactoryBean.setHibernateProperties(properties);

        return sessionFactoryBean;
    }

    @Bean
    @Autowired
    public HibernateTransactionManager getTransactionManager(SessionFactory sessionFactory) {
        return new HibernateTransactionManager(sessionFactory);
    }

    @Bean(name = "dataSource", destroyMethod = "close")
    public BasicDataSource getDatabaseDataSource() throws IOException {
        BasicDataSource databaseDataSource = new BasicDataSource();

        Properties properties = new Properties();

        ClassPathResource propertiesFileResource = new ClassPathResource("database.properties");
        properties.load(propertiesFileResource.getInputStream());

        databaseDataSource.setDriverClassName(properties.getProperty("driverClassName"));
        databaseDataSource.setUrl(properties.getProperty("url"));
        databaseDataSource.setUsername(properties.getProperty("username"));
        databaseDataSource.setPassword(properties.getProperty("password"));

        return databaseDataSource;
    }
}

DAO класс

@Repository
@Transactional
public class DbComputerPartDAO implements ComputerPartDAO {
    private SessionFactory sessionFactory;
    private Strategy strategy;

    @Autowired
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
        strategy = StrategyFactory.getStrategy(StrategyType.ALL, sessionFactory);
    }

    @Override
    @Transactional(readOnly = true)
    public List<ComputerPart> allParts() {
        return sessionFactory.getCurrentSession().createQuery("FROM ComputerPart part ORDER BY part.count DESC", ComputerPart.class).getResultList();
    }

    @Override
    @Transactional(readOnly = true)
    public ComputerPart part(Long id) {
        return sessionFactory.getCurrentSession().find(ComputerPart.class, id);
    }

    @Override
    public void save(String name, boolean isImportant, Long count) {
        sessionFactory.getCurrentSession().saveOrUpdate(new ComputerPart(name, isImportant, count));
    }

    @Override
    public void remove(Long id) {
        ComputerPart computerPart = part(id);

        if (computerPart != null)
            sessionFactory.getCurrentSession().delete(computerPart);
    }

    @Override
    @Transactional(readOnly = true)
    public List<ComputerPart> byImportance(boolean isImportant) {
        return sessionFactory.getCurrentSession().createQuery("FROM ComputerPart part WHERE part.isImportant ORDER BY part.count DESC", ComputerPart.class).getResultList();
    }

    @Override
    public void updateImportance(Long id, boolean isImportant) {
        ComputerPart computerPart = part(id);

        if (computerPart != null)
            computerPart.setImportant(isImportant);
    }

    @Override
    public void updateName(Long id, String name) {
        ComputerPart computerPart = part(id);

        if (computerPart != null)
            computerPart.setName(name);
    }

    @Override
    public void updateCount(Long id, Long count) {
        ComputerPart computerPart = part(id);

        if (computerPart != null)
            computerPart.setCount(count);
    }

    @Override
    @Transactional(readOnly = true)
    public List<ComputerPart> page(int pageNumber) {
        return strategy.page(pageNumber);
    }

    @Override
    @Transactional(readOnly = true)
    public List<ComputerPart> parts() {
        return strategy.parts();
    }

    @Override
    @Transactional(readOnly = true)
    public Integer lastPageNumber() {
        return strategy.lastPageNumber();
    }

    @Override
    @Transactional(readOnly = true)
    public List<ComputerPart> search(String partOfName) {
        return strategy.search(partOfName);
    }

    @Override
    public void changeStrategy(StrategyType strategyType) {
        this.strategy = StrategyFactory.getStrategy(strategyType, sessionFactory);
    }
}

Корневой контекст

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- Root Context: defines shared resources visible to all other web components -->
    <context:annotation-config/>


    <bean class="com.varguss.config.DatabaseConfig"/>
</beans>

Дочерний контекст

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:beans="http://www.springframework.org/schema/beans"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:p="http://www.springframework.org/schema/p"
             xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->

    <!-- Enables the Spring MVC @Controller programming model -->
    <annotation-driven />

    <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
    <resources mapping="/resources/**" location="/resources/" />

    <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /resources/views/ directory -->
    <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/views/" p:suffix=".jsp" />

    <context:component-scan base-package="com.varguss.dao" />
    <context:component-scan base-package="com.varguss.controller" />
</beans:beans>

Вы пробовали использовать <annotation-driven /> в корневом контексте?

Seymur Asadov 13.09.2018 19:21

Хотя дочерний элемент может видеть компоненты из корневого контекста, АОП, определенный в корневом контексте, не применяется к дочернему контексту. АОП применяется только к bean-компонентам в том же контексте. Следовательно, @EnableTransactionManagement не применяется к bean-компонентам в дочернем контексте.

M. Deinum 13.09.2018 21:28

@ M.Deinum Звучит логично. Спасибо за пояснения :) Можете ли вы создать ответный пост? Я должен отметить один, чтобы закрыть вопрос.

rapid88 14.09.2018 07:32
0
3
258
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

При использовании иерархического контекста приложения (родительский и дочерний) дочерний элемент может видеть bean-компоненты от родителя. Таким образом, он может обнаруживать EntityManagerFactory и PlatformTransactionManager.

Однако при использовании таких вещей, как AOP, который применяется только к bean-компонентам в том же контексте приложения, в котором определен AOP. Таким образом, AOP, определенный в родительском контексте, применяется только к bean-компонентам в родительском контексте, а не к bean-компонентам в дочерних контекстах.

Итак, в вашем случае @EnableTransactionManagement находится в родительском контексте, но там нет bean-компонентов с @Transactional, они находятся в дочернем контексте. Так что либо создайте @Configuration, который разрешает там транзакции, либо используйте <tx:annotation-driven /> в своей конфигурации XML.

Другие вопросы по теме