Je veux lire les données textuelles (fichiers CSV) au début de mon application et les mettre dans ma base de données.
Pour cela, j'ai créé un PopulationService avec une méthode d'initialisation ( @PostConstruct annotation).
Je veux aussi qu'ils soient exécutés dans une seule transaction, et c'est pourquoi j'ai ajouté @Transactional sur la même méthode.
Cependant, le @Transactional semble être ignoré: La transaction est démarrée/arrêtée avec mes méthodes DAO de bas niveau.
Dois-je gérer la transaction manuellement alors?
cela pourrait être utile ( http://forum.springsource.org/showthread.php?58337-No-transaction-in-transactional-service-called-from-PostConstruct ):
Dans @PostConstruct (comme avec afterPropertiesSet à partir de l'interface InitializingBean), il n'y a aucun moyen de s'assurer que tout le post-traitement est déjà fait, donc (en effet) il ne peut y avoir aucune transaction. Le seul moyen de s’assurer que cela fonctionne consiste à utiliser un TransactionTemplate.
Donc, si vous voulez que quelque chose dans votre @PostConstruct
soit exécuté dans une transaction, vous devez faire quelque chose comme ceci:
@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
}
});
}
}
Je pense que @PostConstruct
garantit uniquement que le prétraitement/injection de votre classe actuelle est terminé. Cela ne signifie pas que l'initialisation de l'ensemble du contexte de l'application est terminée.
Cependant, vous pouvez utiliser le système d'événements spring pour recevoir un événement lorsque l'initialisation du contexte de l'application est terminée:
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
public void onApplicationEvent(ContextRefreshedEvent event) {
// do startup code ..
}
}
Voir la section documentation Evénements standard et personnalisés pour plus de détails.
La réponse de @Platon Serbin n'a pas fonctionné pour moi. Alors j'ai continué à chercher et j'ai trouvé la réponse suivante qui m'a sauvé la vie. :RÉ
La réponse est ici Aucune session Hibernate dans @PostConstruct , que je me suis permis de transcrire:
@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;
}
});
}
En guise de mise à jour, à partir de Spring 4.2, l'annotation @EventListener
permet une implémentation plus propre:
@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
}
}
S'injecter soi-même et appeler par la méthode @Transactional
public class AccountService {
@Autowired
private AccountService self;
@Transactional
public void resetAllAccounts(){
//...
}
@PostConstruct
private void init(){
self.resetAllAccounts();
}
}
Pour les versions plus anciennes de Spring qui ne prennent pas en charge l'auto-injection, injectez BeanFactory
et obtenez self
comme beanFactory.getBean(AccountService.class)
Utiliser transactionOperations.execute()
dans la méthode @PostConstruct
ou dans la méthode @NoTransaction
fonctionne dans les deux cas
@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 {
}
}