我想在我的应用程序开始时读取文本数据装置(CSV 文件)并将其放入我的数据库中。
为此,我创建了一个带有初始化方法(@PostConstruct注释)的PopulationService。
我还希望它们在单个事务中执行,因此我在同一方法上添加了@Transactional。
但是,@Transactional似乎被忽略了: 事务在我的低级 DAO 方法中启动/停止。
我需要手动管理交易吗?
引自遗留(已关闭)Spring 论坛:
在@PostConstruct(与InitializingBean接口中的afterPropertiesSet一样)中,无法确保所有后处理都已完成,因此(实际上)不能有事务。确保其正常工作的唯一方法是使用 TransactionTemplate。
因此,如果您希望在事务中执行
@PostConstruct
中的某些内容,您必须执行以下操作:
@Service("something")
public class Something {
@Autowired
@Qualifier("transactionManager")
protected PlatformTransactionManager txManager;
@PostConstruct
private void init(){
TransactionTemplate tmpl = new TransactionTemplate(txManager);
tmpl.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
//PUT YOUR CALL TO SERVICE HERE
}
});
}
}
我认为
@PostConstruct
仅确保当前课程的预处理/注入完成。这并不意味着整个应用程序上下文的初始化已经完成。
但是,当应用程序上下文初始化完成时,您可以使用 spring 事件系统来接收事件:
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
public void onApplicationEvent(ContextRefreshedEvent event) {
// do startup code ..
}
}
请参阅文档部分标准和自定义事件了解更多详细信息。
作为更新,从 Spring 4.2 开始,
@EventListener
注释允许更清晰的实现:
@Service
public class InitService {
@Autowired
MyDAO myDAO;
@EventListener(ContextRefreshedEvent.class)
public void onApplicationEvent(ContextRefreshedEvent event) {
event.getApplicationContext().getBean(InitService.class).initialize();
}
@Transactional
public void initialize() {
// use the DAO
}
}
注入 self 并通过它调用
@Transactional
方法
public class AccountService {
@Autowired
private AccountService self;
@Transactional
public void resetAllAccounts(){
//...
}
@PostConstruct
private void init(){
self.resetAllAccounts();
}
}
对于不支持自注入的旧 Spring 版本,注入
BeanFactory
并获取 self
为 beanFactory.getBean(AccountService.class)
编辑
看起来自从这个解决方案在 1.5 年前发布以来,开发人员仍然认为如果有一个方法, 用
@Transactional
注释,从 Bean 初始化时调用的 @PostContruct
注释方法调用,它不会在 Spring Transaction 中实际执行,并且会讨论和接受笨拙(过时?)的解决方案,而不是这个非常简单的解决方案直接的一个,后者甚至会被否决。
持怀疑态度的 Thomases :) 欢迎查看 GitHub 上的 Spring Boot 示例应用程序,它实现了上述解决方案。
恕我直言,真正造成混乱的是:对
@Transactional
方法的调用应该通过定义了此类方法的Bean的代理版本来完成。
当从
另一个Bean调用
@Transactional
方法时,另一个Bean通常会注入这个方法并调用它的代理(例如通过@Autowired)版本,一切都很好。
当直接从同一个 Bean
调用
@Transactional
方法时,通过通常的 Java 调用,不涉及 Spring AOP/Proxy 机制,并且该方法不会在 Transaction 内部执行。
(
@Transactional
字段)从同一个Bean调用self
方法时,情况基本上等同于情况1。
的回答对我不起作用。所以我继续寻找并找到了以下拯救我生命的答案。 :D 答案就在这里
@PostConstruct中没有Session Hibernate,我冒昧抄录一下:
@Service("myService")
@Transactional(readOnly = true)
public class MyServiceImpl implements MyService {
@Autowired
private MyDao myDao;
private CacheList cacheList;
@Autowired
public void MyServiceImpl(PlatformTransactionManager transactionManager) {
this.cacheList = (CacheList) new TransactionTemplate(transactionManager).execute(new TransactionCallback(){
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
CacheList cacheList = new CacheList();
cacheList.reloadCache(MyServiceImpl.this.myDao.getAllFromServer());
return cacheList;
}
});
}
@PostConstruct
没有完全初始化。
使用ContextRefreshedEvent
事件的侦听器来确保事务可用:
@Component
public class YourService
implements ApplicationListener<ContextRefreshedEvent> // <= ensure correct timing!
{
private final YourRepo repo;
public YourService (YourRepo repo) {this.repo = repo;}
@Transactional // <= ensure transaction!
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
repo.doSomethingWithinTransaction();
}
}
transactionOperations.execute()
或
@PostConstruct
方法中使用 @NoTransaction
都可以@Service
public class ConfigurationService implements ApplicationContextAware {
private static final Logger LOG = LoggerFactory.getLogger(ConfigurationService.class);
private ConfigDAO dao;
private TransactionOperations transactionOperations;
@Autowired
public void setTransactionOperations(TransactionOperations transactionOperations) {
this.transactionOperations = transactionOperations;
}
@Autowired
public void setConfigurationDAO(ConfigDAO dao) {
this.dao = dao;
}
@PostConstruct
public void postConstruct() {
try { transactionOperations.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(final TransactionStatus status) {
ResultSet<Config> configs = dao.queryAll();
}
});
}
catch (Exception ex)
{
LOG.trace(ex.getMessage(), ex);
}
}
@NoTransaction
public void saveConfiguration(final Configuration configuration, final boolean applicationSpecific) {
String name = configuration.getName();
Configuration original = transactionOperations.execute((TransactionCallback<Configuration>) status ->
getConfiguration(configuration.getName(), applicationSpecific, null));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
}
}