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}