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}