001package io.ebean.dbmigration; 002 003import io.ebean.Database; 004import io.ebean.annotation.Platform; 005import io.ebean.config.DatabaseConfig; 006import io.ebean.config.dbplatform.DatabasePlatform; 007 008import java.io.IOException; 009import java.util.Iterator; 010import java.util.List; 011import java.util.ServiceLoader; 012 013/** 014 * Generates DDL migration scripts based on changes to the current model. 015 * 016 * <p> 017 * Typically this is run as a main method in src/test once a developer is happy 018 * with the next set of changes to the model. 019 * 020 * <h3>Example: Run for a single specific platform</h3> 021 * 022 * <pre>{@code 023 * 024 * DbMigration migration = DbMigration.create(); 025 * 026 * migration.setPlatform(Platform.POSTGRES); 027 * 028 * // optionally specify the version and name 029 * migration.setName("add indexes to customer"); 030 * 031 * migration.generateMigration(); 032 * 033 * }</pre> 034 * 035 * <p> 036 * Drop column migrations are effectively breaking changes and should 037 * be held back and run in a later migration after the columns deleted 038 * are no longer being used by the application. These changes are called 039 * "pending drops" and we must explicitly specify to include these in 040 * a generated migration. 041 * <p> 042 * Use <code>setGeneratePendingDrop()</code> to specify a prior migration 043 * that has drop column changes that we want to generate a migration for. 044 * 045 * <h3>Example: Generate for pending drops</h3> 046 * 047 * <pre>{@code 048 * 049 * DbMigration migration = DbMigration.create(); 050 * 051 * migration.setPlatform(Platform.POSTGRES); 052 * 053 * // set the migration version that has pending drops 054 * migration.setGeneratePendingDrop("1.3"); 055 * 056 * // generates the migration with drop column changes 057 * migration.generateMigration(); 058 * 059 * }</pre> 060 */ 061public interface DbMigration { 062 063 /** 064 * Create a DbMigration implementation to use. 065 */ 066 static DbMigration create() { 067 Iterator<DbMigration> loader = ServiceLoader.load(DbMigration.class).iterator(); 068 if (loader.hasNext()) { 069 return loader.next(); 070 } 071 throw new IllegalStateException("No service implementation found for DbMigration?"); 072 } 073 074 /** 075 * Set logging to System out (defaults to true). 076 */ 077 void setLogToSystemOut(boolean logToSystemOut); 078 079 /** 080 * Set the path from the current working directory to the application resources. 081 * <p> 082 * This defaults to maven style 'src/main/resources'. 083 */ 084 void setPathToResources(String pathToResources); 085 086 /** 087 * Set the path where migrations are generated to (which defaults to "dbmigration"). 088 * <p> 089 * Normally we only use this when we use Ebean to generate the database migrations 090 * and then use some other tool like FlywayDB to run the migrations. 091 * <p> 092 * Example: with <code>setMigrationPath("db/migration")</code> ... the migrations 093 * are generated into <code>src/resources/db/migration</code>. 094 * <p> 095 * Note that if Ebean migration runner is used we should not use this method but 096 * instead set the migrationPath via a property such that both the migration generator 097 * and migration runner both use the same path. 098 * 099 * @param migrationPath The path that migrations are generated into. 100 */ 101 void setMigrationPath(String migrationPath); 102 103 /** 104 * Set the server to use to determine the current model. Usually this is not called explicitly. 105 */ 106 void setServer(Database database); 107 108 /** 109 * Set the DatabaseConfig to use. Usually this is not called explicitly. 110 */ 111 void setServerConfig(DatabaseConfig config); 112 113 /** 114 * Set the specific platform to generate DDL for. 115 * <p> 116 * If not set this defaults to the platform of the default database. 117 */ 118 void setPlatform(Platform platform); 119 120 /** 121 * Set the specific platform to generate DDL for. 122 * <p> 123 * If not set this defaults to the platform of the default database. 124 */ 125 void setPlatform(DatabasePlatform databasePlatform); 126 127 /** 128 * Set to false in order to turn off strict mode. 129 * <p> 130 * Strict mode checks that a column changed to non-null on an existing table via DB migration has a default 131 * value specified. Set this to false if that isn't the case but it is known that all the existing rows have 132 * a value specified (there are no existing null values for the column). 133 */ 134 void setStrictMode(boolean strictMode); 135 136 /** 137 * Set to include generation of the index migration file. 138 * <p> 139 * When true this generates a {@code idx_<platform>.migrations} file. This can be used by the migration 140 * runner to improve performance of running migrations, especially when no migration changes have occurred. 141 */ 142 void setIncludeIndex(boolean generateIndexFile); 143 144 /** 145 * Set to true to include a generated header comment in the DDL script. 146 */ 147 void setIncludeGeneratedFileComment(boolean includeGeneratedFileComment); 148 149 /** 150 * Set this to false to exclude the builtin support for table partitioning (with @DbPartition). 151 */ 152 void setIncludeBuiltInPartitioning(boolean includeBuiltInPartitioning); 153 154 /** 155 * Set the header that is included in the generated DDL script. 156 */ 157 void setHeader(String header); 158 159 /** 160 * Set the prefix for the version. Set this to "V" for use with Flyway. 161 */ 162 void setApplyPrefix(String applyPrefix); 163 164 /** 165 * Set the version of the migration to be generated. 166 */ 167 void setVersion(String version); 168 169 /** 170 * Set the name of the migration to be generated. 171 */ 172 void setName(String name); 173 174 /** 175 * Generate a migration for the version specified that contains pending drops. 176 * 177 * @param generatePendingDrop The version of a prior migration that holds pending drops. 178 */ 179 void setGeneratePendingDrop(String generatePendingDrop); 180 181 /** 182 * Set to true if ALTER TABLE ADD FOREIGN KEY should be generated with an option to skip validation. 183 * <p> 184 * Currently this is only useful for Postgres DDL adding the <code>NOT VALID</code> option. 185 */ 186 void setAddForeignKeySkipCheck(boolean addForeignKeySkipCheck); 187 188 /** 189 * Set the lock timeout to be included with the DDL generation. 190 * <p> 191 * Currently this is only useful for Postgres migrations adding a <code>set lock_timeout</code> 192 * statement to the generated database migration. 193 */ 194 void setLockTimeout(int seconds); 195 196 /** 197 * Add a platform to write the migration DDL. 198 * <p> 199 * Use this when you want to generate sql scripts for multiple database platforms 200 * from the migration (e.g. generate migration sql for MySql, Postgres and Oracle). 201 */ 202 void addPlatform(Platform platform); 203 204 /** 205 * Add a platform to write with a given prefix. 206 */ 207 void addPlatform(Platform platform, String prefix); 208 209 /** 210 * Add a databasePlatform to write the migration DDL. 211 * <p> 212 * Use this when you want to add preconfigured database platforms. 213 */ 214 void addDatabasePlatform(DatabasePlatform databasePlatform, String prefix); 215 216 /** 217 * Return the list of versions that contain pending drops. 218 */ 219 List<String> getPendingDrops(); 220 221 /** 222 * Generate the next migration sql script and associated model xml. 223 * <p> 224 * This does not run the migration or ddl scripts but just generates them. 225 * </p> 226 * <h3>Example: Run for a single specific platform</h3> 227 * <pre>{@code 228 * 229 * DbMigration migration = DbMigration.create(); 230 * migration.setPlatform(Platform.POSTGRES); 231 * 232 * migration.generateMigration(); 233 * 234 * }</pre> 235 * <p> 236 * 237 * <h3>Example: Generate for "pending drops" (drop column changes)</h3> 238 * 239 * <pre>{@code 240 * 241 * DbMigration migration = DbMigration.create(); 242 * 243 * migration.setPlatform(Platform.POSTGRES); 244 * 245 * // set the migration version that has pending drops 246 * migration.setGeneratePendingDrop("1.3"); 247 * 248 * // generates the migration with drop column changes 249 * migration.generateMigration(); 250 * 251 * }</pre> 252 * 253 * <h3>Example: Run migration generating DDL for multiple platforms</h3> 254 * <pre>{@code 255 * 256 * DbMigration migration = DbMigration.create(); 257 * 258 * migration.setPathToResources("src/main/resources"); 259 * 260 * migration.addPlatform(Platform.POSTGRES); 261 * migration.addPlatform(Platform.MYSQL); 262 * migration.addPlatform(Platform.ORACLE); 263 * 264 * migration.generateMigration(); 265 * 266 * }</pre> 267 * 268 * @return the version of the generated migration or null 269 */ 270 String generateMigration() throws IOException; 271 272 /** 273 * Generate an "init" migration which has all changes. 274 * <p> 275 * An "init" migration can only be executed and used on a database that has had no 276 * prior migrations run on it. 277 * </p> 278 * 279 * @return the version of the generated migration 280 */ 281 String generateInitMigration() throws IOException; 282 283}