spring @ Transmitting on @PostConstruct方法

n9vozmp4  于 12个月前  发布在  Spring
关注(0)|答案(7)|浏览(126)

我想在应用程序开始时读取文本数据固定装置(CSV文件),并将其放入数据库中。
为此,我创建了一个带有初始化方法(@PostConstructannotation)的PopulationService
我还希望它们在单个事务中执行,因此我在同一个方法上添加了**@ translation**。
然而,@ translation似乎被忽略了:transaction在我的低级DAO方法中启动/停止。
那么我需要手动管理交易吗?

jvidinwx

jvidinwx1#

引用自legacy(已关闭)Spring论坛:
在@PostConstruct中(与InitializingBean接口中的afterProperties Set一样),没有办法确保所有的后处理都已经完成,因此(实际上)不可能有Transactions。确保其工作的唯一方法是使用TransactionTemplate。
因此,如果您希望在transaction中执行@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
            }
        });
   }
}

字符串

jckbn6z7

jckbn6z72#

我认为@PostConstruct只是确保当前类的预处理/注入完成,并不意味着整个应用程序上下文的初始化完成。
但是,您可以使用spring事件系统在应用程序上下文初始化完成时接收事件:

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

字符串
有关详细信息,请参阅文档部分标准和自定义事件。

x6h2sr28

x6h2sr283#

作为Spring 4.2的更新,@EventListener annotation允许更清晰的实现:

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

字符串

55ooxyrt

55ooxyrt4#

注入self并调用@Transactional方法

public class AccountService {

    @Autowired
    private AccountService self;

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

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

}

字符串
对于不支持自注入的旧版Spring,inject BeanFactory并获取self作为beanFactory.getBean(AccountService.class)

编辑

看起来,自从这个解决方案在1.5年前发布以来,开发人员仍然有这样的印象,即如果在Bean初始化时从@PostContruct注解的方法调用一个用@Transactional注解的方法,那么它实际上不会在Spring Transaction内部执行,并且很尴尬。(过时?)解决方案得到讨论和接受,而不是这个非常简单和直接的,后者甚至得到否决。
欢迎持怀疑态度的人:)查看实现上述解决方案的example Spring Boot application at GitHub
IMHO,实际上导致混淆的原因是:@Transactional方法的调用应该通过定义了这种方法的Bean的代理版本来完成
1.当@Transactional方法从另一个Bean调用时,另一个Bean通常会注入这个方法并调用它的代理(例如通过@Autowired)版本,一切正常。
1.当@Transactional方法直接从同一Bean调用时,通过通常的Java调用,不涉及Spring AOP/Proxy机制,并且该方法不在Transaction内部执行。
1.在建议的解决方案中,当@Transactional方法通过自注入代理从同一Bean调用self字段)时,情况基本上等同于情况1。

hpxqektj

hpxqektj5#

@Platon Serbin的答案对我不起作用。所以我继续搜索,找到了以下拯救我生命的答案。:D

答案在这里No Session Hibernate in @PostConstruct,我冒昧地转录:

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

    });
}

字符串

xn1cxnb4

xn1cxnb46#

spring的transaction部分可能没有在@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();
    }
}

字符串

oyt4ldly

oyt4ldly7#

@PostConstruct@NoTransaction方法中使用transactionOperations.execute()都可以工作

@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 {

    }
}

字符串

相关问题