@在Spring上关闭事务以正确关闭Hsqldb
这个问题的核心是:是否可以从由SpringShutdown钩子触发的方法执行事务 目前,我有一个实现SmartLifeCycle的HyperSqlDbServer类,如本问题所示: 我在该类中有一个标记为transactional的方法,该方法作为stop方法的一部分被调用:@在Spring上关闭事务以正确关闭Hsqldb,spring,hsqldb,shutdown,transactional,Spring,Hsqldb,Shutdown,Transactional,这个问题的核心是:是否可以从由SpringShutdown钩子触发的方法执行事务 目前,我有一个实现SmartLifeCycle的HyperSqlDbServer类,如本问题所示: 我在该类中有一个标记为transactional的方法,该方法作为stop方法的一部分被调用: @Transactional public void executeShutdown() { hsqlDBShutdownService.executeShutdownQuery(); hsqlDBShu
@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();
}
}
我找到了一个关闭HsqlDB数据库的解决方法,但它涉及到避免使用Spring的EntityManager和@Transactional,因为它们在服务器关闭期间显然不起作用。下面是我修改的HSQLDShutdownservice。关键的变化是,我没有使用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锁定文件。尝试调试该行并查看
hsqldshutdownservice
实例的对象类型。它应该被代理。如果没有,则表示您的@Transactional
无法工作。调试显示该对象上没有代理。所以我猜,结合原始TransactionRequiredException,可以确认注释不起作用。那么,作为SmartLifecycle停止方法的一部分,是否不可能执行事务?@mag32就是这样。如果您使用事务管理器配置更新了您的问题,我们可以仔细查看。好的。此外,在调试过程中,HsqlDBShutdownService未被代理,但在其executeShutdownQuery方法中,EntityManger被代理。您没有@ComponentScan
注释。您必须扫描包含@组件的包,即@服务
。此外,您的TransactionManager
应该有一个对数据源
或EntityManager
(不确定是哪个)的引用,以便它可以启动和结束事务。