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}