package marubinotto.piggydb.impl.db; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import marubinotto.h2.fulltext.FullTextSearch; import marubinotto.util.Assert; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.BadSqlGrammarException; import org.springframework.jdbc.core.JdbcTemplate; public class DatabaseSchema { private static Log logger = LogFactory.getLog(DatabaseSchema.class); private static Map<String, String> sqlCache = new HashMap<String, String>(); private static String getSql(String path) throws IOException { String sql = sqlCache.get(path); if (sql != null) { return sql; } logger.info("Loading :" + path); InputStream input = DatabaseSchema.class.getClassLoader().getResourceAsStream(path); if (input == null) { logger.info("Not found :" + path); return null; } try { sql = IOUtils.toString(input); } finally { IOUtils.closeQuietly(input); } sqlCache.put(path, sql); return sql; } private JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } private static final String INITIAL_DATABASE = "sql/initial-database.sql"; private static final String DELTA_DIR = "sql/database-delta/"; public static String getDeltaSql(int version) throws IOException { return getSql(DELTA_DIR + version + ".sql"); } private void initFullText() throws SQLException { logger.info("Initializing full text search ..."); Connection connection = this.jdbcTemplate.getDataSource().getConnection(); FullTextSearch.init(connection); } public void update() throws DataAccessException, IOException, SQLException { logger.info("Updating schema ..."); initFullText(); int version = getVersion(); logger.info("Version: " + version); // Initial schema if (version == 0) { String sql = getSql(INITIAL_DATABASE); logger.info("Creating initial database ..."); this.jdbcTemplate.update(sql); version = getVersion(); Assert.assertTrue(version == 1, "version should be 1 after init"); } // Apply delta while (true) { version++; String deltaSql = getDeltaSql(version); if (deltaSql == null) { break; } logger.info("Applying delta: " + version); this.jdbcTemplate.update(deltaSql); setVersion(version); } version = getVersion(); logger.info("Database has been updated to: " + version); } private static final String KEY_VERSION = "database.version"; public int getVersion() { try { return this.jdbcTemplate.queryForInt( "select setting_value from global_setting where setting_name = ?", new Object[]{KEY_VERSION}); } catch (BadSqlGrammarException e) { logger.info("Couldn't get a version: " + e.getSQLException().getMessage()); return 0; } } public void setVersion(int version) { int affected = this.jdbcTemplate.update( "update global_setting set setting_value = ? where setting_name = ?", new Object[]{String.valueOf(version), KEY_VERSION}); if (affected == 0) { this.jdbcTemplate.update( "insert into global_setting (setting_name, setting_value) values(?, ?)", new Object[]{KEY_VERSION, String.valueOf(version)}); } } public static interface MetaDataHandler { public void handle(DatabaseMetaData metaData) throws SQLException; } public void analyzeMetadata(MetaDataHandler handler) throws SQLException { Assert.Arg.notNull(handler, "handler"); Assert.Property.requireNotNull(jdbcTemplate, "jdbcTemplate"); Connection connection = this.jdbcTemplate.getDataSource().getConnection(); try { handler.handle(connection.getMetaData()); } finally { connection.close(); } } }