/* * Copyright (C) 2009-2010 Open Wide * Contact: contact@openwide.fr * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package fr.openwide.core.jpa.util; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import javax.annotation.PostConstruct; import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DelegatingDataSource; import org.springframework.transaction.support.TransactionSynchronizationManager; import fr.openwide.core.spring.property.service.IPropertyService; /** * <p>Initialise le schéma de la base.</p> * * @author Open Wide */ public class DatabaseInitializationService { private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseInitializationService.class); @Autowired private IPropertyService propertyService; private JdbcTemplate jdbcTemplate; /** * Méthode utilisée par l'initialisation spring pour la mise en place * du {@link DataSource} */ public void setDataSource(DataSource dataSource) { /* * Il faut utiliser une DelegatingDataSource car le transaction manager d'hibernate * n'accepte pas de partager la gestion d'un data-source avec un autre transaction * manager. * On wrappe donc la data-source pour qu'hibernate accepte de déléguer la gestion. */ DelegatingDataSource myDataSource = new DelegatingDataSource(dataSource); jdbcTemplate = new JdbcTemplate(myDataSource); } /** * Méthode permettant de mettre en place le schéma de la base. */ @PostConstruct protected void initDatabaseSchema() throws IOException { if (propertyService.isConfigurationTypeDevelopment()) { LOGGER.warn("Initialisation de la base de données"); InputStream sqlDropFile = new ClassPathResource("sql/drop_tables.sql").getInputStream(); InputStream sqlInitFile = new ClassPathResource("sql/create_tables.sql").getInputStream(); /* * Si on ne gère pas la connexion à la main en utilisant les méthodes * TransactionSynchronizationManager.initSychronization/clear, alors * les appels consécutifs de jdbcTemplate.execute : * - ne se feront pas sur la même connexion du pool * - de ce fait, il sera impossible d'avoir une quelconque influence sur les transactions */ TransactionSynchronizationManager.initSynchronization(); try { executeQueriesFromFile(sqlDropFile, true); jdbcTemplate.execute("BEGIN"); executeQueriesFromFile(sqlInitFile, false); jdbcTemplate.execute("COMMIT"); } catch (RuntimeException e) { LOGGER.error("Initialization problem", e); jdbcTemplate.execute("ROLLBACK"); } finally { TransactionSynchronizationManager.clear(); } LOGGER.warn("Initialisation terminée"); } } /** * Exécute les requêtes SQL d'un fichier. Une erreur sur une requête n'interrompt pas le processing mais * affiche un message d'erreur. * * @param sqlFile le fichier sql * @param doCommit indique si chaque opération du fichier doit être encadrée d'un BEGIN/COMMIT */ private void executeQueriesFromFile(InputStream sqlFile, boolean doCommit) { StringBuilder sql = new StringBuilder(); InputStreamReader isr = null; BufferedReader in = null; try { isr = new InputStreamReader(sqlFile); in = new BufferedReader(isr); boolean inFunction = false; String line; while ((line = in.readLine()) != null) { String lineTrimed = line.trim(); if (lineTrimed.startsWith("--") || lineTrimed.length() == 0) { continue; } sql.append(line); sql.append('\n'); if (lineTrimed.contains("$func$")) { inFunction = !inFunction; } if (!inFunction && lineTrimed.endsWith(";")) { String sqlQuery = sql.toString(); // exécution après un point-virgule ou à la fin d'une // fonction try { if (doCommit) { jdbcTemplate.execute("BEGIN"); } jdbcTemplate.execute(sqlQuery); } catch (RuntimeException err) { if (doCommit) { jdbcTemplate.execute("ROLLBACK"); } LOGGER.error(err.getMessage(), err); } finally { if (doCommit) { jdbcTemplate.execute("COMMIT"); } } sql.delete(0, sql.length()); } } } catch (RuntimeException | IOException e) { LOGGER.error(e.getMessage(), e); } finally { try { if (sqlFile != null) { sqlFile.close(); } if (isr != null) { isr.close(); } if (in != null) { in.close(); } } catch (IOException e) { LOGGER.error(e.getMessage(), e); } } } }