45

我想在我的应用程序开始时读取文本数据装置(CSV 文件)并将其放入我的数据库中。

为此,我创建了一个带有初始化方法(@PostConstruct注释)的PopulationService 。

我还希望它们在单个事务中执行,因此我在同一方法上添加了@Transactional 。

但是,@Transactional 似乎被忽略了事务是在我的低级 DAO 方法中启动/停止的。

那我需要手动管理交易吗?

4

7 回答 7

91

来自旧版(已关闭)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
            }
        });
   }
}
于 2013-09-13T15:53:12.343 回答
17

我认为@PostConstruct只能确保完成当前课程的预处理/注入。这并不意味着整个应用程序上下文的初始化完成。

但是,您可以使用 spring 事件系统在应用程序上下文的初始化完成时接收事件:

public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
  public void onApplicationEvent(ContextRefreshedEvent event) {
    // do startup code ..
  }    
}

有关更多详细信息,请参阅文档部分标准和自定义事件

于 2013-06-27T16:22:32.423 回答
9

作为更新,从 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
    }

}

于 2018-12-31T01:28:49.503 回答
3

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

    });
}
于 2018-03-03T14:37:22.107 回答
2

注入 self 并通过它调用@Transactional方法

public class AccountService {

    @Autowired
    private AccountService self;

    @Transactional
    public void resetAllAccounts(){
        //... 
    }

    @PostConstruct
    private void init(){
        self.resetAllAccounts();
    }

}

对于不支持自注入的旧 Spring 版本,注入BeanFactory并获取selfbeanFactory.getBean(AccountService.class)

编辑

看起来是这样的,因为这个解决方案已经在 1.5 年前发布了,开发人员仍然有这样的印象,如果一个用 注释的方法@Transactional是从@PostContructBean 初始化时调用的 -annotated 方法调用的,它实际上不会在 Spring Transaction 内部执行,并且尴尬的(过时的?)解决方案得到了讨论和接受,而不是这个非常简单和直接的解决方案,后者甚至被否决了。

欢迎怀疑的 Thomases :)在 GitHub 上查看一个示例 Spring Boot 应用程序,该应用程序实现了上述解决方案。

恕我直言,真正造成混淆的原因是:对方法的调用@Transactional应该通过定义了此类方法的 Bean 的代理版本来完成

  1. 当从另一个 Bean@Transactional调用一个方法时,另一个 Bean 通常会注入这个方法并调用它的代理(例如通过@Autowired)版本,一切都很好。

  2. 当直接@Transactional同一个 Bean调用方法时,通过通常的 Java 调用,不涉及 Spring AOP/Proxy 机制,也不在 Transaction 内部执行该方法。

  3. 当如建议的解决方案中,通过自注入代理(字段)@Transactional从同一个 Bean 调用方法时,情况基本等同于案例 1。self

于 2018-06-19T18:45:49.807 回答
0

使用 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 {

    }
}
于 2018-03-28T07:46:53.367 回答
0

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();
    }
}
于 2021-04-26T10:29:25.913 回答