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 * </p> 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 * <p> 044 * Use <code>setGeneratePendingDrop()</code> to specify a prior migration 045 * that has drop column changes that we want to generate a migration for. 046 * </p> 047 * 048 * <h3>Example: Generate for pending drops</h3> 049 * 050 * <pre>{@code 051 * 052 * DbMigration migration = DbMigration.create(); 053 * 054 * migration.setPlatform(Platform.POSTGRES); 055 * 056 * // set the migration version that has pending drops 057 * migration.setGeneratePendingDrop("1.3"); 058 * 059 * // generates the migration with drop column changes 060 * migration.generateMigration(); 061 * 062 * }</pre> 063 */ 064public interface DbMigration { 065 066 /** 067 * Create a DbMigration implementation to use. 068 */ 069 static DbMigration create() { 070 071 Iterator<DbMigration> loader = ServiceLoader.load(DbMigration.class).iterator(); 072 if (loader.hasNext()) { 073 return loader.next(); 074 } 075 throw new IllegalStateException("No service implementation found for DbMigration?"); 076 } 077 078 /** 079 * Set to false to suppress logging to System out. 080 */ 081 void setLogToSystemOut(boolean logToSystemOut); 082 083 /** 084 * Set the path from the current working directory to the application resources. 085 * <p> 086 * This defaults to maven style 'src/main/resources'. 087 */ 088 void setPathToResources(String pathToResources); 089 090 /** 091 * Set the path where migrations are generated to (which defaults to "dbmigration"). 092 * <p> 093 * Normally we only use this when we use Ebean to generate the database migrations 094 * and then use some other tool like FlywayDB to run the migrations. 095 * <p> 096 * Example: with <code>setMigrationPath("db/migration")</code> ... the migrations 097 * are generated into <code>src/resources/db/migration</code>. 098 * <p> 099 * Note that if Ebean migration runner is used we should not use this method but 100 * instead set the migrationPath via a property such that both the migration generator 101 * and migration runner both use the same path. 102 * 103 * @param migrationPath The path that migrations are generated into. 104 */ 105 void setMigrationPath(String migrationPath); 106 107 /** 108 * Set the server to use to determine the current model. 109 * Typically this is not called explicitly. 110 */ 111 void setServer(Database database); 112 113 /** 114 * Set the DatabaseConfig to use. Typically this is not called explicitly. 115 */ 116 void setServerConfig(DatabaseConfig config); 117 118 /** 119 * Set the specific platform to generate DDL for. 120 * <p> 121 * If not set this defaults to the platform of the default database. 122 * </p> 123 */ 124 void setPlatform(Platform platform); 125 126 /** 127 * Set the specific platform to generate DDL for. 128 * <p> 129 * If not set this defaults to the platform of the default database. 130 * </p> 131 */ 132 void setPlatform(DatabasePlatform databasePlatform); 133 134 /** 135 * Set to false to turn off strict mode. 136 * <p> 137 * Strict mode checks that a column changed to non-null on an existing table via DB migration has a default 138 * value specified. Set this to false if that isn't the case but it is known that all the existing rows have 139 * a value specified (there are no existing null values for the column). 140 * </p> 141 */ 142 void setStrictMode(boolean strictMode); 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 * </p> 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 * </p> 195 */ 196 void setLockTimeout(int seconds); 197 198 /** 199 * Add an additional platform to write the migration DDL. 200 * <p> 201 * Use this when you want to generate sql scripts for multiple database platforms 202 * from the migration (e.g. generate migration sql for MySql, Postgres and Oracle). 203 * </p> 204 */ 205 void addPlatform(Platform platform, String prefix); 206 207 /** 208 * Add an additional databasePlatform to write the migration DDL. 209 * <p> 210 * Use this when you want to add preconfigured database platforms. 211 * </p> 212 */ 213 void addDatabasePlatform(DatabasePlatform databasePlatform, String prefix); 214 215 /** 216 * Return the list of versions that contain pending drops. 217 */ 218 List<String> getPendingDrops(); 219 220 /** 221 * Generate the next migration sql script and associated model xml. 222 * <p> 223 * This does not run the migration or ddl scripts but just generates them. 224 * </p> 225 * <h3>Example: Run for a single specific platform</h3> 226 * <pre>{@code 227 * 228 * DbMigration migration = DbMigration.create(); 229 * migration.setPlatform(Platform.POSTGRES); 230 * 231 * migration.generateMigration(); 232 * 233 * }</pre> 234 * <p> 235 * 236 * <h3>Example: Generate for "pending drops" (drop column changes)</h3> 237 * 238 * <pre>{@code 239 * 240 * DbMigration migration = DbMigration.create(); 241 * 242 * migration.setPlatform(Platform.POSTGRES); 243 * 244 * // set the migration version that has pending drops 245 * migration.setGeneratePendingDrop("1.3"); 246 * 247 * // generates the migration with drop column changes 248 * migration.generateMigration(); 249 * 250 * }</pre> 251 * 252 * <h3>Example: Run migration generating DDL for multiple platforms</h3> 253 * <pre>{@code 254 * 255 * DbMigration migration = DbMigration.create(); 256 * 257 * migration.setPathToResources("src/main/resources"); 258 * 259 * migration.addPlatform(Platform.POSTGRES, "pg"); 260 * migration.addPlatform(Platform.MYSQL, "mysql"); 261 * migration.addPlatform(Platform.ORACLE, "oracle"); 262 * 263 * migration.generateMigration(); 264 * 265 * }</pre> 266 * 267 * @return the version of the generated migration or null 268 */ 269 String generateMigration() throws IOException; 270 271 /** 272 * Generate an "init" migration which has all changes. 273 * <p> 274 * An "init" migration can only be executed and used on a database that has had no 275 * prior migrations run on it. 276 * </p> 277 * 278 * @return the version of the generated migration 279 */ 280 String generateInitMigration() throws IOException; 281 282}