@Transactional при завершении работы Spring для правильного завершения работы Hsqldb

Суть этого вопроса заключается в следующем: возможно ли выполнить транзакцию из метода, запущенного хуком выключения Spring?

На данный момент у меня есть класс HyperSqlDbServer, который реализует SmartLifeCycle, как указано в этом вопросе: В bean-компоненте Spring возможно ли иметь метод завершения работы, который может использовать транзакции?

У меня есть метод в этом классе, помеченный как транзакционный, который вызывается как часть метода остановки:

@Transactional
public void executeShutdown() {
    hsqlDBShutdownService.executeShutdownQuery();
    hsqlDBShutdownService.closeEntityManager();
}

Служба, используемая в этом методе, является чем-то вроде хака, который мне пришлось сделать, потому что я не мог автоматически подключить EntityManager к этому классу:

@Service
public class HsqlDBShutdownService {

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    private HyperSqlDbServer hyperSqlDbServer;

    @Transactional
    public void executeShutdownQuery() {
        entityManager.createNativeQuery("SHUTDOWN").executeUpdate();
    }

    @Transactional
    public void closeEntityManager() {
        entityManager.close();
    }

    @PostConstruct
    public void setHsqlDBShutdownService() {
        hyperSqlDbServer.setShutdownService(this);
    }
}

Вы можете заметить, что все, что я на самом деле пытаюсь сделать, это вызвать запрос «SHUTDOWN» перед остановкой сервера. Без этого файл блокировки hsqldb сохраняется при перезапуске сервера, и сервер выдает исключение.

Приведенный выше код создает следующее исключение:

javax.persistence.TransactionRequiredException: Executing an update/delete query
    at org.hibernate.ejb.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:96)
        ...

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

К вашему сведению, я также попробовал аннотацию @PreDestroy, но получил то же исключение TransactionRequiredException.

Изменить: для полноты я использую JpaTransactionManager, а аннотации @Transactional работают во всем моем проекте, за исключением завершения работы...

Изменить 2: конфигурация источника данных и диспетчера транзакций:

@Configuration
@EnableTransactionManagement
@PropertySource("classpath:persistence.properties")
public class PersistenceConfig implements TransactionManagementConfigurer {

    private static final String PASSWORD_PROPERTY = "dataSource.password";
    private static final String USERNAME_PROPERTY = "dataSource.username";
    private static final String URL_PROPERTY = "dataSource.url";
    private static final String DRIVER_CLASS_NAME_PROPERTY = "dataSource.driverClassName";

    @Autowired
    private Environment env;

    @Bean
    @DependsOn("hsqlDb")
    public DataSource configureDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty(DRIVER_CLASS_NAME_PROPERTY));
        dataSource.setUrl(env.getProperty(URL_PROPERTY));
        dataSource.setUsername(env.getProperty(USERNAME_PROPERTY));
        dataSource.setPassword(env.getProperty(PASSWORD_PROPERTY));
        return dataSource;
    }

    @Bean
    @DependsOn("hsqlDb")
    public LocalContainerEntityManagerFactoryBean configureEntityManagerFactory() {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(configureDataSource());
        entityManagerFactoryBean.setPackagesToScan("com.mycompany.model.db");
        entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());

        Properties jpaProperties = new Properties();
        jpaProperties.put(org.hibernate.cfg.Environment.DIALECT, env.getProperty(org.hibernate.cfg.Environment.DIALECT));
        jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_AUTO, env.getProperty(org.hibernate.cfg.Environment.HBM2DDL_AUTO));
        jpaProperties.put(org.hibernate.cfg.Environment.SHOW_SQL, env.getProperty(org.hibernate.cfg.Environment.SHOW_SQL));
        jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_IMPORT_FILES_SQL_EXTRACTOR, env.getProperty(org.hibernate.cfg.Environment.HBM2DDL_IMPORT_FILES_SQL_EXTRACTOR));
        jpaProperties.put(org.hibernate.cfg.Environment.HBM2DDL_IMPORT_FILES, env.getProperty(org.hibernate.cfg.Environment.HBM2DDL_IMPORT_FILES));
        entityManagerFactoryBean.setJpaProperties(jpaProperties);

        return entityManagerFactoryBean;
    }

    @Override
    @Bean()
    @DependsOn("hsqlDb")
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return new JpaTransactionManager();
    }

}

