001package org.avaje.dbmigration; 002 003import org.avaje.dbmigration.runner.LocalMigrationResource; 004import org.avaje.dbmigration.runner.LocalMigrationResources; 005import org.avaje.dbmigration.runner.MigrationTable; 006import org.avaje.dbmigration.util.JdbcClose; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010import javax.sql.DataSource; 011import java.io.IOException; 012import java.sql.Connection; 013import java.sql.SQLException; 014import java.util.List; 015 016/** 017 * Runs the DB migration typically on application start. 018 */ 019public class MigrationRunner { 020 021 private static final Logger logger = LoggerFactory.getLogger("org.avaje.dbmigration.MigrationRunner"); 022 023 private final MigrationConfig migrationConfig; 024 025 public MigrationRunner(MigrationConfig migrationConfig) { 026 this.migrationConfig = migrationConfig; 027 } 028 029 /** 030 * Run by creating a DB connection from driver, url, username defined in MigrationConfig. 031 */ 032 public void run() { 033 034 Connection connection = migrationConfig.createConnection(); 035 run(connection); 036 } 037 038 public void run(DataSource dataSource) { 039 040 String username = migrationConfig.getDbUsername(); 041 String password = migrationConfig.getDbPassword(); 042 if (username == null) { 043 throw new IllegalStateException("No DB migration user specified (to run the db migration) ?"); 044 } 045 046 try { 047 Connection connection = dataSource.getConnection(username, password); 048 logger.debug("using db user [{}] to run migrations ...", username); 049 run(connection); 050 051 } catch (SQLException e) { 052 throw new IllegalArgumentException("Error trying to connect to database using DB Migration user [" + username + "]", e); 053 } 054 } 055 056 /** 057 * Run the migrations if there are any that need running. 058 */ 059 public void run(Connection connection) { 060 061 LocalMigrationResources resources = new LocalMigrationResources(migrationConfig); 062 if (!resources.readResources()) { 063 logger.debug("no migrations to check"); 064 return; 065 } 066 067 try { 068 connection.setAutoCommit(false); 069 runMigrations(resources, connection); 070 071 connection.commit(); 072 073 } catch (Exception e) { 074 JdbcClose.rollback(connection); 075 throw new RuntimeException(e); 076 077 } finally { 078 JdbcClose.close(connection); 079 } 080 } 081 082 /** 083 * Run all the migrations as needed. 084 */ 085 private void runMigrations(LocalMigrationResources resources, Connection connection) throws SQLException, IOException { 086 087 MigrationTable table = new MigrationTable(migrationConfig, connection); 088 table.createIfNeeded(); 089 090 // get the migrations in version order 091 List<LocalMigrationResource> localVersions = resources.getVersions(); 092 093 logger.info("local migrations:{} existing migrations:{}", localVersions.size(), table.size()); 094 095 LocalMigrationResource priorVersion = null; 096 097 // run migrations in order 098 for (int i = 0; i < localVersions.size(); i++) { 099 LocalMigrationResource localVersion = localVersions.get(i); 100 if (!table.shouldRun(localVersion, priorVersion)) { 101 break; 102 } 103 priorVersion = localVersion; 104 connection.commit(); 105 } 106 } 107 108}