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