我想在我的应用程序开始时读取文本数据装置(CSV 文件)并将其放入我的数据库中。
为此,我创建了一个带有初始化方法(@PostConstruct注释)的PopulationService 。
我还希望它们在单个事务中执行,因此我在同一方法上添加了@Transactional 。
但是,@Transactional 似乎被忽略了:事务是在我的低级 DAO 方法中启动/停止的。
那我需要手动管理交易吗?
我想在我的应用程序开始时读取文本数据装置(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
}
}
@Platon Serbin的回答对我不起作用。所以我一直在寻找,找到了以下救了我一命的答案。:D
答案是@PostConstruct 中的 No 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;
}
});
}
注入 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
是从@PostContruct
Bean 初始化时调用的 -annotated 方法调用的,它实际上不会在 Spring Transaction 内部执行,并且尴尬的(过时的?)解决方案得到了讨论和接受,而不是这个非常简单和直接的解决方案,后者甚至被否决了。
欢迎怀疑的 Thomases :)在 GitHub 上查看一个示例 Spring Boot 应用程序,该应用程序实现了上述解决方案。
恕我直言,真正造成混淆的原因是:对方法的调用@Transactional
应该通过定义了此类方法的 Bean 的代理版本来完成。
当从另一个 Bean@Transactional
调用一个方法时,另一个 Bean 通常会注入这个方法并调用它的代理(例如通过@Autowired)版本,一切都很好。
当直接@Transactional
从同一个 Bean调用方法时,通过通常的 Java 调用,不涉及 Spring AOP/Proxy 机制,也不在 Transaction 内部执行该方法。
当如建议的解决方案中,通过自注入代理(字段)@Transactional
从同一个 Bean 调用方法时,情况基本等同于案例 1。self
使用 transactionOperations.execute()
in@PostConstruct
或 in@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 {
}
}
spring 的事务部分可能在@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();
}
}