person mag382    schedule 24.04.2013    source источник
comment
Попробуйте отладить эту строку и посмотреть на тип объекта вашего экземпляра HsqlDBShutdownService. Он должен быть проксирован. Если это не так, ваш @Transactional не работает.   -  person Sotirios Delimanolis    schedule 25.04.2013
comment
Отладка не показывает прокси для этого объекта. Итак, я предполагаю, что в сочетании с исходным TransactionRequiredException подтверждает, что аннотация не работает. Так что, просто невозможно выполнить транзакцию как часть метода остановки SmartLifecycle?   -  person mag382    schedule 25.04.2013
comment
@ mag32 Вот и все. Если вы обновите свой вопрос с конфигурацией диспетчера транзакций, мы сможем рассмотреть его более подробно.   -  person Sotirios Delimanolis    schedule 25.04.2013
comment
Ок загрузил. Кроме того, при отладке HsqlDBShutdownService не был проксирован, но в его методе executeShutdownQuery проксирован EntityManger IS.   -  person mag382    schedule 25.04.2013
comment
У вас нет аннотации @ComponentScan. Вы должны сканировать пакеты, которые включают ваши @Component, т.е. @Service. Кроме того, ваш TransactionManager должен иметь ссылку на ваш DataSource или EntityManager (не уверен, какой), чтобы он мог начинать и завершать транзакции.   -  person Sotirios Delimanolis    schedule 25.04.2013
comment
Да, у меня есть это в другом файле конфигурации, правильно компонент сканирует мои пакеты: @ComponentScan(basePackages = {com.mycompany}). Теперь посмотрю ваше второе заявление.   -  person mag382    schedule 25.04.2013
comment
Спасибо за предложение, но явная установка EntityManagerFactory не решила проблему: JpaTransactionManager jpa = new JpaTransactionManager(); jpa.setEntityManagerFactory(configureEntityManagerFactory().getObject());   -  person mag382    schedule 25.04.2013
comment
У меня заканчиваются идеи. Является ли этот класс @Configuration частью контекста приложения или контекста сервлета?   -  person Sotirios Delimanolis    schedule 25.04.2013
comment
Это часть контекста приложения. В настоящее время я пытаюсь создать собственное отдельное соединение jdbc, чтобы самостоятельно отключить запрос на отключение. Кажется, это работает, но при перезапуске сервера я получаю исключения nosuchbean, что, как мне кажется, указывает на какую-то утечку памяти.   -  person mag382    schedule 25.04.2013
comment
Я нашел решение. Спасибо за помощь в разговоре. Я мог бы опубликовать ответ, но вы должны получить некоторую карму за помощь мне. Есть ли способ отправить ответ в личку?   -  person mag382    schedule 25.04.2013
comment
Вы должны ответить на свой вопрос. Не беспокойтесь о голосовании, если у вас есть решение.   -  person Sotirios Delimanolis    schedule 25.04.2013


Ответы (1)


Я нашел обходной путь для закрытия базы данных HsqlDB, но он включает в себя отказ от использования Spring EntityManager и @Transactional, поскольку они, по-видимому, не работают во время выключения сервера. Мой модифицированный HsqlDBShutdownService приведен ниже. Ключевое изменение заключается в том, что вместо использования EntityManager для вызова запроса я создаю новое соединение jdbc вручную и таким образом вызываю запрос. Это позволяет избежать требования для @Transactional:

@Service
public class HsqlDBShutdownService  {

    @Autowired
    private ApplicationContext applicationContext;

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    private HyperSqlDbServer hyperSqlDbServer; 

    public void executeShutdownQuery() {

        Connection conn = null;
        try {
            JdbcTemplate jdbcTemplate = new JdbcTemplate(this.applicationContext.getBean(DataSource.class));
            conn = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
            conn.setAutoCommit(true);
            jdbcTemplate.execute("SHUTDOWN"); 
        } catch(Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                if(conn != null)
                    conn.close();
            } catch(Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    @Transactional
    public void closeEntityManager() {
        entityManager.close();
    }

    @PostConstruct
    public void setHsqlDBShutdownService() {
        hyperSqlDbServer.setShutdownService(this);
    }

}

Теперь сервер может успешно перезапуститься, не оставляя файлов блокировки Hsqldb.

person mag382    schedule 25.04.2013