5

この質問の核心は、Spring シャットダウン フックによってトリガーされたメソッドからトランザクションを実行することは可能ですか?

現時点では、この質問にあるように SmartLifeCycle を実装する HyperSqlDbServer クラスがあります: In a spring Bean is it possible to have a shutdown method that can use transactions?

そのクラスには、停止メソッドの一部として呼び出されるトランザクションとしてマークされたメソッドがあります。

@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();
    }

}
4

1 に答える 1

2

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 ロック ファイルを残すことなく、サーバーを正常に再起動できるようになりました。

于 2013-04-25T16:14:56.273 に答える